抽象类和接口都是用来体现OOP中的抽象概念,都可以用来实现多态。
1. 抽象类
抽象类表达的是“is-a”的关系,是对多个具体类公共部分的抽象,定义了子类是什么(如Bird、Duck本质上都是Animal,即可以将其抽象为Animal类),由于抽象类没有足够的信息,是不完整的,所以必须由具体的子类来继承它并实现抽象方法。
抽象类定义
如果一个类含有抽象方法(abstact修饰的方法),则其为抽象类,在class关键字前使用abstract进行修饰。抽象类是为了继承而存在的,即抽象类不能直接创建实例,必须通过子类继承并实现其抽象方法。抽象类中并不只有抽象方法,还可以有提供实现的具体方法和成员变量。
抽象类特点
- 抽象类不能被实例化,必须通过子类继承;
- 抽象方法必须为public或protected(如果为private,就不能被子类继承了),默认为public;
- abstract不能与final同时修饰抽象类中的class和方法,因为这样就不能被继承和重写了。
- 如果一个类继承抽象类,则子类必须实现父类的抽象方法,如果子类没有实现父类的抽象方法,则必须将子类也定义为抽象类。
2. 接口
接口表达的是“has-a”的关系,是对行为的抽象,并不能表达子类的本质特征(即子类是什么),子类实现了接口表面其具有该接口提供的行为,(如有一个Flyable接口,提供fly()方法,Bird实现该接口时,必须实现fly()方法,表明其具有飞行行为)。因为Java不允许多继承,通过实现接口可以间接实现多继承。
- 接口的所有方法都不能有具体的实现,即所有方法都是抽象方法(JDK8可以有默认方法实现),可以有成员变量,但会被隐式的指定为public static final,而方法会被隐式地指定为public abstract方法且只能是public abstract方法;
- 实现接口的非抽象类必须要实现该接口的所有方法。抽象类可以不用实现。
3. 抽象类和接口区别
抽象类和接口都用来表达面向对象中的抽象概念,都可以作为超类型提供多态的实现。抽象类作为父类对子类中公共的特征进行抽象(所以必须是对具有一组相同概念本质的子类,如都是动物),而接口不用管子类具体是什么,只需要提供行为,对行为进行抽象,如Flyable接口提供飞行行为,Bird和AirPlane都可以实现该接口,并实现其中的飞行方法,但这两个类本质是不同的类。所以Java提供抽象类和接口,一个用来抽象子类是什么(is-a),一个用来抽象子类可以具有哪些行为方法(has-a)。二者可以结合起来使用,实现多个接口。
语法层面
- 抽象类可以拥有任意范围的成员数据,同时也可以拥有自己的非抽象方法,但是接口方式中,它仅能够有静态、不能修改的成员数据(但是我们一般是不会在接口中使用成员数据),同时它所有的方法都必须是抽象的。在某种程度上来说,接口是抽象类的特殊化。
- 一个类只能继承一个抽象类,可以实现多个接口,从某种程度上说,接口允许Java的多继承。
- 接口中不能有静态代码块和静态方法,而抽象类可以有。
设计层面
- 抽象层次不同:抽象类是对类的本质特征进行抽象,接口是对行为进行抽象。抽象类是对整个类整体进行抽象,包括属性、行为,但是接口却是对类局部(行为)进行抽象。
- 跨域不同:抽象类所跨域的是具有相似特点的类,而接口则可以跨不同的类。继承抽象类的子类必须和其父类在概念本质上是相同的,而实现接口的子类可以不存在任何关系,仅仅是实现了接口定义的契约而已。
- 设计层次不同:抽象类是自底向上抽象而来的,接口是自顶向下设计出来的。
举个例子:
门和警报的例子:有一个Door类,具有open()和close()两个行为。我们可以通过抽象类和接口来定义这个概念:
抽象类:
abstract class Door{
abstract void open();
abstract void close();
}
接口:
interface Door {
void open();
void close();
}
现在要给门加一个报警的功能alarm(),如何实现呢,有两种思路:
- 将报警方法放在抽象类里,但是这样一来所有继承于这个抽象类的子类都具备了报警功能,但是并不是所有的门并一定具备报警功能;
- 将这三个功能都放在接口里面,需要用到报警功能的类就需要实现这个接口中的open( )和close( ),也许这个类根本就不具备open( )和close( )这两个功能,比如火灾报警器。
从这里可以看出,open()和close()是所有Door的固有方法,所以可以将其抽象出来并在抽象类中定义,而alarm()方法属于附加行为,所以应该将其单独抽象为一个接口,代表具有报警行为,这样不只门可以实现该接口,其他具有报警功能的都可以实现。
所以open()和close()放在抽象类里,alarm()单独用一个接口来表示:
interface Alram {
void alarm();
}
abstract class Door {
void open();
void close();
}
class AlarmDoor extends Door implements Alarm {
void oepn() {
//....
}
void close() {
//....
}
void alarm() {
//....
}
}
参考: