javaSE 基础笔记之抽象类和接口
第七章抽象类和接口
学习目标:
² 理解和掌握抽象类
² 掌握接口的基本概念
² 掌握多重接口
² 理解解口的基本思想
² 掌握接口作为类型使用
² 理解接口和抽象类的选择
一:抽象类
1:抽象类是什么
有时在开发中,要创建一个体现某些基本行为的类,并为该类声明方法,但不能在该类
中实现该行为,取而代之,在子类中实现该方法。知道其行为的其它类可以在类中实现这些
方法。
这种只给出方法定义而不具体实现的方法被称为抽象方法,抽象方法是没有方法体的,
在代码的表达上就是没有“{}” 。
怎么表示一个方法是抽象的呢?使用abstract修饰符来表达抽象。
abstract 修饰符可以与类和方法一起使用。被修饰的类不能被实例化,被修饰的方法必
须在包含此方法的类的子类中被实现。
抽象类简单地说:使用abstract修饰的类就是抽象类。
示例如下:
public abstract class Test{//抽象类定义
public abstract void doItByHand();//抽象方法定义
}
2:什么情况下会使用抽象类
例如,考虑一个Drawing类。该类包含用于各种绘图设备的方法,但这些必须以独立平
台的方法实现。它不可能去访问机器的录像硬件而且还必须是独立于平台的。其意图是绘图
类定义哪种方法应该存在,但实际上,由特殊的从属于平台子类去实现这个行为。
正如Drawing类这样的类,它声明方法的存在而不是实现,以及带有对已知行为的方法
的实现,这样的类通常被称做抽象类。通过用关键字abstract进行标记声明一个抽象类。被
声明但没有实现的方法(即,这些没有程序体或{}) ,也必须标记为抽象。
1 public abstract class Drawing { 2 3 public abstract void drawDot(int x, int y); 4 5 public void drawLine(int x1, int y1, int x2, int y2) { 6 7 // draw using the drawDot() method repeatedly. 8 9 } 10 11 }
3:抽象类的使用
抽象类不能直接使用,必须用子类去实现抽象类,然后使用其子类的实例。然而可以
创建一个变量,其类型是一个抽象类,并让它指向具体子类的一个实例,也就是可以使用抽
象类来充当形参,实际实现类作为实参,也就是多态的应用。
不能有抽象构造方法或抽象静态方法。
abstract类的子类为它们父类中的所有抽象方法提供实现,否则它们也是抽象类。
1 public class MachineDrawing extends Drawing { 2 3 public void drawDot (int machX, int machY) { 4 5 // 画点 6 7 } 8 9 }Drawing d = new MachineDrawing();4:抽象类和抽象方法
在下列情况下,一个类将成为抽象类:
(1) :当一个类的一个或多个方法是抽象方法时;
(2) :当类是一个抽象类的子类,并且不能为任何抽象方法提供任何实现细节或方法主
体时;
(3) :当一个类实现一个接口,并且不能为任何抽象方法提供实现细节或方法主体时;
注意:
(1) :这里说的是这些情况下一个类将成为抽象类,没有说抽象类一定会有这些情况。
(2) :一个典型的错误:抽象类一定包含抽象方法。 但是反过来说“包含抽象方法的类一
定是抽象类”就是正确的。
(3) :事实上,抽象类可以是一个完全正常实现的类
二:接口的基本概念
接口可以说是 Java程序设计中最重要的概念之一了, “面向接口编程”是面向对象世界
的共识,所以深刻理解并熟练应用接口是每一个学习Java 编程人员的重要任务
1:接口概念
Java 可以创建一种称作接口(interface)的类,在这个类中,所有的成员方法都是抽象的,
也就是说它们都只有定义而没有具体实现,接口是抽象方法和常量值的定义的集合。从本质
上讲,接口是一种特殊的抽象类,这种抽象类中只包含常量和方法的定义,而没有变量和
方法的实现。定义接口的语法格式如下:
访问修饰符 修饰符 interface 接口名称 {
抽象属性集
抽象方法集
}
示例程序:Runner.java
1 public interface Runner { 2 3 int ID = 1; 4 5 public void start(); 6 7 public void run(); 8 9 public void stop(); 10 11 }
目前看来接口和类差不多。确实如此,接口本就是从抽象类中演化而来的,因而除特别
规定,接口享有和类同样的“待遇” 。比如,源程序中可以定义 0~多个类或接口,但最多只
能有一个 public的类或接口,如果有则源文件必须取和 public的类和接口相同的名字。和类
的继承格式一样,接口之间也可以继承,子接口可以继承父接口中的常量和抽象方法并添加
新的抽象方法等。
但接口有其自身的一些特性:
接口中声明的属性默认为,也只能是 public static final的,因而在常量声明时可以省略这些修饰符; 接口中只能定义抽象方法,这些方法默认为 public abstract的、也只能是 public abstract的,因而在声明方法时可以省略这些修饰符; 和继承抽象父类类似,Java 类还可以“实现”接口。
Java 类在继承父类的同时可以同时实现多个接口,也继承了所有接口中的全部成分,
仍然必须重写(实现)全部抽象方法,否则只能声明为抽象类。
类实现多个接口时,您需要做的仅是在关键字“implements”后用逗号分隔接口。总之,
接口就是其它类需要实现的行为模板(以方法的形式表现)。
例:接口定义及实现
程序:Person.java
1 public class Person implements Runner { 2 3 public void start() { 4 5 //弯腰、蹬腿、咬牙、瞪眼、 开跑 6 7 } 8 9 public void run() { 10 11 // 摆动手臂、 维持直线方向 12 13 } 14 15 public void stop() { 16 17 // 减速直至停止、喝水。 18 19 } 20 21 } 22 23
2:为什么使用接口
两个类中的两个类似的功能,调用它们的类动态地决定一种实现, 那它们提供一个抽象
父类,子类分别实现父类所定义的方法。
问题的出现:Java 是一种单继承的语言,一般情况下,哪个具体类可能已经有了一个父
类,解决是给它的父类加父类,或者给它父类的父类加父类,只到移动到类等级结构的最顶
端。这样一来, 对一个具体类的可插入性的设计,就变成了对整个等级结构中所有类的修改。
接口是可插入性的保证。
在一个等级结构中的任何一个类都可以实现一个接口, 这个接口会影响到此类的所有子
类,但不会影响到此类的任何父类。此类将不得不实现这个接口所规定的方法,而其子类可
以从此类自动继承这些方法,当然也可以选择置换掉所有的这些方法,或者其中的某一些方
法,这时候,这些子类具有了可插入性(并且可以用这个接口类型装载,传递实现了他的所
有子类) 。
我们关心的不是哪一个具体的类,而是这个类是否实现了我们需要的接口。
接口提供了关联以及方法调用上的可插入性,软件系统的规模越大,生命周期越长,接
口使得软件系统的灵活性和可扩展性,可插入性方面得到保证。
3:接口的基本作用
接口把方法的特征和方法的实现分割开来。这种分割体现在接口常常代表一个角色,它
包装与该角色相关的操作和属性,而实现这个接口的类便是扮演这个角色的演员。一个角色
由不同的演员来演,而不同的演员之间除了扮演一个共同的角色之外,并不要求其它的共同
之处。
对于下述情况,接口是有用的:
(1) 声明方法,期望一个或更多的类来实现该方法。
(2)揭示一个对象的编程接口,而不揭示类的实际程序体。 (当将类的一个包输送到其
它开发程序中时它是非常有用的。 )
(3) 捕获无关类之间的相似性,而不强迫类关系。
(4)可以作为参数被传递到在其它对象上调用的方法中
4:接口示例
1:接口示例1:接口定义,接口实现类
2:接口示例2:一个接口可以有多个不同的实现类
3:接口示例3:一个类可以继承,也可以实现
三:多重接口
1:多重接口的基本概念
一个类只可以继承一个父类(保持有兄弟关系,同属一族) ,但可以实现多个接口,而
不需要在继承树中同属一个族。一个类实现多个接口就被称为多重接口。
类不可多重继承,因为两个父类的变量或方法可能重复。而接口不会继承变量,即便两
个接口方法定义相同,接口中没有方法实现,类中也只有一个方法实现。
如下图所示:SeaPlane实现了Flyable和 Sailer 两个接口,继承了 Airplane 类
接口的多重实现机制从一定程度上弥补了 Java 类单继承的局限,不但多个无关的类可
以实现同一个接口,一个类可以同时实现多个无关的接口。与继承关系类似,接口与其实现
类之间存在多态性。如果说继承父类代表着获得一种“家族身份”的话,实现接口则意味着
取得了某种“资格” 。在 java GUI(Graphical User Interface,图形用户界面)事件处理中,
大量使用了接口,届时读者会对此有更深入的了解。
2:extends和implements
注意 extends从句放在 implements 从句之前,这样一个类既具有自己定义的方法,继
承父类方法,同时具有覆盖的接口方法。
1 class Bird extends Animal implements Flyable{ 2 3 //自己定义 4 5 public void buildNest(){} 6 7 public void layEggs(){} 8 9 //继承父类 10 11 public void eat(){} 12 13 //覆盖方法 14 15 public void takeOff(){//加速起飞} 16 17 public void land(){//减速着陆} 18 19 public void fly(){//保持引擎运转} 20 21 } 22 23
3:接口多继承
事实上,Java的接口是可以实现多继承的,类不允许多继承。如下示例:
1 public interface Test extends A,B { 2 3 //定义 4 5 } 6 7 interface A{ 8 9 //定义 10 11 } 12 13 interface B{ 14 15 //定义 16 17 }
四:接口的基本思想
接口及相关机制的最基本作用在于:通过接口可以实现不相关类的相同行为,而不需考
虑这些类之间的层次关系。根据接口可以了解对象的交互界面,而不需了解对象所属的类。
面向对象程序设计讲究“提高内聚,降低耦合” ,那么不同的程序模块怎么相互访问呢,
就是通过接口,也就是接口是各部分对外的统一外观。接口在 Java 程序设计中体现的思想
就是隔离,因为接口只是描述一个统一的行为,所以开发人员在面向接口编程时并不关心具
体的实现。
由以上讲到的接口的作用和基本思想可以看到,接口在面向对象的 Java 程序设计中占
有举足轻重的地位。事实上在设计阶段最重要的任务之一就是设计出各部分的接口,然后通
过接口的组合,形成程序的基本框架结构。
具体的接口的在设计和实现中的使用,本书将在第二部分 Java 程序设计和第三部分案
例分析里面,结合具体的例子详细讲述,希望同学们好好体会。
五:接口作为类型使用
1:接口的使用
接口的使用与类的使用有些不同。
在需要使用类的地方,会直接使用 new关键字来构建一个类的实例进行应用:
ClassA a = new ClassA(); 这是正确的
但接口不可以这样用,因为接口不能直接使用 new关键字来构建实例。
InterfaceA a = new InterfaceA(); 这是错误的
接口在使用的时候要实例化相应的实现类。
InterfaceA a = new ImplementsA(); 这是正确的
下面就可以使用a.method()的方式来调用接口的方法了。
2:接口作为类型使用
接口作为引用类型来使用,任何实现该接口的类的实例都可以存储在该接口类型的变量
中,通过这些变量可以访问类中所实现的接口中的方法,Java 运行时系统会动态地确定应
该使用哪个类中的方法,实际上是调用相应的实现类的方法。
示例如下:
1 public class Test { 2 3 public void test1(A a){ 4 5 a.doSth(); 6 7 } 8 9 public static void main(String[] args) { 10 11 Test t = new Test(); 12 13 14 15 A a = new B(); 16 17 t.test1(a); 18 19 } 20 21 } 22 23 interface A{ 24 25 public int doSth(); 26 27 } 28 29 class B implements A{ 30 31 @Override 32 33 public int doSth() { 34 35 System.out.println("now in B"); 36 37 return 123; 38 39 } 40 41 }
运行结果:now in B
大家看到接口可以作为一个类型来使用,把接口作为方法的参数和返回类型。
六:接口和抽象类的选择
由于从某种角度讲,接口是一种特殊的抽象类,它们的渊源颇深,有很大的相似之处,
所以在选择使用谁的问题上很容易迷糊。
很多人有过这样的疑问:为什么有的地方必须使用接口而不是抽象类,而在另一些地方,
又必须使用抽象类而不是接口呢?或者说,在考虑Java类的一般化问题时,很多人会在接口和
抽象类之间犹豫不决,甚至随便选择一种。
实际上接口和抽象类的选择不是随心所欲的。 要理解接口和抽象类的选择原则,有两个概
念很重要:对象的行为和对象的实现。如果一个实体可以有多种实现方式,则在设计实体行为的
描述方式时,应当达到这样一个目标:在使用实体的时候,无需详细了解实体行为的实现方式。
也就是说,要把对象的行为和对象的实现分离开来。既然 Java 的接口和抽象类都可以定义不提
供具体实现的方法,在分离对象的行为和对象的实现时, 到底应该使用接口还是使用抽象类呢?
在接口和抽象类的选择上,必须遵守这样一个原则: 行为模型应该总是通过接口而不是抽象
类定义。所以通常是:
(1) :优先选用接口,尽量少用抽象类。
练习实践
程序 1:
接口与Java 的“多继承”
需求:定义多个接口,并实现多接口继承
目标:
1. 接口的含义;
2. Java 中多继承的接口实现;
程序:
1 //: Adventure.java 2 3 package com.useful.java.part3; 4 5 import java.util.*; 6 7 interface CanFight { 8 9 void fight(); 10 11 } 12 13 interface CanSwim { 14 15 void swim(); 16 17 } 18 19 interface CanFly { 20 21 void fly(); 22 23 } 24 25 class ActionCharacter { 26 27 public void fight() {} 28 29 } 30 31 class Hero extends ActionCharacter implements CanFight, CanSwim, CanFly { 32 33 public void swim() {} 34 35 public void fly() {} 36 37 } 38 39 public class Adventure { 40 41 static void t(CanFight x) { x.fight(); } 42 43 static void u(CanSwim x) { x.swim(); } 44 45 static void v(CanFly x) { x.fly(); } 46 47 static void w(ActionCharacter x) { x.fight(); } 48 49 public static void main(String[] args) { 50 51 Hero i = new Hero(); 52 53 t(i); // Treat it as a CanFight 54 55 u(i); // Treat it as a CanSwim 56 57 v(i); // Treat it as a CanFly 58 59 w(i); // Treat it as an ActionCharacter 60 61 } 62 63 }
说明:
1、 Java 和C++不同,不存在多继承,如果非在实现多继承,那只有通过接口,变向实
现;
2、 从中可以看到,Hero 将具体类 ActionCharacter 同接口 CanFight,CanSwim 以及
CanFly合并起来。 按这种形式合并一个具体类与接口的时候, 具体类必须首先出现,
然后才是接口(否则编译器会报错) 。请注意 fight()的签名在 CanFight 接口与
作业
1:定义一个对象“交通工具” ,并定义接口,说明交通工具可以移动。继承交通工具而产生
汽车、飞机、轮船,并定义类来实现其移动的方法。
2:定义一个类来使用上面的接口






浙公网安备 33010602011771号