本文介绍面向对象设计之面向接口设计,并运用在聊天系统的设计中。
声明:本系列博文为"爱校码.中国"原创博文,版权所有,未经授权,不得转载或抄袭,若发现将依法追责。
接口是调用方的客户端程序代码与提供服务的类之间的一个纽带。Java接口只声明这种纽带而不实现。聊天系统的提供服务的具体类通过定义由接口声明的所有方法来实现该接口。而且一些常数属性定义在接口中。
Java语言中有一种类,其只能被继承和扩展,而不能用于创建自己的对象实例,这种类被称为抽象类。抽象类用于建模现实世界中的没有具体对象的纯粹抽象概念。例如,“图形”可以认为是一个纯粹抽象的概念,它本身没有任何具体的对象实例,任何具体的实例都是由“图形”经过特殊化形成的某个具体的种类的对象,而“图形”的概念是具体的图形经过抽象形成的。
在Java语言中,抽象类由关键字abstract
来修饰,由关键字abstrac
t修饰的方法称为抽象方法,而抽象方法没有方法体
,直接跟分号表示结束。抽象方法必须声明在抽象类中,格式如下:
[public] abstract class <class_name>{
[public| protected] abstract <return_type> <method_name>(<parameter_list>);
}
<class_name>
代表声明的抽象类名,<return_type>
、<method_name>
和<parameter_list>
分别表示抽象方法的返回类型、抽象方法名和参数列表。
含有抽象方法的类一定被声明为抽象类,但抽象类也可以存在非抽象方法。由于抽象方法只需要声明,不需要实现,这样在抽象类中没有定义完整的实现,因此不能用new
来创建对象,也不能定义abstract
构造方法和abstract
的static方法。可以声明一个抽象类的对象名,指向其非抽象子类的对象引用。
抽象类必须被继承,抽象类中的抽象方法是子类通过重写的方式来实现继承的,抽象方法的访问控制符不能为private,保证抽象类的子类必须重写实现父类中的所有抽象方法,或者如果子类没有重写父类中的所有抽象方法,则它自己也定义成抽象类。然而将抽象类中构造方法和抽象方法声明为protected
而非public是个好习惯,因为这些只能被抽象类的子类调用,可以使程序员更清楚它们不能被任意类的对象调用。
清单4-13 抽象类的声明及实现
1: abstract class Shape {
2: protected double length;
3: protected double width;
4: protected abstract double area();
5: protected Shape(double length,double width){
6: this.length=length;
7: this.width=width;
8: }
9: }
10:
11: class Square extends Shape {
12: Square(final double num, final double num1) {
13: super(num, num1);
14: }
15: double area() {
16: System.out.println("正方形的面积为:");
17: return length * width;
18: }
19: }
20:
21: public class AreaCalculate {
22: public static void main(String[] args) {
23: Square square =new Square(20.2,30.5);
24: System.out.print(square.area());
25: }
26: }
代码行1和行4为抽象类Shape
的定义和抽象方法area()
的声明,代码行15到行18为子类Square
对抽象方法area()
的重写实现。
接口是抽象类的特例,通过关键字interface
进行声明,其中只有包含静态常量和抽象方法,而没有成员变量的定义和其它成员方法的实现,格式如下:
[public] interface <interface_name> [extends <superinterface_list>]{
[public][static][final] <data_type> <variable_name>=<constant_value >;
[public][abstract] <return_type> <method_name>(<parameter_list>);
}
这里<interface_name>
代表接口名,<superinterface_list>
表示继承的父接口名列表,父接口之间用逗号(,
)分开;在声明静态常量时,<data_type>
、<variable_name>
与<constant_value >
分别表示常量类型、常量名和常量值,并且必须由public static final
修饰,这是由系统默认的,可显式也可隐含。静态常量必须用常量值将其初始化,意味着它不能在类实现时被修改;在定义抽象方法时,<return_type>
、<method_name>
与<parameter_list>
分别表示返回类型、方法名和参数列表,方法没有语句体,在参数列表后紧跟一个分号(;
)就结束了,而且必须由public abstract
修饰,也是被系统默认的,可显式也可隐含。有时抽象方法通过throws
引导异常,有关异常的内容在后续情景中讲解,格式如下:
[public][abstract] <return_type> <method_name>(<parameter_list>) [throws <Exception_lis>];
设计Java接口是为了在运行时支持动态调用方法。接口是这样一种机制,它使一个或多个方法的声明和继承结构分割开来,提供一种声明程序开发的“协议”概念。接口的声明类似于类的声明,只是定义接口时不必关心实现的细节,通常由public修饰,表示可以被任何类或接口使用,可单独保存在与接口名同名并且扩展名为.java
的文件中。
清单4-14 接口的声明
1: public interface Shape {
2: public abstract double area(); //计算面积
3: public abstract double volume(); //计算体积
4: public abstract String getName(); //获取物体形状名称
5: }
一个类根据需要扩展可以实现多个接口,而且必须完成一个接口中的所有方法的实现,被重写的方法的类型标志必须与接口中声明的方法参数的类型标志完全相同,包括访问控制修饰符、返回值类型与参数列表等,而且public
修饰符必须显式表示。如果实现一个接口的类为抽象类,则可以部分的实现接口中的方法。格式如下:
[public] class <class_name> [extends <superclass_name>] implements <interface_list>{
…
public <return_type> <method_name>(<parameter_list>) [throws <Exception_lis>]{
<method_body>
}
…
}
关键词implements
用于表示一个类实现若干接口,接口名之间用逗号隔开。如果一个类实现两个具有同名方法的接口,则该方法可以被两个接口的客户使用。
接口与实现的类可以不在同一个层次中,允许不同层次中的不相关的几个类实现同一个接口,表现相同的行为,而不需要考虑几个类之间的关系,这是因为接口只依赖于方法,不依赖于数据。
可以用接口作为类型声明一个对象的引用变量,任何实现了接口的类的实例化的对象的地址可以保存在这个变量中,若通过引用变量访问方法,则Java将根据当前的对象访问具体的方法实现,调用那个方法是由程序运行时动态决定的,调用通过接口可以找到正确的方法而不需知道这个方法是由那个类的对象实现的。
清单4-15 接口的实现
1: import java.text.DecimalFormat;
2: class Point implements Shape{
3: protected int x;
4: protected int y;
5: public Point(){
6: setPoint(0,0);
7: }
8: public Point(int x, int y){
9: setPoint(x,y);
10: }
11: public void setPoint(int x,int y){
12: this.x=x;
13: this.y=y;
14: }
15: public int getX(){
16: return x;
17: }
18: public int getY(){
19: return y;
20: }
21: public double area(){
22: return 0.0;
23: }
24: public double volume(){
25: return 0.0;
26: }
27: public String getName(){
28: return "Point";
29: }
30: }
31:
32: class Circle extends Point{
33: protected double radius;
34: public Circle(){
35: setRadius(0.0);
36: }
37: public Circle(double radius,int x,int y){
38: super(x,y);
39: setRadius(radius);
40: }
41: public double getRadius(){
42: return radius;
43: }
44: public void setRadius(double radius){
45: this.radius=radius;
46: }
47: public double area(){
48: return radius*radius*Math.PI;
49: }
50: public String getName(){
51: return "Cirle";
52: }
53: }
54:
55: class Cylinder extends Point {
56: protected double height;
57: public Cylinder(){
58: setHeight(0.0);
59: }
60: public Cylinder( double height,double radius,int x,int y){
61: super(radius,x,y);
62: setHeight(height);
63: }
64: public void setHeight(double height){
65: this.height=height;
66: }
67: public double getHeight(){
68: return height;
69: }
70: public double area(){
71: return Math.PI*2*radius*height+super.area()*2;
72: }
73: public double volume(){
74: return super.area()*height;
75: }
76: public String getName(){
77: return "Cylinder";
78: }
79: }
80:
81: public class ShapeTest {
82: public static void main(String[] args) {
83: Shape arrayShape[]=new Shape[3];
84: Point point=new Point(17,21);
85: arrayShape[0]=point;
86: Shape circle=new Circle(5.6,30,12);
87: arrayShape[1]=circle;
88: Shape cylinder=new Cylinder(13,6.7,20,20);
89: arrayShape[2]=cylinder;
90: DecimalFormat decimalPrecision=new DecimalFormat("0.00");
91: for (int i=0;i<arrayShape.length;i++){
92: System.out.println("图形名称:"+arrayShape[i].getName());
93: System.out.println("图形面积:"+
decimalPrecision.format(arrayShape[i].area()));
94: System.out.println("图形体积:"+
decimalPrecision.format(arrayShape[i].volume()));
95: }
96: }
97: }
代码行2为类Point
对接口Shape
的实现声明,代码行21到行29为类Point
对接口Shape
的方法area()
、volume()
和getName()
的实现。代码行32为类Circle对类Point
的继承声明,代码行47到行52为类Circle
对类Point
的方法area()
和getName()
重写。代码行55为类Cylinder
对类Poin
t的继承声明,代码行70到行78为类Cylinder
对类Poin
t的方法area()
、volume()
和getName()
的重写。
在聊天系统中,客户端需要经常发送聊天信息,将发送信息的行为可以定义在一个接口中,然后,通过其实现类完成接口方法的实现,如图所示:用一条带封闭三角形空箭头的虚线来表示实现关系,封闭三角形空箭头在提供规格说明的接口一边,而末端在提供实现的类一边,这样的图素符号等价于Java语言中的implements关键词。
清单4-16 IUpdateChat.java
清单4-17 UpdateChat.java
聊天系统的服务器经常发送查询到数据库和从数据库传递结果,也要处理聊天消息,并将其广播到每一个已登录的成员,可将这些处理行为定义在一个接口中,再通过接口的实现类完成处理行为的实施,如图所示:用一条带封闭三角形空箭头的虚线来表示实现关系。
清单4-18 IServer.java
清单4-19 Server.java
接口是Java程序设计的基本单元之一,它是抽象类的特例,相当于纯抽象类,即只包含有抽象方法的抽象类。接口只能定义public修饰的静态常量和抽象方法,表达了设计级的内容,表示通过该接口进行访问的调用者和该接口的实现者之间的约束关系。
接口中的方法由于没有方法体的实现,不能用于创建对象实例,但它可以被作为引用数据类型来声明对象变量,而对象变量的引用指向是与该接口具有实现关系的类的实例对象。
在【清单4-14】和【清单4-15】中,接口与实现类之间的关系如图所示,其中代码行86行和88行中的对象变量的类型声明为Shape,而对象变量的引用指向分别为Circle类的对象实例和Cylinder类的对象实例。
接口是观察实现该接口的类的一种视角,类实现一个接口就是承诺可以从某个角度对它进行观察。一个类可以实现多个接口,因为现实世界的实体总是可以从多个角度去观察。一个接口也可被多个类所实现,即可以通过同一角度去观察不同的实体。
由于接口是类的一种观察,因此接口往往使用形容词命名,Java API
中有许多接口是这样命名的,例如,“可序列化的(Serializable
)”、“可克隆的(Cloneable
)”等;也有使用名词命名接口,例如,“动作监听器(ActionListener
)”、“布局管理器(LayoutManager
)”等,表明实现这种接口的类可以承担某种角色,而角色也是对实体的一种观察角度。在Java中使用接口引用数据类型定义的对象变量,可通过该变量引用实现接口的类的对象实例,从而调用该接口所定义的方法来观察此对象实例,而对象实例则通过实现接口的方法支持这样的观察。
实现接口的类的类型是该接口的子类型,这是Java语言中子类型关系的一种重要形式。
在Java语言中,允许一个接口可以继承多个接口,如图所示:
由于接口中仅含有静态常量数据成员和抽象方法,因此接口的多继承不会带来重复继承的问题,一个祖先接口被后代接口多次继承时,在后代接口中总是只有一个版本。接口的继承是设计级的继承,这样的接口多继承不会带来复杂的重复继承和名称冲突问题。
当一个类实现多个接口时,需要多重实现,如图所示:
图中体现了一个类对多重接口的实现,其含义为水上飞机
不仅能飞
,也能在水面航行
。
假设一组对象具有同样的能力,它们都能飞
。可以构造一个公共接口Flyer
,它支持三种行为:takeoff
、land
和fly
。可以有多个类来实现Flyer
接口,飞机(Airplane
)能飞,鸟儿(Bird
)也能飞。不过飞机是一个交通工具(Vehicle
),而鸟儿是一种动物(Animal
),表明它们所代表的类既能从一个类继承,但也可以实现某些其他接口。如图所示:
通过接口类型Flyer的引用变量flyer声明如下:
Flyer flyer;
由于接口本身不能创建对象实例,引用变量flyer
总是用来指向它的子类型(实现Flyer
接口的类Airplane
或Bird
)的对象实例,代码实现如下:
flyer = new Airplane();
或者:
flyer = new Bird ();
因此,通过接口类型的引用变量flyer
调用方法fly()
总是会产生运行时多态。代码实现如下:
flyer.fly();
博文最后更新时间: