友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!
Java编程思想第4版[中文版](PDF格式)-第80部分
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部! 如果本书没有阅读完,想下次继续接着阅读,可使用上方 "收藏到我的浏览器" 功能 和 "加入书签" 功能!
到如今,我们可能习惯于使用对象的多个特性,这也是一个动作集合。在设计时,可视化组件的动作可由事
件部分地代表,意味着“任何事件都可以发生在组件上”。通常,由我们决定想发生的事件,当一个事件发
456
…………………………………………………………Page 458……………………………………………………………
生时,对所发生的事件连接代码。
这是关键性的部分:应用程序构建工具可以动态地询问组件(利用映象)以发现组件支持的事件和属件。一
旦它知道它们的状态,应用程序构建工具就可以显示组件的属性并允许我们修改它们的属性(当我们构建程
序时,保存它们的状态),并且也显示这些事件。一般而言,我们做一些事件像双击一个事件以及应用程序
构建工具创建一个代码并连接到事件上。当事件发生时,我们不得不编写执行代码。应用程序构建工具累计
为我们做了大量的工作。结果我们可以注意到程序看起来像它所假定的那样运行,并且依赖应用程序构建工
具去为我们管理连接的详细资料。可视化的编程工具如此成功的原因是它们明显加快构建的应用程序的处理
过程——当然,用户接口作为应用程序的一部分同样的好。
13。18。1 什么是 Bean
在经细节处理后,一个组件在类中被独特的具体化,真正地成为一块代码。关键的争议在于应用程序构建工
具发现组件的属性和事件能力。为了创建一个 VB 组件,程序开发者不得不编写正确的同时也是复杂烦琐的代
码片,接下来由某些协议去展现它们的事件和属性。Delphi 是第二代的可视化编程工具并且这种开发语言主
动地围绕可视化编程来设计因此它更容易去创建一个可视化组件。但是,Java 带来了可视化的创作组件做为
Java Beans 最高级的“装备”,因为一个 Bean 就是一个类。我们不必再为制造任何的Bean 而编写一些特殊
的代码或者使用特殊的编程语言。事实上,我们唯一需要做的是略微地修改我们对我们方法命名的办法。方
法名通知应用程序构建工具是否是一个属性,一个事件或是一个普通的方法。
在Java 的文件中,命名规则被错误地曲解为“设计范式”。这十分的不幸,因为设计范式(参见第 16章)
惹来不少的麻烦。命名规则不是设计范式,它是相当的简单:
(1) 因为属性被命名为 xxx,我们代表性的创建两个方法:getXxx()和 setXxx()。注意 get 或 set后的第一
个字母小写以产生属性名。“get”和“set”方法产生同样类型的自变量。“set”和“get”的属性名和类
型名之间没有关系。
(2) 对于布尔逻辑型属性,我们可以使用上面的“get”和“set”方法,但我们也可以用“is”代替
“ get ”。
(3) Bean 的普通方法不适合上面的命名规则,但它们是公用的。
4。对于事件,我们使用“listener (接收器)”方法。这种方法完全同我们看到过的方法相同:
(addFooBarListener(FooBarListener)和 removeFooBarListener(FooBarListener)方法用来处理 FooBar 事
件。大多数时候内建的事件和接收器会满足我们的需要,但我们可以创建自己的事件和接收器接口。
上面的第一点回答了一个关于我们可能注意到的从 Java 1。0 到Java 1。1 的改变的问题:一些方法的名字太
过于短小,显然改写名字毫无意义。现在我们可以看到为了制造Bean 中的特殊的组件,大多数的这些修改不
得不适合于“get”和“set”命名规则。
现在,我们已经可以利用上面的这些指导方针去创建一个简单的Bean :
//: Frog。java
// A trivial Java Bean
package frogbean;
import java。awt。*;
import java。awt。event。*;
class Spots {}
public class Frog {
private int jumps;
private Color color;
private Spots spots;
private boolean jmpr;
public int getJumps() { return jumps; }
public void setJumps(int newJumps) {
jumps = newJumps;
}
public Color getColor() { return color; }
public void setColor(Color newColor) {
457
…………………………………………………………Page 459……………………………………………………………
color = newColor;
}
public Spots getSpots() { return spots; }
public void setSpots(Spots newSpots) {
spots = newSpots;
}
public boolean isJumper() { return jmpr; }
public void setJumper(boolean j) { jmpr = j; }
public void addActionListener(
ActionListener l) {
//。。。
}
public void removeActionListener(
ActionListener l) {
// 。。。
}
public void addKeyListener(KeyListener l) {
// 。。。
}
public void removeKeyListener(KeyL istener l) {
// 。。。
}
// An 〃ordinary〃 public method:
public void croak() {
System。out。println(〃Ribbet!〃);
}
} ///:~
首先,我们可看到Bean 就是一个类。通常,所有我们的字段会被作为专用,并且可以接近的唯一办法是通过
方法。紧接着的是命名规则,属性是jump,color,jumper,spots (注意这些修改是在第一个字母在属性名
的情况下进行的)。虽然内部确定的名字同最早的三个例子的属性名一样,在jumper 中我们可以看到属性名
不会强迫我们使用任何特殊的内部可变的名字(或者,真的拥有一些内部的可变的属性名)。
Bean 事件的句柄是ActionEvent 和KeyEvent ,这是根据有关接收器的“add”和“remove”命名方法得出
的。最后我们可以注意到普通的方法croak()一直是Bean 的一部分,仅仅是因为它是一个公共的方法,而不
是因为它符合一些命名规则。
13。18。2 用 Introspector 提取 BeanInfo
当我们拖放一个Bean 的调色板并将它放入到窗体中时,一个Bean 的最关键的部分的规则发生了。应用程序
构建工具必须可以创建 Bean (如果它是默认的构建器的话,它就可以做)然后,在此范围外访问Bean 的源
代码,提取所有的必要的信息以创立属性表和事件处理器。
解决方案的一部分在 11章结尾部分已经显现出来:Java 1。1 版的映象允许一个匿名类的所有方法被发现。
这完美地解决了Bean 的难题而无需我们使用一些特殊的语言关键字像在其它的可视化编程语言中所需要的那
样。事实上,一个主要的原因是映象增加到Java 1。1 版中以支持 Beans (尽管映象同样支持对象串联和远程
方法调用)。因为我们可能希望应用程序构建工具的开发者将不得不映象每个Bean 并且通过它们的方法搜索
以找到Bean 的属性和事件。
这当然是可能的,但是 Java 的研制者们希望为每个使用它的用户提供一个标准的接口,而不仅仅是使 Bean
更为简单易用,不过他们也同样提供了一个创建更复杂的 Bean 的标准方法。这个接口就是Introspector
类,在这个类中最重要的方法静态的getBeanInfo() 。我们通过一个类处理这个方法并且getBeanInfo()方法
全面地对类进行查询,返回一个我们可以进行详细研究以发现其属性、方法和事件的 BeanInfo 对象。
通常我们不会留意这样的一些事物——我们可能会使用我们大多数的现成的Bean ,并且我们不需要了解所有
的在底层运行的技术细节。我们会简单地拖放我们的Bean 到我们窗体中,然后配置它们的属性并且为事件编
写处理器。无论如何它都是一个有趣的并且是有教育意义的使用Introspector 来显示关于Bean 信息的练
458
…………………………………………………………Page 460……………………………………………………………
习,好啦,闲话少说,这里有一个工具请运行它(我们可以在 forgbean 子目录中找到它):
//: BeanDumper。java
// A method to introspect a Bean
import java。beans。*;
import java。lang。reflect。*;
public class BeanDumper {
public static void dump(Class bean){
BeanInfo bi = null;
try {
bi = Introspector。getBeanInfo(
bean; java。lang。Object。class);
} catch(IntrospectionException ex) {
System。out。println(〃Couldn't introspect 〃 +
bean。getName());
System。exit(1);
}
PropertyDescriptor'' properties =
bi。getPropertyDescriptors();
for(int i = 0; i 《 properties。length; i++) {
Class p = properties'i'。getPropertyType();
System。out。println(
〃Property type:n 〃 + p。getName());
System。out。println(
〃Property name:n 〃 +
properties'i'。getName());
Method readMethod =
properties'i'。getReadMethod();
if(readMethod != null)
System。out。println(
〃Read method:n 〃 +
readMethod。toString());
Method writeMethod =
properties'i'。getWriteMethod();
if(writeMethod != null)
System。out。println(
〃Write method:n 〃 +
writeMethod。toString());
System。out。println(〃====================〃);
}
System。out。println(〃Public methods:〃);
MethodDescriptor'' methods =
bi。getMethodDescriptors();
for(int i = 0; i 《 methods。length; i++)
System。out。println(
methods'i'。getMethod()。toString());
System。out。println(〃======================〃);
System。out。println(〃Event support:〃);
EventSetDescriptor'' events =
bi。getEventSetDescriptors();
for(int i = 0; i 《 events。length; i++) {
459
…………………………………………………………Page 461……………………………………………………………
System。out。println(〃Listener type: n 〃 +
events'i'。getListenerType()。getName());
Method'' lm =
events'i'。getListenerMethods();
for(int j = 0; j 《 lm。length; j++)
System。out。println(
〃Listener method:n 〃 +
lm'j'。getName());
MethodDescriptor'' lmd =
events 'i'。getListenerMethodDescriptors();
for(int j = 0; j 《 lmd。length; j++)
System。out。println(
〃Method descriptor:n 〃 +
lmd'j'。getMethod()。toString());
Method addListener =
events'i'。getAddListenerMethod();
System。out。println(
〃Add Listener Method:n 〃 +
addListener。toString());
Method removeListener =
events'i'。getRemoveListenerMethod();
System。out。println(
〃Remove Listener Method:n 〃 +
removeListener。toString());
System。out。println(〃====================〃);
}
}
// Dump the class of your choice:
public static void main(String'' args) {
if(args。length 《 1) {
System。err。println(〃usage: n〃 +
〃BeanDumper fully。qualified。class〃);
System。exit(0);
}
Class c = null;
try {
c = Class。forName(args'0');
} catch(ClassNotFoundException ex) {
System。err。println(
〃Couldn't find 〃 + args'0');
System。exit(0);
}
dump(c);
}
} ///:~
BeanDumper。dump()是一个可以做任何工作的方法。首先它试图创建一个BeanInfo 对象,如果成功地调用
BeanInfo 的方法,就产生关于属性、方法和事件的信息。在 Introspector。getBeanInfo()中,我们会注意到
有一个另外的自变量。由它来通知 Introspector访问继承体系的地点。在这种情况下,它在分析所有对象方
法前停下,因为我们对看到那些并不感兴趣。
因为属性,getPropertyDescriptors()返回一组的属性描述符号。对于每个描述符号我们可以调用
getPropertyType()方法彻底的通过属性方法发现类的对象。这时,我们可以用getName()方法得到每个属性
460
…………………………………………………………Page 462……………………………………………………………
的假名(从方法名中提取),getname()方法用 getReadMethod()和getWriteMethod()完成读和写的操作。最
后的两个方法返回一个可以真正地用来调用在对象上调用相应的方法方法对象(这是映象的一部分)。对于
公共方法(包括属性方法),getMethodDescriptors( )返回一组方法描述字符。每一个我们都可以得到相
当的方法对象并可以显示出它们的名字。
对于事件而言,getEventSetDescriptors()返回一组事件描述字符。它们中的每一个都可以被查询以找出接
收器的类,接收器类的方法以及增加和删除接收器的方法。BeanDumper 程序打印出所有的这些信息。
如果我们调用BeanDumper 在Frog 类中,就像这样:
java BeanDumper frogbean。Frog
它的输出结果如下(已删除这儿不需要的额外细节):
class name: Frog
Property type:
Color
Property name:
color
Read method:
public Color getColor()
Write method:
public void setColor(Color)
====================
Property type:
Spots
Property name:
spots
Read method:
public Spots getSpots()
Write method:
public void setSpots(Spots)
====================
Property type:
boolean
Property name:
jumper
Read method:
public boolean isJumper()
Write method:
public void setJumper(boolean)
====================
Property type:
int
Property name:
jumps
Read method:
public int getJumps()
Write method:
public void setJumps(int)
====================
Public methods:
public void setJumps(int)
public void croak()
public void removeActionListener(ActionListener)
public void addActionListener(ActionListener)
461
…………………………………………………………Page 463……………………………………………………………
public int getJumps()
public void setColor(Color)
public void setSpots(Spots)
public void setJumper(boolean)
public boolean isJumper()
public void addKeyListener(KeyListener)
public Color getColor()
public void removeKeyListener(KeyListener)
public Spots getSpots()
======================
Event support:
Listener type :
KeyListener
Listener method:
keyTyped
Listener method:
keyPressed
Listener method:
keyReleased
Method descriptor:
public void keyTyped(KeyEvent)
Method descriptor:
public void keyPressed(KeyEvent)
Method descriptor:
public void keyReleased(KeyEvent)
Add Listener Method:
public void addKeyListener(KeyListener)
Remove Listener Method:
public void removeKeyListener(KeyListener)
====================
Listener type:
ActionListener
Listener method:
actionPerformed
Method descriptor:
public void actionPerformed(ActionEvent)
Add Listener Method:
public void addActionListener(ActionListener)
Remove Listener Method:
public void removeActionListener(ActionListener)
====================
这个结果揭示出了 Introspector 在从我们的 Bean 产生一个BeanInfo 对象时看到的大部分内容。我们可注意
到属性的类型和它们的名字是相互独立的。请注意小写的属性名。(当属性名开头在一行中有超过不止的大
写字母,这一次程序就不会被执行。)并且请记住我们在这里所见到的方法名(例如读和与方法)真正地从
一个可以被用来在对象中调用相关方法的方法对象中产生。
通用方法列表包含了不相关的事件或者属性,例如 croak()。列表中所有的方法都是我们可以有计划的为
Bean 调用,并且应用程序构建工具可以选择列出所有的方法,当我们调用方法时,减轻我们的任务。
最后,我们可以看到事件在接收器中完全地分析研究它的方法、增加和减少接收器的方法。基本上,一旦我
们拥有BeanInfo ,我们就可以找出对Bean 来说任何重要的事物。我们同样可以为Bean 调用方法,即使我们
除了对象外没有任何其它的信息(此外,这也是映象的特点)。
462
…………………………………………………………Page 464……………………………………………………………
13。18。3 一个更复杂的 Bean
接下的程序例子稍微复杂一些,尽管这没有什么价值。这个程序是一张不论鼠标何时移动都围绕它画一个小
圆的 弧5蔽颐前聪率蟊昙 保 谄聊恢醒胂允疽桓鲎帧奥ang! ”,并且一个动作接收器被激活。画布。当
按下鼠标键时,我们可以改变的属性是圆的大小,除此之外还有被显示文字的色彩,大小,内容。BangBean
同样拥有它自己的addActionListener()和 removeActionListener()方法,因此我们可以附上自己的当用户
单击在BangBean 上时会被激活的接收器
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!