JavaBeans的屬性
JavaBeans的屬性與一般Java程序中所指的屬性,或者說與所有面向對象的程序設計語言中對象的屬性是一個概念,在程序中的具體體現就是類中的變量。在JavaBeans設計中,按照屬性的不同作用又細分為四類:Simple, Index, Bound與Constrained屬性。
1. Simple屬性
一個簡單屬性表示一個伴隨有一對get/set方法(C語言的過程或函數在Java程序中稱為"方法")的變量。屬性名與和該屬性相關的get/set方法名對應。例如:如果有setX和getX方法,則暗指有一個名為"X"的屬性。如果有一個方法名為isX,則通常暗指"X"是一個布爾屬性(即X的值為true或false)。例如在下面這個程序中:

public?class?alden1?extends?Canvas?
{?
string?ourString=?"Hello";?//屬性名為ourString,類型為字符串?

public?alden1()
{? //alden1()是alden1的構造函數,?
與C++中構造函數的意義相同?
setBackground(Color.red);?
setForeground(Color.blue);?
}?

/**//*?"set"屬性*/?

public?void?setString(String?newString)?
{?
ourString=newString;?
}?

/**//*?"get"屬性?*/?

public?String?getString()?
{?
return?ourString;?
}?
}?2. Indexed屬性
一個Indexed屬性表示一個數組值。使用與該屬性對應的set/get方法可取得數組中的數值。該屬性也可一次設置或取得整個數組的值。例:

public?class?alden2?extends?Canvas?
{?

int[]?dataSet=
{1,2,3,4,5,6};?//?dataSet是一個indexed屬性?

public?alden2()?
{?
setBackground(Color.red);?
setForeground(Color.blue);?
}?

/**//*?設置整個數組 */?

public?void?setDataSet(int[]?x)
{?
dataSet=x;?
}?

/**//*?設置數組中的單個元素值?*/?

public?void?setDataSet(int?index,?int?x)
{?
dataSet[index]=x;?
}?

/**//*?取得整個數組值?*/?

public?int[]?getDataSet()
{?
return?dataSet;?
}?

/**//*?取得數組中的指定元素值?*/?

public?int?getDataSet(int?x)
{?
return?dataSet[x];?
}?
}? 3. Bound屬性
一個Bound屬性是指當該種屬性的值發生變化時,要通知其它的對象。每次屬性值改變時,這種屬性就點火一個PropertyChange事件(在
Java程序中,事件也是一個對象)。事件中封裝了屬性名、屬性的原值、屬性變化后的新值。這種事件是傳遞到其它的Beans,至于接收事件的Beans應做什么動作由其自己定義。當PushButton的background屬性與Dialog的background屬性bind時,若PushButton的background屬性發生變化時,Dialog的background屬性也發生同樣的變化。 例:

public?class?alden3?extends?Canvas
{?
String?ourString=?"Hello";?
//ourString是一個bound屬性?
private?PropertyChangeSupport?changes?=?new?PropertyChangeSupport(this);?

/**?*//**?注:Java是純面向對象的語言,?
如果要使用某種方法則必須指明是要使用哪個對象的方法,?
在下面的程序中要進行點火事件的操作,?
這種操作所使用的方法是在PropertyChangeSupport類中的。?
所以上面聲明并實例化了一個changes對象,?
在下面將使用changes的firePropertyChange方法來點火ourString的屬性改變事件。*/?


public?void?setString(string?newString)
{?
String?oldString?=?ourString;?
ourString?=?newString;?

/**//*?ourString的屬性值已發生變化,于是接著點火屬性改變事件?*/?
changes.firePropertyChange("ourString",oldString,newString);?
}?

public?String?getString()
{?
return?ourString;?
}?

/**?*//**?以下代碼是為開發工具所使用的。?
我們不能預知alden3將與哪些其它的Beans組合成為一個應用,?
無法預知若alden3的ourString屬性發生變化時有哪些其它的組件與此變化有關,?
因而alden3這個Beans要預留出一些接口給開發工具,?
開發工具使用這些接口,?
把其它的JavaBeans對象與alden3掛接。*/?


public?void?addPropertyChangeListener(PropertyChangeLisener?l)
{?
changes.addPropertyChangeListener(l);?
}?

public?void?removePropertyChangeListener(PropertyChangeListener?l)
{?
changes.removePropertyChangeListener(l);?
}???????? 通過上面的代碼,開發工具調用changes的addPropertyChangeListener方法,把其它
JavaBeans注冊入ourString屬性的監聽者隊列l中,l是一個Vector數組,可存儲任何
Java對象。
開發工具也可使用changes的removePropertyChangeListener方法,從l中注銷指定的對象,使alden3的ourString屬性的改變不再與這個對象有關。
當然,當
程序員手寫代碼編制
程序時,也可直接調用這兩個方法,把其它
Java對象與alden3掛接。
4. Constrained屬性
一個
JavaBeans的constrained屬性,是指當這個屬性的值要發生變化時,與這個屬性已建立了某種連接的其它
Java對象可否決屬性值的改變。constrained屬性的監聽者通過拋出PropertyVetoException來阻止該屬性值的改變。例:下面
程序中的constrained屬性是PriceInCents。

public?class?JellyBeans?extends?Canvas
{?
private?PropertyChangeSupport?changes=new?PropertyChangeSupport(this);?
private?VetoableChangeSupport?Vetos=new?VetoableChangeSupport(this);?

/**//*與前述changes相同,?
可使用VetoableChangeSupport對象的實例Vetos中的方法,?
在特定條件下來阻止PriceInCents值的改變。*/?


?

public?void?setPriceInCents(int?newPriceInCents)?throws?PropertyVetoException?
{?

/**//*方法名中throws?PropertyVetoException的作用是當有?
其它Java對象否決PriceInCents的改變時,?
要拋出例外。*/?

/**//*?先保存原來的屬性值*/?

int?oldPriceInCents=ourPriceInCents;?

/**?*//**點火屬性改變否決事件*/?
vetos.fireVetoableChange("priceInCents",new?Integer(OldPriceInCents),new?Integer(newPriceInCents));?


/**?*//**若有其它對象否決priceInCents的改變,?
則程序拋出例外,不再繼續執行下面的兩條語句,?
方法結束。若無其它對象否決priceInCents的改變,?
則在下面的代碼中把ourPriceIncents賦予新值,?
并點火屬性改變事件*/?

ourPriceInCents=newPriceInCents;?
changes.firePropertyChange("priceInCents",?new?Integer(oldPriceInCents),?new?Integer(newPriceInCents));?
}?


/**?*//**與前述changes相同,?
也要為PriceInCents屬性預留接口,?
使其它對象可注冊入PriceInCents否決改變監聽者隊列中,?
或把該對象從中注銷?

public?void?addVetoableChangeListener(VetoableChangeListener?l)?
{
vetos.addVetoableChangeListener(l);?
}?
public?void?removeVetoableChangeListener(VetoableChangeListener?l){?
vetos.removeVetoableChangeListener(l);?
}?

?
}?從上面的例子中可看到,一個constrained屬性有兩種監聽者:屬性變化監聽者和否決屬性改變的監聽者。否決屬性改變的監聽者在自己的對象代碼中有相應的控制語句,在監聽到有constrained屬性要發生變化時,在控制語句中判斷是否應否決這個屬性值的改變。
總之,某個Beans的constrained屬性值可否改變取決于其它的Beans或者是
Java對象是否允許這種改變。允許與否的條件由其它的Beans或
Java對象在自己的類中進行定義。
JavaBeans的事件 事件處理是
JavaBeans體系結構的核心之一。通過事件處理機制,可讓一些組件作為事件源,發出可被描述環境或其它組件接收的事件。這樣,不同的組件就可在構造工具內組合在一起,組件之間通過事件的傳遞進行通信,構成一個應用。從概念上講,事件是一種在"源對象"和"監聽者對象"之間,某種狀態發生變化的傳遞機制。事件有許多不同的用途,例如在Windows
系統中常要處理的鼠標事件、窗口邊界改變事件、鍵盤事件等。在
Java和
JavaBeans中則是定義了一個一般的、可擴充的事件機制,這種機制能夠:
·對事件類型和傳遞的模型的定義和擴充提供一個公共框架,并適合于廣泛的應用。
·與
Java語言和環境有較高的集成度。
·事件能被描述環境捕獲和點火。
·能使其它構造工具采取某種技術在設計時直接控制事件,以及事件源和事件監聽者之間的聯系。
·事件機制本身不依賴于復雜的開發工具。特別地,還應當:
·能夠發現指定的對象類可以生成的事件。
·能夠發現指定的對象類可以觀察(監聽)到的事件。
·提供一個常規的注冊機制,允許動態操縱事件源與事件監聽者之間的關系。
·不需要其它的虛擬機和語言即可實現。
·事件源與監聽者之間可進行高效的事件傳遞。
·能完成
JavaBeans事件模型與相關的其它組件體系結構事件模型的中立映射。
JavaBeans事件模型的主要構成有: 事件從事件源到監聽者的傳遞是通過對目標監聽者對象的
Java方法調用進行的。對每個明確的事件的發生,都相應地定義一個明確的
Java方法。這些方法都集中定義在事件監聽者(EventListener)接口中,這個接口要繼承java.util.EventListener。實現了事件監聽者接口中一些或全部方法的類就是事件監聽者。 伴隨著事件的發生,相應的狀態通常都封裝在事件狀態對象中,該對象必須繼承自java.util.EventObject。事件狀態對象作為單參傳遞給應響應該事件的監聽者方法中。 發出某種特定事件的事件源的標識是:遵從規定的設計格式為事件監聽者定義注冊方法,并接受對指定事件監聽者接口實例的引用。 有時,事件監聽者不能直接實現事件監聽者接口,或者還有其它的額外動作時,就要在一個源與其它一個或多個監聽者之間插入一個事件適配器類的實例,來建立它們之間的聯系。
事件狀態對象(Event State Object)
與事件發生有關的狀態信息一般都封裝在一個事件狀態對象中,這種對象是java.util.EventObject的子類。按設計習慣,這種事件狀態對象類的名應以Event結尾。例如: |
public?class?MouseMovedExampleEvent?extends?java.util.EventObject?


{
protected?int?x,?y;?

/**//* 創建一個鼠標移動事件MouseMovedExampleEvent?*/?

MouseMovedExampleEvent(java.awt.Component?source,?Point?location)?
{?
super(source);?
x?=?location.x;?
y?=?location.y;?
}?

/**//*?獲取鼠標位置*/?

public?Point?getLocation()?
{?
return?new?Point(x,?y);?
}
} ?? 事件監聽者接口(EventListener Interface)與事件監聽者 由于
Java事件模型是基于方法調用,因而需要一個定義并組織事件操縱方法的方式。
JavaBeans中,事件操縱方法都被定義在繼承了java.util.EventListener類的EventListener接口中,按規定,EventListener接口的命名要以Listener結尾。任何一個類如果想操縱在EventListener接口中定義的方法都必須以實現這個接口方式進行。這個類也就是事件監聽者。例如:

/**//*先定義了一個鼠標移動事件對象*/?
public?class?MouseMovedExampleEvent?

extends?java.util.EventObject?
{?
//?在此類中包含了與鼠標移動事件有關的狀態信息?
?
?
}?

/**//*定義了鼠標移動事件的監聽者接口*/?
interface?MouseMovedExampleListener?

extends?java.util.EventListener?
{?

/**//*在這個接口中定義了鼠標移動事件監聽者所應支持的方法*/?
void?mouseMoved(MouseMovedExampleEvent?mme);?
}??

在接口中只定義方法名,方法的參數和返回值類型。 如:上面接口中的mouseMoved方法的具體實現是在下面的ArbitraryObject類中定義的。

class?ArbitraryObject?implements?MouseMovedExampleListener?
{?
public?void?mouseMoved(MouseMovedExampleEvent?mme)?

?
{?
?}?
} ArbitraryObject就是MouseMovedExampleEvent事件的監聽者。
事件監聽者的注冊與注銷 為了各種可能的事件監聽者把自己注冊入合適的事件源中,建立源與事件監聽者間的事件流,事件源必須為事件監聽者提供注冊和注銷的方法。在前面的bound屬性介紹中已看到了這種使用過程,在實際中,事件監聽者的注冊和注銷要使用標準的設計格式:
public?void?add<?ListenerType>(<?ListenerType>?listener);?
public?void?remove<?ListenerType>(<?ListenerType>?listener);??

例如:
首先定義了一個事件監聽者接口:
public?interface?

ModelChangedListener?extends?java.util.EventListener?
{?
void?modelChanged(EventObject?e);?
}??

接著定義事件源類:

public?abstract?class?Model?
{?
private?Vector?listeners?=?new?Vector();?//?定義了一個儲存事件監聽者的數組?


/**//*上面設計格式中的<?ListenerType>在此處即是下面的ModelChangedListener*/?

public?synchronized?void?addModelChangedListener(ModelChangedListener?mcl)?

{?listeners.addElement(mcl);?}//把監聽者注冊入listeners數組中?
public?synchronized?void?removeModelChangedListener(ModelChangedListener?mcl)?

{?listeners.removeElement(mcl);?//把監聽者從listeners中注銷?
?。?

/**//*以上兩個方法的前面均冠以synchronized,?
是因為運行在多線程環境時,?
可能同時有幾個對象同時要進行注冊和注銷操作,?
使用synchronized來確保它們之間的同步。?
開發工具或程序員使用這兩個方法建立源與監聽者之間的事件流*/?


protected?void?notifyModelChanged()?
{?

/**?*//**事件源使用本方法通知監聽者發生了modelChanged事件*/?
?Vector?l;?
EventObject?e?=?new?EventObject(this);?

/**//*?首先要把監聽者拷貝到l數組中,?
凍結EventListeners的狀態以傳遞事件。?
這樣來確保在事件傳遞到所有監聽者之前,?
已接收了事件的目標監聽者的對應方法暫不生效。*/?

synchronized(this)?
{?
l?=?(Vector)listeners.clone();?
}?

for?(int?i?=?0;?i?<?l.size();?i++)?
{?

?/**//*?依次通知注冊在監聽者隊列中的每個監聽者發生了modelChanged事件,?
?并把事件狀態對象e作為參數傳遞給監聽者隊列中的每個監聽者*/?
((ModelChangedListener)l.elementAt(i)).modelChanged(e);?
}?
?}?
}? 在
程序中可見事件源Model類顯式地調用了接口中的modelChanged方法,實際是把事件狀態對象e作為參數,傳遞給了監聽者類中的modelChanged方法。
適配類 適配類是
Java事件模型中極其重要的一部分。在一些應用場合,事件從源到監聽者之間的傳遞要通過適配類來"轉發"。例如:當事件源發出一個事件,而有幾個事件監聽者對象都可接收該事件,但只有指定對象做出反應時,就要在事件源與事件監聽者之間插入一個事件適配器類,由適配器類來指定事件應該是由哪些監聽者來響應。
適配類成為了事件監聽者,事件源實際是把適配類作為監聽者注冊入監聽者隊列中,而真正的事件響應者并未在監聽者隊列中,事件響應者應做的動作由適配類決定。目前絕大多數的開發工具在生成代碼時,事件處理都是通過適配類來進行的。
JavaBeans用戶化 JavaBeans開發者可以給一個Beans添加用戶化器(Customizer)、屬性編輯器(PropertyEditor)和BeansInfo接口來描述一個Beans的內容,Beans的使用者可在構造環境中通過與Beans附帶在一起的這些信息來用戶化Beans的外觀和應做的動作。一個Beans不必都有BeansCustomizer、PrpertyEditor和BeansInfo,根據實際情況,這些是可選的,當有些Beans較復雜時,就要提供這些信息,以Wizard的方式使Beans的使用者能夠用戶化一個Beans。有些簡單的Beans可能這些信息都沒有,則構造工具可使用自帶的透視裝置,透視出Beans的內容,并把信息顯示到標準的屬性表或事件表中供使用者用戶化Beans,前幾節提到的Beans的屬性、方法和事件名要以一定的格式命名,主要的作用就是供開發工具對Beans進行透視。當然也是給
程序員在手寫
程序中使用Beans提供方便,使他能觀其名、知其意。
用戶化器接口(Customizer Interface) 當一個Beans有了自己的用戶化器時,在構造工具內就可展現出自己的屬性表。在定義用戶化器時必須要實現java.Beanss.Customizer接口。例如,下面是一個"按鈕"Beans的用戶化一器:
public?class?OurButtonCustomizer?

extends?Panel?implements?Customizer?
{?
?
?

/**//*當實現象OurButtonCustomizer這樣的常規屬性表時,?
一定要在其中實現addProperChangeListener?
和removePropertyChangeListener,這樣,?
構造工具可用這些功能代碼為屬性事件添加監聽者。*/?
?
?
private?PropertyChangeSupport?changes=new?PropertyChangeSupport(this);?

public?void?addPropertyChangeListener(PropertyChangeListener?l)?
{?
changes.addPropertyChangeListener(l);?

public?void?removePropertyChangeListener(PropertyChangeListener?l)?
{?
changes.removePropertyChangeListener(l);?
}?

?
? ????? 屬性編輯器接口(PropertyEditor Interface) 一個
JavaBeans可提供PropertyEditor類,為指定的屬性創建一個編輯器。這個類必須繼承自java.Beanss.PropertyEditorSupport類。構造工具與手寫代碼的
程序員不直接使用這個類,而是在下一小節的BeansInfo中實例化并調用這個類。例:

public?class?MoleculeNameEditor?extends?java.Beanss.PropertyEditorSupport?
{?

public?String[]?getTags()?
{?

String?resule[]=
{?
"HyaluronicAcid","Benzene","buckmisterfullerine",?"cyclohexane","ethane","water"};?
return?resule;}?
}? 上例中是為Tags屬性創建了屬性編輯器,在構造工具內,可從下拉表格中選擇MoleculeName的屬性應是"HyaluronicAid"或是"water"。
BeansInfo接口 每個Beans類也可能有與之相關的BeansInfo類,在其中描述了這個Beans在構造工具內出現時的外觀。BeansInfo中可定義屬性、方法、事件,顯示它們的名稱,提供簡單的幫助說明。 例如:

public?class?MoleculeBeansInfo?extends?SimpleBeansInfo?
{?

public?PropertyDescriptor[]?getPropertyDescriptors()?
{?

try?
{?
PropertyDescriptor?pd=new?PropertyDescriptor("moleculeName",Molecule.class);?

/**//*通過pd引用了上一節的MoleculeNameEditor類,取得并返回moleculeName屬性*/?
pd.setPropertyEditorClass(MoleculeNameEditor.class);?

PropertyDescriptor?result[]=
{pd};?
return?result;?

}?catch(Exception?ex)?
{?
System.err.println("MoleculeBeansInfo:?unexpected?exeption:?"+ex);?
return?null;?
}?
}?
}?
JavaBeans持久化 當一個
JavaBeans在構造工具內被用戶化,并與其它Beans建立連接之后,它的所有狀態都應當可被保存,下一次被load進構造工具內或在運行時,就應當是上一次修改完的信息。為了能做到這一點,要把Beans的某些字段的信息保存下來,在定義Beans時要使它實現java.io.Serializable接口。例如:
public?class?Button?

implements?java.io.Serializable?
{}? ?????? 實現了序列化接口的Beans中字段的信息將被自動保存。若不想保存某些字段的信息則可在這些字段前冠以transient或static關鍵字,transient和static變量的信息是不可被保存的。通常,一個Beans所有公開出來的屬性都應當是被保存的,也可有選擇地保存內部狀態。 Beans開發者在修改軟件時,可以添加字段,移走對其它類的引用,改變一個字段的private/protected/public狀態,這些都不影響類的存儲結構關系。然而,當從類中刪除一個字段,改變一個變量在類體系中的位置,把某個字段改成transient/static,或原來是transient/static,現改為別的特性時,都將引起存儲關系的變化。
JavaBeans的存儲格式 JavaBeans組件被設計出來后,一般是以擴展名為jar的Zip格式文件存儲,在jar中包含與JavaBeans有關的信息,并以MANIFEST文件指定其中的哪些類是JavaBeans。以jar文件存儲的JavaBeans在網絡中傳送時極大地減少了數據的傳輸數量,并把JavaBeans運行時所需要的一些資源捆綁在一起,本章主要論述了JavaBeans的一些內部特性及其常規設計方法,參考的是JavaBeans規范1.0A版本。隨著世界各大ISV對JavaBeans越來越多的支持,規范在一些細節上還在不斷演化,但基本框架不會再有大的變動。
地震讓大伙知道:居安思危,才是生存之道。
posted on 2007-03-25 23:04
小尋 閱讀(311)
評論(0) 編輯 收藏 所屬分類:
j2se/j2ee/j2me