JavaBeans措施开拓从入门到能干
副标题#e#
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];
}
}
#p#副标题#e#
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属性
#p#分页标题#e#
一个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)
#p#分页标题#e#
与事件产生有关的状态信息一般都封装在一个事件状态工具中,这种工具是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要领。
适配类
#p#分页标题#e#
适配类是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耐久化
#p#分页标题#e#
当一个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越来越多的支持,类型在一些细节上还在不绝演化,但根基框架不会再有大的变换。