面向对象编程概念
如果你以前从来没有使用面向对象语言,你需要在开始编写JAVA代码之前先理解这个概念。你需要理解什么是对象、什么是类、对象和类的关系怎样以及使用消息怎样在对象之间进行通讯。本教程的前面部分将描述面向对象编程的概念,而后面的教程将教你怎样将这个概念编成代码。
对象是一些相关的变量和方法的软件集。软件对象经常用于模仿现实世界中我们身边的一些对象。对象是理解面向对象技术的关键。你在学习之前可以看看现实生活中的对象,比如狗、桌子、电视、自行车等等。你可以发现现实世界中的对象有两个共同特征:它们都有状态和行为。比如狗有自己的状态(比如名字、颜色、生育以及饥饿等等)和行为(比如摇尾巴等等)。同样自行车也有自己的状态(比如当前档位、两个轮子等等)和行为(比如刹车、加速、减速以及改变档位等等)。
而软件对象实际上是现实世界对象的造型,因为它同样有状态和行为。一个软件对象利用一个或者多个变量来维持它的状态。变量是由用户标识符来命名的数据项。软件对象用它的方法来执行它的行为。方法是跟对象有关联的函数(子程序)。
你可以利用软件对象来代表现实世界中的对象。你可能想用一个动画程序来代表现实世界中的狗,或者用可以控制电子自行车的程序来代表现实世界的自行车。同样你可以使用软件对象来造型抽象的概念,比如,事件是一个用在GUI窗口系统的公共对象,它可以代表用户按下鼠标按钮或者键盘上的按键的反应。
如图1是一个软件对象的公共可视代表。
(图1)
软件对象的状态和行为都可以用在对象中的变量和方法来表达。构造现实世界的自行车的软件对象要有指示自行车的当前状态的变量:速度为20mph,它的当前档位为第三档。这些变量就是我们熟知的实例变量,因为它们包含了用于特殊自行车对象的状态,并且在面向对象技术中,特殊的对象称为实例。
如图2所示,是作为软件对象的自行车造型。
(图2)
除了变量,软件自行车同样有用于刹车、改变踏板步调以及改变档位的方法。这些方法就是熟知的实例方法因为它们检查或者改变特殊自行车实例的状态。
以上的对象图显示了对象的变量组成了圆心部分。方法处在变量的四周并且在程序中从其它对象隐藏了对象的核心。用保护方法的方法来包装对象的变量称为封装。这个对象图就是对象理想的表示法,也是面向对象系统设计者努力的最后目标。然而这并不是全部的内容。通常,出于某种现实的理由,对象可能暴露它的一些变量或者隐藏一些方法。在JAVA编程语言中,一个对象可以为变量和方法指定四种访问等级中的一种。这个访问等级决定哪个对象和类可以访问变量或者方法。在JAVA中访问变量和方法可以转换为控制访问类的成员函数。封装相关的变量和方法到一个简洁的软件集是一个简单而且强有力的方法,它为软件开发者提供了两个主要好处:
模块性:对象的源代码可以独立于其它的对象源代码来进行编写和维护。同样,对象可以很容易地在系统中传递。你可以将你的自行车对象给其它的对象,它仍然可以正常工作。
信息隐藏:一个对象如果有一个公共的界面,那么其它的对象就可以与之进行通讯。这个对象可以维护私人的信息和方法,它可以在任何时候被改变而不影响依耐于它的其它对象。所以你不必理解自行车中齿轮的机理就可以使用它。
软件对象之间进行交互作用和通讯是利用消息的。
单一的一个对象通常不是很有用的。相反,一个对象通常是一个包含了许多其它对象的更大的程序或者应用程序。通过这些对象的交互作用,程序员可以获得高阶的功能以及更为复杂的行为。你的自行车如果不使用它的时候,它就是一堆铝合金和橡胶,它没有任何的活动。而只有当有其它的对象来和它交互的时候才是有用的。
软件对象与其它对象进行交互与通讯是利用发送给其它对象来实现的。当对象A想对象B来执行一个B中的方法,对象A就会消息给对象B。如图3所示。
(图3)
有时候,接收的对象需要更多的信息就至于它可以正确知道该如何做。比如,当你想改变自行车的齿轮,你就必须指出哪个齿轮。这个信息是将信息作为参数来传递的。如图4所示的现实了一个信息由三个组件组成:
1. 被寻址消息的对象(YourBicycle)
2.
3.
4. 要执行方法的名字(changeGears)
5.
6.
7. 这个方法需要的所有参数(lowerGear)
8.
(图4)
上面的三个组件对于接收方的对象执行相应的方法是给出了充分的信息。再也不需要其它的信息或者上下文了。
消息提供了两个重要的好处:
1. 对象的行为是通过它的方法来表达的,因此消息传递支持所有在对象之间的可能的交互。
2.
3.
4. 对象不需要在相同的进程或者相同的机器上来发送和接收消息给其它的对象
类实际上是对某种类型的对象定义变量和方法的原型。
在现实世界中,你经常看到相同类型的许多对象。比如 ,你的自行车只是现实世界中许多自行车的其中一辆。使用面向对象技术,我们可以说你的自行车是自行车对象类的一个实例。通常,自行车有一些状态(当前档位、两个轮子等等)以及行为(改变档位、刹车等等)。但是,每辆自行车的状态都是独立的并且跟其它自行车不同。
当厂家制造自行车的时候,厂商利用了自行车共有的特性来根据相同的蓝图制造许多自行车。如果制造一辆自行车就要产生一个新蓝图,那效率就太低了。
在面向对象软件中,同样地,可以让相同种类地许多对象来共有一些特性,比如矩形、雇员记录、视频夹等等。就象自行车制造商人,你可以利用相同种类的对象是相似的事实并且你可以为这些对象创建一个蓝图。对对象的软件蓝图叫做类。
自行车的类需要定义一些实例变量来包括当前档位、当前速度等等。这个类将为实例方法定义和提供实施方法,它允许骑车者改变档位、刹车以及改变脚踏板的节奏,如图5所示:
(图5)
当你创建了自行车类以后,你可以从这个类创建任意个自行车对象。当你创建了一个类的实例后,系统将为这个对象和的实例变量分配内存。每个实例将给所有实例变量的副本定义在类中。如图6所示:
(图6)
除了实例变量,类还要定义类的变量。类变量包含了被类所有实例共享的信息。比如,假设所有的自行车有相同的档位数。在本例子中,要定义一个实例变量来容纳档位数。每一个实例都会有变量的副本,但是在每一个实例中数值都是相同的。在这样的情况下,你可以定义一个类变量来包含档位数,这样所有的类的实例都共享这个变量。如果一个对象改变了变量,它就为改变那个类的所有对象。类同样可以定义类方法。你可以直接从类中调用类方法,然而你必须在特定的实例中调用实例方法。如图7所示。
(图7)
下面详细讨论一下实例和类成员,具体涉及变量和方法以及类变量和方法:
你这样声明一个成员变量,比如在类Myclass中有一个float型的aFloat:
class MyClass {
float aFloat;
}
这样你就声明一个实例变量。每次你创建一个类的实例的时候,系统就为实例创建了类的每一个实例变量的副本。你可以从对象中访问对象的实例变量。
实例变量跟类变量是不一样的,类变量示使用静态修改量来声明的。不管类创建了多少个实例,系统为每个类变量分配了类变量。系统为类变量分配的内存是在它第一次调用类的时候发生的。所有的实例共享了类的类变量的相同副本。你可以通过实例或者通过类本身来访问类变量。
它们的方法是类似的:你的类可以有实例方法和类方法。实例方法是对当前对象的实例变量进行操作的,而且访问类变量。另外一个方法,类方法不能访问定义在类中的实例变量,除非它们创建一个新的对象并通过对象来访问它们。同样,类方法可以在类中被调用,你不必需要一个实例来调用一个类方法。
缺省地,除非其它的成员被指定,一个定义在类中成员就是一个实例成员。这个在下面定义的类有一个实例变量,有一个整型的x,两个实例方法x和setX,它们设置其它对象以及查询x的数值。
class AnIntegerNamedX {
int x;
public int x() {
return x;
}
public void setX(int newX) {
x = newX;
}
}
每次你从一个类实例化一个新的对象,你可以得到每个类的实例变量的副本。这些副本都是跟新对象有关系的。因此,每次你从这个类实例化一个新的AnIntegerNamedX对象的时候,你得以得到跟新的AnIntegerNamedX对象有关的新副本。
一个类的所有实例共享一个实例方法的相同的实行;所有的AnIntegerNamedX实例都共享x和setX的相同执行。这里注意,两个方法x和setX是指对象的实例变量x。但是,你可能会问:如果所有AnIntergerNamedX共享x和setX的相同执行,会不会造成模棱两可的状态?答案当然是:不是。在实例方法中,实例变量的名字是指当前对象的实例变量,假如实例变量不是由一个方法参数来隐藏的。这样在x和setX中,x就等价于这个x,而不会造成混乱。
对于AnIntegerNamedX外部的对象如果想访问x,它必须通过特定的AnIntegerNamedX的实例来实现。假如这个代码片段处在其它对象的方法中。它创建了两种不同类型的AnIntegerNamedX,它设置了x为不同的数值,然后显示它们:
AnIntegerNamedX myX = new AnIntegerNamedX();
AnIntegerNamedX anotherX = new AnIntegerNamedX();
myX.setX(1);
anotherX.x = 2;
System.out.println("myX.x = " + myX.x());
System.out.println("anotherX.x = " + anotherX.x());
这里注意,代码使用setX来为myX设置x的值,而直接给anotherX.x指定一个数值。不管用什么方法,代码是在操作两个不同的x副本:一个包含在myX对象中一,另外一个包含在anotherX对象中。其输出是用以下代码片段来实现的:
myX.x = 1
anotherX.x = 2
上面代码显示了类AnIntegerNamedX的每一个实例有自己实例变量x的副本以及每个x有自己的数值。
你可以在声明成员变量的时候,指定变量是一个类变量而不是一个实例变量。相似地,你可以指定方法是一个类方法而不是一个实例方法。系统在第一次调用类来定义变量的时候创建了一个类变量的副本。所有的类实例共享了类变量的相同副本。类方法可以只操作类变量,它们不能访问定义在类中的实例变量。
为了指定一个成员变量为一个类变量,你可以使用static关键字。比如,我们可以修改一下上面的AnIntegerNamedX类,使得x变量现在是一个类变量:
class AnIntegerNamedX {
static int x;
public int x() {
return x;
}
public void setX(int newX) {
x = newX;
}
}
现在设置它们的x数值并显示不同的输出:
myX.x = 2
anotherX.x = 2
这次的输出不同,是因为x现在是一个类变量,所以就只有这个变量的副本,它是被AnIntegerNamedX的所有实例所共享的,包括myX和anotherX。当你在其它实例中调用setX的时候,你可以为所有的AnIntegerNamedX的所有实例改变x的数值。
同样,当我们声明一个方法的时候,你可以指定方法为类方法而不是实例方法。类方法只可以在类变量中进行操作,并且不能访问定义在类中的所有实例变量。
为了指定方法为类方法,你可以在方法声明处使用static关键字。下面,我们再次来修改AnIntegerNamedX类,使它的成员变量x为一个实例变量,以及它的两个方法为类方法:
class AnIntegerNamedX {
int x;
static public int x() {
return x;
}
static public void setX(int newX) {
x = newX;
}
}
当你想编译这个版本的AnIntegerNamedX,编译器就会显示如下的错误: AnIntegerNamedX.java:4: Can't make a static reference to
nonstatic variable x in class AnIntegerNamedX.
return x;
^
出现这些错误的原因是类方法不能访问实例变量,除非方法先创建AnIntegerNamedX的一个实例并且通过它来访问变量。
下面我们修改一下AnIntegerNamedX,让x变量成为类变量:
class AnIntegerNamedX {
static int x;
static public int x() {
return x;
}
static public void setX(int newX) {
x = newX;
}
}
现在为x设置数值,并打印出x数值:
myX.x = 2
anotherX.x = 2
再一次,我们通过myX来改变x,并将它改变为AnIntegerNamedX的其它实例。
实例成员和类成员之间的另外一个差别是类成员可以从类本身进行访问。你不必实例化类来访问它的类成员。下面让我们编写一段代码来直接从AnIntegerNamedX类中访问x和setX:
. . .
AnIntegerNamedX.setX(1);
System.out.println("AnIntegerNamedX.x = " + AnIntegerNamedX.x());
. . .
值得一提的是,你现在已经不用再创建myX和anotherX了。你可以设置x并直接AnIntegerNamedX类中检索x。你不能利用实例成员来处理它,你只能从一个对象来调用实例方法并且只可以从对象中访问实例变量。而你可以从类的实例或者从类本身来访问类变量和方法。
2.4.2初始化实例和类成员
下面讲讲初始化实例和类成员:
你可以在类中定义它们的时候,使用static初始化程序和实例初始化程序来为类和实例成员提供初始化数值:
class BedAndBreakfast {
static final int MAX_CAPACITY = 10;
boolean full = false;
}
这个对于原始数据类型是没有问题的。有时候,它可以用在创建数组和对象。但是这个初始化表单有它的限制,如下:
1. 初始化程序只可以执行用赋值语句表达的初始化 。
2.
3.
4. 初始化程序不能调用任何的包含异常错误的方法。
5.
6.
7. 如果初始化程序调用一个包含异常错误的方法,它不能进行错误恢复。
8.
如果你有一些初始化要完成,可能有些不能在初始化程序实现,因为出现了上面的限制之一,这时你不得不将初始化代码随意放置了。为了初始化类成员,在static初始化块中放置初始化代码。为了初始化实例成员,就要在构造函数中放置初始化代码了。
2.4.3 Static初始化块
下面再讲讲Static初始化块
下面举一个例子,如图8所示:
(图8)
errorStrings源代码必须在static初始化块中被初始化。这是因为错误恢复必须在源代码没有被找到得时候才被执行。同时,errorStrings是一个类成员,它不能在构造函数中被初始化。在前面得例子中一,一个static初始化块是以static关键字开头得,并且JAVA代码是用大括号“{}”括起来的。
一 个类可以有许多static初始化块,它可以出现在类中任何地方。系统保证static输出化块以及static初始化程序是按它们在源代码中的顺序被调用的。
如果你想初始化一个实例变量而且不能在变量声明处来处理它,那么就只能在构造函数中来为这个类初始化了。假如errorStrings是一个实例变量而不是一个类变量,你就可以使用以下的代码来初始化它:
import java.util.ResourceBundle;
class Errors {
ResourceBundle errorStrings;
Errors() {
try {
errorStrings = ResourceBundle.
getBundle("ErrorStrings");
} catch (java.util.MissingResourceException e) {
// error recovery code here
}
}
}
现在代码是在构造函数中为类来初始化这个errorStrings的。
有时,类包含了许多构造函数并且每个构造函数允许调用者为新对象的不同实例变量提供初始化数值。比如,java.awt.Rectangle有以下的三个构造函数:
Rectangle();
Rectangle(int width, int height);
Rectangle(int x, int y, int width, int height);
Rectangle()构造函数没有任何的参数,所以它不能让用户大小或者原点和大小提供初始化数值;而其它的两个构造函数,它可以让用户设置初始数值。
然而,所有的实例变量(原点和大小)都必须初始化。在这个例子中,类经常有一个构造函数来完成所有的初始化。其它的构造函数调用这个构造函数并且提供给它参数或者缺省数值。比如下面是以上所说的三个构造函数,它们初始化如下:
Rectangle() {
this(0,0,0,0);
}
Rectangle(int width, int height) {
this(0,0,width,height);
}
Rectangle(int x, int y, int width, int height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
JAVA语言支持实例初始化块,你可以放心使用它。这里建议使用构造函数来初始化,主要有以下三个原因:
1. 所有的初始化代码处在一个地方,这样使得代码更容易维护和阅读。
2.
3.
4. 缺省值可以清除地知道。
5.
6.
7. 构造函数广泛被JAVA程序设计人员所熟悉,包括相对新的JAVA程序设计人员,而实例初始化程序不能,而且他可能导致其它JAVA程序设计员的困惑。
你可能会注意到对象和类看起来很相似。在现实世界中,类和对象之间的区别经常是让程序员困惑的源泉。在现实世界中,很明显,类不能是它们描述的对象本身。然而,在软件中很困难来区分类和对象。有部分原因是软件对象只是现实世界中的电子模型或者是抽象概念。但是也因为对象通常有时是指类和实例。
一个类可以从它的父类继承状态和行为。继承为组织和构造软件程序提供了一个强大的和自然的机理。
总得说来,对象是以类得形式来定义得。你可能现在已经可以从它类知道许多对象了。即使你如知道,如果我告诉你它是一辆自行车,你就会知道它有两个轮子和脚踏板等等。面向对象系统就更深入一些了,它允许类在其它类中定义。比如,山地自行车、赛车以及串座双人自行车都是各种各样的自行车。在面向对象技术中,山地自行车、赛车以及串座双人自行车都是自行车类的子类。同样地,自行车类是山地自行车、赛车以及串座双人自行车的父类。这个父子关系可以如图9所示:
(图9)
每一个子例从父类中继承了状态。山地自行车、赛车以及串座双人自行车共享了这些状态:速度等。同样,每一个子类继承类从父类的方法,山地自行车、赛车以及串座双人自行车共享了这些行为:刹车、改变脚踏速度等等。
然而,子类不能受到父类提供的状态和行为的限制。子类可以增加变量和方法到从父类继承而来的变量和方法。比如,串座双人自行车有两个座位,这是它的父类没有的。
子类同样可以重载继承的方法并且为这些方法提供特殊执行方法。比如 ,如果你有一个山地自行车有额外 的齿轮设置,你就可以重载改变齿轮方法来使骑车者可以使用这些新的齿轮。
你也不能受限于继承的一个层次。继承树或者类的分级结构可以是很深。方法和变量是逐级继承的。总的来说,在分级结构的越下方,就有越多的行为。
如果对象类处于分级结构的顶端,那么每个类都是它的后代(直接地或者是间接地)。一种类型的对象保留任何对象的一个引用,比如类或者数组的一个实例。对象提供了行为,这些行为是运行在JAVA虚拟机所需要的。比如,所有类继承了对象的toString方法,它返回了代表对象的字符串。
下面说说我们为什么要使用继承,它到底有哪些好处呢?好处是有的:
1. 子类提供了特殊的行为,这是在父类中所没有的。通过使用继承,程序员可以多次重新使用在父类中的代码。
2.
3.
4. 程序员可以执行父类(称为抽象类)来定义总的行为。这个抽象的父类可以定义并且部分执行行为,但是绝大多数的父类是未定义和未执行的。其它的部分由程序员来实现特殊的子类。
接口是一个收集方法和常数表单的契约。当类执行一个接口,它就许诺声明在那个接口中执行所有的方法。
接口是一个设备或者一个系统,它是用于交互的无关的实体。根据这个定义,远程控制是一个在你和电视的接口;而英语是两个人之间的接口;强制在军事中的行为协议是不同等价人之间的接口。在JAVA语言中,接口是一个设备,它是用来与其它对象交互的设备。一个接口可能对一个协议是类似的。实际上,其它面向对象语言有接口的功能,但它们调用它们的接口协议。
自行车类和它的类分级结构定义了什么是自行车。但是自行车在其它方面与现实世界交互作用,例如,仓库中的自行车可以由一个存货程序来管理。一个存货程序不关心管理项目的哪一类只要项目提供某一信息,比如价格和跟踪数字。取代强迫类与其它无关项的关系,存货程序建立了通讯的协议。这个协议是由包含在接口中的常数和方法定义组成的。这个存货清单接口将要定义(但不执行)方法来设置和得到零售价格,指定跟踪数字等等。
为了在存货清单程序中操作,自行车类必须在执行接口的时候遵守这个协议。当一个了执行一个接口的时候,类遵守定义在接口中的所有方法。因此,自行车将为这些设置和获得零售价格并指定跟踪数值等等的方法提供执行。
你可以使用接口来定义一个行为的协议,这个行为可以有在类分级结构中任何类来执行。接口的主要好处有一下几点:
1. 不用人工强迫类关系在无关类中截获相似处。
2.
3.
4. 声明想执行的一个或者更多类的方法。
5.
6.
7. 在不暴露对象的类的前提下,暴露对象的编程接口。
这一小节将给你展现创建对象、执行类、发送消息、创建一个父类以及执行一个接口的代码。
以下是一个applet(applet是用JAVA编程语言编写的程序,它可以运行在兼容JAVA平台的网络浏览器,比如HotJava或者Netscape Navigator)的程序,名为ClickMe。如图10所示,当你点击方框内任何地方,一个红点就会出现。
(图10)
提示:上面的applet需要JDK1.1。如果你使用老的不支持JDK1.1的浏览器,你将不能运行这个applet。相反,你需要在一个1.1浏览器上来看这个网页,比如在HotJava、JDK Applect浏览器(appletviewer)或者某个版本的Netscape Navigator和Internet Explorer。
下面具体解释一下这个Applet。
ClickMe Applet是一个相对简单的程序因此它的代码就短了多了。但是,如果你还没有太多的编程经验,你可以发现这些代码也不是那么容易的。我们不要求你马上理解程序中的每个问题,并且这节教程也不是讲了十分详细的。这里的目的示暴露一些源代码给你并且跟你刚才所学道的概念和技术联系上。你将在以后的教程中学到更详细的内容。
2.7.1ClickMe的源代码和Applet标签
为了编译这个applet你需要两个源文件:ClickMe.java和Spot.java。为了运行这个applet你需要利用这个applet标签来创建一个html文件:
然后装载网页到浏览器或者appletviewer工具。并且确保所有必要的文件都在相同的目录中。 如图11所示:
(图11)
在这个applet中有许多对象。两个最明显的是:applet本身和红点。
浏览器在包含applet的HTML代码中碰到applet标签的时候就创建了applet对象。这个applet标签从创建applet对象的地方提供类的名字。在本例子中,这个类的名字为ClickMe。
ClickME.applet将创建一个对象来在屏幕上画出点。每次你在applet中点击鼠标的时候,applet就将通过改变对象的x和y坐标来移动红点。这个点不是自己画出来的,它是applet画出的,它是根据包含在点对象中的信息画出的。
除了前面两个明显的对象,另外还有一些看不见的对象呢。有代表三种颜色(黑、白、红)的三个对象以及代表点击鼠标的用户动作的事件对象等等。
因为代表在屏幕上点的对象是很简单,接下来让我们看看这个名为spot的类吧。它声明了三个实例变量:包括点半径的size,包含点当前水平位置的x坐标以及包含点当前垂直位置的y坐标:
public class Spot {
//实例变量
public int size;
public int x, y;
//构造函数
public Spot(int intSize) {
size = intSize;
x = -1;
y = -1;
}
}
另外,类有一个构造函数,它用来初始化由类创建的新对象。构造函数跟类有相同的名字。这个构造函数初始化所有三个对象的变量。Size的初始化数值是在调用的时候座位参数提供的。x和y变量都被设置为-1,这里-1的目的是为了让点在开始的时候处于屏幕的外面,即产生假不可视的效果。
这个applet是在applet初始化的时候创建了一个新的点对象。下面是applet类的相关代码:
private Spot spot = null;
private static final int RADIUS = 7;
...
spot = new Spot(RADIUS);
第一行声明了一个名为spot的变量,它是Spot数据类型,并且初始化这个变量为NULL。第二行声明了一个整型变量,名为RADIUS,它的值为7。最后一行是创建一个对象。New关键字为对象分配了内存空间。Spot(RADIUS)调用了上面已经描述了的构造函数并且传递RADIUS数值,这样点对象的size就被设置为7。如图12所示的左图代表了Spot类,而右边的是代表了spot对象。
(图12)
就如所知道的,对象A可以使用消息来请求对象B做一些事情,一个消息有三个组成部分:
1. 消息被寻址的对象
2.
3.
4. 要执行执行方法的名字
5.
6.
7. 方法需要的任何参数
8.
在ClickMe applet种有以下两行这样的代码:
g.setColor(Color.white);
g.fillRect(0, 0, getSize().width - 1, getSize().height - 1);
这两个消息都是从applet到名为g的对象。其中g是一个图形对象,它知道怎样在屏幕上简单画一些形状或者文本。这个对象在浏览器指示applet来画的时候提供了applet。上面代码的第一行设置颜色为白色,第二行是填充一个指定大小的矩形区域,它的颜色为白色。如图13所示,是一个消息的三个组成部分:
(图13)
为了在浏览器种运行,对象必须是一个applet。这就意味着对象必须是类的一个实例,这个类是从由JAVA平台提供的Applet类派生而来的。
ClickMe applet对象是一个ClickMe类的一个实例,它是这样声明的:
public class ClickMe extends Applet implements MouseListener {
...
}
上面的语句就产生了Applet的一个子类。ClickMe继承了父类的许多功能,包括初始化、由浏览器来开始和结束、在浏览器区域画图以及对接收到的鼠标事件注册。除了有了这些功能,ClickMe类还要实现以下的功能:它的画图代码在paint的方法中,初始化代码必须在init方法中等等。
public void init() {
... // 这里加入ClickMe的初始化代码
}
public void paint(Graphics g) {
... // 这里加入ClickMe的画图代码
}
ClickMe applet是通过在鼠标点击出显示一个红点来响应鼠标的点击事件。如果对象想通知鼠标点击,JAVA平台事件系统需要对象执行MouseListener接口。这个对象必须同时作为鼠标监听器来注册。
这个MouseListener接口声明了五种不同的志芋工,每种方法是用在鼠标被点击的时候对不同鼠标事件的调用。当鼠标被点击的时候,或者当鼠标移动到applet外面的时候等等。
下面是ClickMe applet完整的代码。其中mousePressed是处理鼠标事件的:
本节教程测试一下你对对象、类、消息等等的理解,我们是通过做一些练习以及回答一些问题来进行的。
你可以使用API文档来回答这些问题:
1. ClickMe applet使用Color.red来设置画图颜色为红色。其它有什么颜色可以象这样来使用?
2.
3.
4. 怎样设置颜色为紫色(purple)?
5.
现在,利用你从API文档中学到的知识来修改ClickMe applet。为了编译这个程序,你同样需要Spot.java文件。具体修改如下:
1. 修改这个applet来画一个绿色的方框而不是一个红点。
2.
3.
4. 修改这个applet来用紫色显示你的名字而不是一个红点。