这节教程讲讲两个JavaTM编程语言特性来帮助你管理类之间的关系。首先,你将学到怎样编写和使用对象之间的通讯接口协议。然后,你会学到怎样捆绑类和接口到一个包。
6.1 创建接口
在这节教程中你将看到一个执行接口的例子,而且可以阅读到一些关于接口的内容:它们是为了什么、为什么要编写接口、怎样编写接口等等。
JAVA编程语言支持接口,你可以使用接口来定义行为的协议。这些行为可以在类分级结构中的任何类中被执行。
6.1.1什么是接口
这一小节定义了什么是接口,给出了一个接口的例子以及怎样使用它,并且讨论为什么你需要在程序中使用接口。
接口定义了行为的协议,这些行为可以在类分级结构中的任何类中被执行。接口定义了许多方法但是没有执行它们。类履行接口协议来执行所有定义在接口中的方法,因此适合某些行为。
因为接口是简单的未执行的系列以及一些抽象的方法,你可能会思考究竟接口于抽象类有什么区别。知道了它们的区别是相当重要的。 它们之间有以下的区别:
接口不能执行任何的方法,而抽象类可以。
类可以执行许多接口,但只有一个父类。
接口不是类分级结构的一部分。而没有联系的类可以执行相同的接口。
下面我们还是举个例子。
假如你已经编写了一个类,这个类可以注意股票的价格。这个类允许其它的类来注册以知道什么什么特定的股票的价格改变了。首先,编写StockMonitor类,它可以执行一个方法来让其它对象的注册以得到通知。
public class StockMonitor {
public void watchStock(StockWatcher watcher,
String tickerSymbol, double delta) {
...
}
}
这个方法的第一个参数未StockWatcher对象。StockWatcher是一个接口的名字,它的代码将在后面的教程中给出。这个界面声明了一个方法:valueChanged。要被通知股票改变的对象必须试执行接口和valueChanged方法的类的实例。其它两个参数提供了股票的符号以观察改变的数目。当StockMonitor类检测到一个感兴趣的变化,它就会调用watcher的valueChanged方法。
WatchStock方法要通过第一个参数的数据类型确保所有注册对象执行valueChanged方法。如果StockMonitor已经使用了一个类名作为数据类型,就要强制它的用户的类关系。因为类只可以有一个父类,所以这也限制了什么类型的数据可以使用这个服务。通过使用接口,注册对象类可以是Applet或者Thread等等,比如它允许类分级结构中的任何类使用这个服务。
6.1.2 定义接口
定义一个接口跟创建一个新类是相似的。接口定义需要两个组件:接口定义和接口实体。
interfaceDeclaration {
interfaceBody
}
interfaceDeclaration声明了各种关于接口的属性,比如它的名字和是否扩展其它的接口。这个interfaceBody包含了在接口中常量和方法声明。
如图29所示给出了接口定义有两个组件:接口声明和接口实体。接口声明定义了各种关于接口的属性,比如它的名字和是否扩展其它的属性;接口实体包含了常数和用于接口的方法声明。
(图29)
StockWatcher接口和接口定义的结构为:
public interface StockWatcher {
final String
sunTicker = "SUNW";
final String oracleTicker = "ORCL";
final String ciscoTicker = "CSCO";
void valueChanged(String tickerSymbol, double newValue);
}
接口定义了三个常量,它们是watchable股票的股票行情自动收集器的符号。这个接口也定义了valueChanged方法,但是没有执行它。执行这个接口的类为方法提供了执行。
下面讲讲界面的声明:
如图30给出了接口声明的所有可能组件:
(图30)
在接口定义中需要两个元素:interface关键字和接口的名字。Public指示了接口可以在任何的包中任何的类中使用。如果你没有指定接口为public,那么接口就只能在定义接口的包中类使用了。
接口定义可以有另外一个组件:superinterfaces系列。一个接口可以扩展另外的接口,这跟类可以扩展一样。但是,类只能扩展一个另外的类,而接口可以扩展任意个接口。Superinterfaces系列椒以逗号分隔的所有接口,这些接口可以由新的接口扩展。
这时候,你也许会问:那接口实体怎么说呢?别急,下面就会解释了:
接口实体为所有包含在接口中的方法包含了方法声明。在接口中的方法声明可以紧跟着一个逗号,因为接口不为定义在它上面的方法提供执行。所有定义在接口中的方法可以隐含地为public和abstact。
接口可以包含常量s声明以及方法声明。所有定义在接口中的常量可以是public、 static和final。定义在接口中的成员声明不允许使用一些声明修饰语,比如你不能在接口中的成员声明中使用transient、volatile或者synchronized。同样你不能在声明接口的成员的时候使用private和protected修饰语。
6.1.3 执行接口
为了使用接口,你要编写执行接口的类。如果一个类可以执行一个接口,那么这个类就提供了执行定义在接口中的所有方法的方法。
一个接口定义了行为的协议。一个类可以根据定义在接口中的协议来执行接口。为了声明一个类执行一个接口,要包括一条执行语句在类的声明中。你的类可以执行多个接口(因为JAVA平台支持接口的多个继承),因此可以在implements后面列出由类执行的接口系列,这些接口是以逗号分隔的。
以下是一个applet的部分例子,它执行StockWatcher接口:
public class StockApplet extends Applet implements StockWatcher {
...
public void valueChanged(String tickerSymbol, double newValue) {
if (tickerSymbol.equals(sunTicker)) {
...
} else if (tickerSymbol.equals(oracleTicker)) {
...
} else if (tickerSymbol.equals(ciscoTicker)) {
...
}
}
}
这里注意,这个类引用了定义在StockWatcher.sunTicker的常量,如oracleTicker等等。执行接口的类继承了定义在接口中的常量。因此这些类可以使用简单的名字来引用常量。你可以象下面的语句,使其它任何类使用接口常量:
StockWatcher.sunTicker
从本质上讲,当类执行一个接口的时候,就签定了一个契约。所有的类必须执行所有定义在接口以及它的superinterfaces中的方法,以及类必须定义为abstract。这个方法的签名(名字和在类中参数类型的数目)必须匹配方法的签名。StockApplet执行SockWatcher接口,因此applet提供了valueChanged方法。这个方法公开地更新了applets的显示或者使用这个信息。
6.1.4 作为一种类型来使用接口
当你定义一个新的接口的时候,从本质上讲,你定义了一个新的引用数据类型。你可以在你使用其它类型的名字(比如变量声明、方法参数等等)的地方,使用接口名字。回忆一下,我们前面在StockMonitor类中的watchStock方法中的第一个参数的数据类型,为StockWatcher:
public class StockMonitor {
public void watchStock(StockWatcher watcher,
String tickerSymbol, double delta) {
...
}
}
只有执行接口的类的实例可以赋值为一个引用变量,它的类型为接口名字。因此只有执行StockWatcher接口的类的实例可以注册以得到股票数值改变的通知。
6.1.5 接口不能发展
如果你将接口传给其它的程序员,接口有个限制你应该注意:接口不能发展。下面对此进行解释:
假如你想怎样一个函数到StockWatcher。比如,你想增加一个汇报当前股票价格的方法,而不管数值是否被改变了:
public interface StockWatcher {
final String sunTicker = "SUNW";
final String oracleTicker = "ORCL";
final String ciscoTicker = "CSCO";
void valueChanged(String tickerSymbol, double newValue);
void currentValue(String tickerSymbol, double newValue);
}
但是,如果你做了这个改变的话,执行老版本的StockWatcher接口的所有类都将中断,因为它们不能执行这个接口了。接口不能发展,这是所有程序员要知道的。为了达到以上增加一个方法的目的你可以创建更多的界面。比如,你可以创建一个
StockWatcher的subinterface(子接口)StockTracker::
public interface StockTracker extends StockWatcher {
void currentValue(String tickerSymbol, double newValue);
}
6.2 创建和使用包
本节教程将描述怎样捆绑你的类到包中,并且教你怎样使用包中的类:
为了使得类更容易地被发现和使用,以及避免名字冲突、控制访问,程序员要捆绑相关的类和接口到包中。类和接口都是JAVA平台的一部分,它们都是各种由函数捆绑类的包的成员:基本类是在java.lang中,而用于阅读和书写的类在java.io中等等。你可以放置你的类和接口到包中:
让我们看看以下的类并检查为什么你想将它们放置到包中。你可以编写一系列的图形对象的类:比如circles、rectangles、lines和points,你同样可以编写接口Draggable,它是在用户拖动鼠标的时候可以移动这些图形对象:
//在Graphics.java中的文件
public abstract class Graphic {
. . .
}
//在 Circle.java 的文件
public class Circle extends Graphic implements Draggable {
. . .
}
//在 Rectangle.java 中的文件
public class Rectangle extends Graphic implements