
1.8 abstract class(抽象类)与interface(接口)的异同
如果一个类中包含抽象方法,那么这个类就是抽象类。在Java语言中,可以通过把类或者类中的某些方法声明为abstract(abstract只能用来修饰类或者方法,不能用来修饰属性)来表示一个类是抽象类。只要包含一个抽象方法的类就必须被声明为抽象类,抽象类可以仅声明方法的存在而不去实现它,被声明为抽象的方法不能包含方法体。在实现时,必须包含相同的或者更低的访问级别(public->protected->private)。抽象类在使用的过程中不能被实例化,但是可以创建一个对象使其指向具体子类的一个实例。抽象类的子类为父类中所有的抽象方法提供具体的实现,否则它们也是抽象类。
接口就是指一个方法的集合,在Java语言中,接口是通过关键字interface来实现的。在Java8之前,接口中既可以定义方法也可以定义变量,其中变量必须是public、static、final的,而方法必须是public、abstract的。由于这些修饰符都是默认的,所以在Java8之前,下面的写法都是等价的。

从Java8开始,通过使用关键字default可以给接口中的方法添加默认实现,此外,接口中还可以定义静态方法,示例代码如下所示:

那么,为什么要引入接口中方法的默认实现呢?
其实,这样做的最重要的一个目的就是实现接口升级。在原有的设计中,如果想要升级接口,例如给接口中添加一个新的方法,那么会导致所有实现这个接口的类都需要被修改,这给Java语言已有的一些框架进行升级带来了很大的麻烦。如果接口能支持默认方法的实现,那么可以给这些类库的升级带来许多便利。例如,为了支持Lambda表达式,Collection中引入了foreach方法,可以通过这个语法增加默认的实现,从而降低了对这个接口进行升级的代价,不需要对所有实现这个接口的类进行修改。
在Java8之前,实现接口的非抽象类必须要实现接口中的方法,在Java8中引入接口中方法的默认实现后,实现接口的类也可以不实现接口中的方法,例如:

接口中的静态方法只能通过接口名来调用,不可以通过实现类的类名或者实现类的对象来调用。而default方法则只能通过接口实现类的对象来调用。示例代码如下:

在Java中由于不支持多重继承,也就是说一个类只能继承一个父类,不能同时继承多个父类,但是一个类可以实现多个接口。因此经常通过实现多个接口的方式来实现多重继承的目的。那么如果多个接口中存在同名的static和default方法会有什么样的问题呢?静态方法并不会导致歧义的出现,因为静态方法只能通过接口名来调用;对于default方法来说,在这种情况下,这个类必须要重写接口中的这个方法,否则无法确定到底使用哪个接口中默认的实现。
从上面的介绍可以看出接口与抽象类有很多相似的地方,那么它们有哪些不同点呢?
(1)抽象类
1)抽象类只能被继承(用extends)。并且一个类只能继承一个抽象类。
2)抽象类强调所属关系,其设计理念为is-a关系。
3)抽象类更倾向于充当公共类的角色,不适用于日后重新对里面的代码进行修改。
4)除了抽象方法之外,抽象类还可以包含具体数据和具体方法(可以有方法的实现)。
5)抽象类不能被实例化,如果子类实现了所有的抽象方法,那么子类就可以被实例化了。如果子类只实现了部分抽象方法,那么子类还是抽象类,不能被实例化。
(2)接口
1)接口需要实现(用implements),一个类可以实现多个接口,因此使用接口可以间接地实现多重继承的目的。
2)接口强调特定功能的实现,其设计理念是has-a关系。
3)接口被运用于实现比较常用的功能,便于日后维护或者添加删除方法。
4)接口不是类,而是对类的一组需求描述,这些类要遵从接口描述的统一格式进行定义。
5)接口中的所有方法都是public的,因此,在实现接口的类中,必须把方法声明成public,因为类中默认的访问属性是包可见的,而不是public,这就相当于在子类中降低了方法的可见性,会导致编译错误。
总之,接口是一种特殊形式的抽象类,使用接口完全有可能实现与抽象类相同的操作,但一般而言,抽象类多用于在同类事物中有无法具体描述的方法的场景,所以当子类和父类之间存在有逻辑上的层次结构时,推荐使用抽象类,而接口多用于不同类之间,定义不同类之间的通信规则,所以当希望支持差别较大的两个或者更多对象之间的特定交互行为时,应该使用接口。
此外,接口可以继承接口,抽象类可以实现接口,抽象类也可以继承具体类。抽象类也可以有静态的main方法。