抽象类和接口的异同
1. 抽象类
1.1 概念
含有抽象方法的类,在Java中使用abstract修饰
1.2 包含内容
具有普通的类具有的所有内容,除此之外还具有抽象方法,说白了就是具有抽象方法的普通类(但是通过abstract修饰) ,具体可以包含的内容为:
-
(任意修饰符的)抽象方法
-
(任意修饰符的)的具体、静态方法
-
(任意修饰符的)普通、静态成员变量
-
(任意修饰符的)常量
-
(任意修饰符的)含有/不含 方法体构造方法
例子(试过了没有发生报错):
public abstract class AAbstract {
/**
* 抽象方法可以具有以下内容(普通类可以有的抽象类都有,且抽象类还能具有抽象方法):
*/
//1.(任意修饰符的普通)成员变量
public int i;
//2.(任意修饰符的静态)成员变量
public static final int j = 0;
//3.含有/不含方法体的构造方法
private AAbstract(){
this.i = 1;
}
//4.(任意修饰符的)抽象方法
public abstract void a();
//5.(任意修饰符的)具体方法
public void b(){
}
}
1.3 继承
1.3.1 子类与父类
- 在Java中,子类 可以通过关键字extends 抽象类,来实现对抽象类的继承 ,在继承这一层关系中,才有子类 与父类 的概念
1.3.2 单继承
-
一般来说,一个类(包括抽象类)只能实现单继承
-
特殊情况,接口可以实现多继承 ,但是 继承的只能也是接口
1.3.3 什么类可以被继承
- 所有的类(接口不行)都可以被继承,但是 声明了final的类不能被继承
1.3.4 重写父类方法
-
子类如果继承了父类,必须重写父类的所有抽象方法 (普通方法可选,不强制要求);但是如果子类为抽象类,则不强制要求重写父类的抽象方法
-
子类不能重写父类用final、private和static修饰的方法 ,其中:
-
父类的final方法 只能被子类继承,不能被重写,但是可以被子类的实例使用
-
父类的private方法 只能被子类继承、不能被重写,也不能被子类内部和子类实例使用使用,相当于子类完全用不了也看不见。这是因为private修饰符只允许该方法在父类内部使用。
-
父类的static方法 可以被子类继承、子类的内部和子类实例使用,但不能被重写 ,因为static方法属于父类,而不属于子类的单个实例(子类的内容就是实例的内容)
-
2. 接口
2.1 概念及包含内容
接口是为了方便对外提供API接口为产生的(因此所有方法均为public),只包含 静态常量 和抽象方法(不能包含final方法)。在JDK 8之后,接口的内容新增了static方法和default方法,在JDK 9之后又允许含有private方法,以下是具体说明:
-
注意,此处的default方法 并不是只方法权限修饰符,而是指 默认实现。与抽象方法不同,接口并不要求子类一定要实现default方法,如果为实现则子类将继承default方法的已有默认内容。default方法的诞生初衷是:开发人员如果想在已有接口新增一些新的方法来实现新的功能,则主要会遇到两个问题:
-
原先的接口只允许存在抽象方法,抽象方法不允许有方法体,故不能实现功能。
-
如果原有接口新增了方法,则大量相关子类也必须实现这些方法,那么这样造成的变动是很大的。
-
-
private方法是为了方便接口内能够调用内部的方法。
2.2 实现关系
- Java中,实现类 可以用关键字implements 来 实现接口
2.3 多实现
- Java中 一个类(包括抽象类)支持多实现(多个接口之前用逗号分割开),但是 一个接口不能实现其他接口(单实现也不行),实例如下:
public abstract class AAbstract implements AInterface,BInterface {
}
2.4 实现接口方法
- 类似的,实现类必须实现接口的抽象方法,其他的static方法 、private方法 和default方法 不强制要求实现类来实现。
3. 接口和抽象类的区别
3.1 抽象类比接口多了构造方法
抽象类多了构造方法主要是为了能够规范子类的行为,如父类Animal类为了能够要求子类Dog构造时也能初始化name属性,则可以这么做:
在Animal类当中:
public abstract class Animal {
protected String name;
protected Animal(String name) {
this.name = name;
}
}
则子类Dog的构造方法必须为:
public class Dog extends Animal{
protected Dog(String name) {
super(name);
}
}
如果子类Dog的构造方法为:
public class Dog extends Animal{
protected Dog() {
}
}
则会报错:There is no default constructor available in ‘interfaceAndAbstract.Animal’
若父类Animal只有上述有参构造方法而子类Dog没有实现对应的有参构造方法,则也会报出上述错误。
子类Dog要想也保留空参构造方法,则父类也需要有空参构造方法,总之父类有哪种构造方法,则子类也需要有对应的构造方法,从而实现父类对子类构造方法的约束行为。
3.2 接口的方法默认均为public的
-
Java的接口的初衷是为了向外部提供接口,即对外部类公开,因此接口所有的方法 无论是否显示声明,均为public,已确保实现类能够被外部统一调用。
-
Java 8之后接口支持static方法和default方法,默认也均为public的。注意:此时default表示的并不是访问权限,指的是默认方法,此外还有这几个细节需要注意:
-
Java 9 之后接口支持private方法,因此可以在static之前加上private或者public,因为static并不是访问权限符,并不会冲突:
//这两种方式都不会报错(但是没必要再写一个public,因为default方法默认就是public的,再加一个public就显得冗余了) public static void b(){ System.out.println("这是接口A的静态方法"); }private static void b(){ System.out.println(“这是接口A的静态方法”); }
-
-
Java 9之后接口支持private方法,由于private和public都表示访问权限修饰符,因此不能混用,因此以下使用方式是错的:
public private void d(){ System.out.println("这是接口A的私有方法"); } -
因此,该说法是错误 的:Java 9之后,接口的所有方法都是public 。因为Java 9之后的private方法,也允许接口通过私有接口方法向外隐藏内部的实现细节。
3.3 接口和抽象类的选择
-
当需要 子类继承父类的成员变量(属性) ,或者需要 父类约束子类的构造方法(控制子类的实例化) 时,就使用抽象类+继承
-
除此之外的其他情况,均用接口,因为接口支持多实现。
