接口 & 抽象类

接口和抽象类的区别
抽象类的设计目的,是代码复用;接口的设计目的,是对类的行为进行约束。
- 当需要表示is-a的关系,并且需要代码复用时用抽象类
- 当需要表示has-a的关系,可以使用接口
比如狗具有睡觉和吃饭方法,我们可以使用接口定义:
1 | public interface Dog { |
但是需要让每个派生类都实现一次sleep方法。此时为了完成对sleep方法的复用,可以选择采用抽象类来定义:
1 | public abstract class Dog { |
但是如果训练让狗拥有一项技能——握手,怎么添加这个行为?将握手方法写入抽象类中,但是这会导致所有的狗都能够握手。
握手是训练出来的,是对狗的一种扩展。所以这时候我们需要单独定义握手这种行为。这种行为是否可以采用抽象类来定义呢?
1 | public abstract Handshake{ |
如果采用抽象类定义握手,那我们现在需要创建一类能够握手的狗怎么办?
1 | public class HandShakeDog extends Dog //,Handshake |
此时,就需要使用接口来定义握手行为:
1 | public interface Handshake{ |
不是所有的狗都会握手,也不止狗会握手。我们可以同样训练猫,让猫也具备握手的技能,那么猫Cat类,同样能够实现此接口,这就是”has-a”的关系。
接口(Interface)
- 成员限制:接口中可以定义方法、常量和默认方法(Java 8+),但不能包含字段(字段默认为静态和常量)或实现。
- 多继承:接口支持多继承,一个类可以实现多个接口。
- 实现:接口中的所有方法都是抽象的,需要实现类提供具体的实现。
- 目的:接口用于定义类之间的契约或协议,以确保类遵循某种行为标准,促进代码的可复用性和可替换性。
1 | interface Shape { |
抽象类(Abstract Class)
- 成员限制:抽象类可以定义方法、字段、构造函数和抽象方法。抽象方法是没有实现的方法,必须由子类提供具体实现。
- 单继承:抽象类只支持单继承,一个类只能继承一个抽象类。
- 实现:抽象类可以包含已经实现的方法,也可以包含抽象方法,需要子类提供具体实现。
- 目的:抽象类用于定义一个类的通用特性,以及提供一些默认实现,同时允许子类继承和重写这些特性。
1 | abstract class Shape { |
区别总结
- 设计目的:接口用于定义行为契约,抽象类用于定义类的通用特性和行为。
- 成员限制:接口只能包含抽象方法和默认方法,而抽象类可以包含字段、构造函数、抽象方法和已实现的方法。
- 继承关系:接口支持多继承,抽象类只支持单继承。
- 使用场景:如果需要多继承、定义行为契约或者希望实现多态性,通常使用接口。如果希望提供类的通用特性,同时为子类提供默认实现,通常使用抽象类。
语法层面上
- 抽象类可以提供成员方法的实现细节,而接口中只能存在 public abstract 方法;
- 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的;
- 接口中不能含有静态代码块,而抽象类可以有静态代码块;
- 一个类只能继承一个抽象类,而一个类却可以实现多个接口。
设计层面上
- 抽象类是对一种事物的抽象,即对类抽象,继承抽象类的子类和抽象类本身是一种 is-a 的关系。而接口是对行为的抽象。抽象类是对整个类整体进行抽象,包括属性、行为,但是接口却是对类局部(行为)进行抽象。
- 举个简单的例子,飞机和鸟是不同类的事物,但是它们都有一个共性,就是都会飞。那么在设计的时候,可以将飞机设计为一个类 Airplane,将鸟设计为一个类 Bird,但是不能将 飞行 这个特性也设计为类,因此它只是一个行为特性,并不是对一类事物的抽象描述。
- 此时可以将 飞行 设计为一个接口 Fly,包含方法 fly(),然后 Airplane 和 Bird 分别根据自己的需要实现 Fly 这个接口。然后至于有不同种类的飞机,比如战斗机、民用飞机等直接继承 Airplane 即可,对于鸟也是类似的,不同种类的鸟直接继承 Bird 类即可。从这里可以看出,继承是一个 “是不是”的关系,而 接口 实现则是 “有没有”的关系。如果一个类继承了某个抽象类,则子类必定是抽象类的种类,而接口实现则是有没有、具备不具备的关系,比如鸟是否能飞(或者是否具备飞行这个特点),能飞行则可以实现这个接口,不能飞行就不实现这个接口。
- 抽象类是对一种事物的抽象,即对类抽象,继承抽象类的子类和抽象类本身是一种 is-a 的关系。而接口是对行为的抽象。抽象类是对整个类整体进行抽象,包括属性、行为,但是接口却是对类局部(行为)进行抽象。
接口是对类的某种行为的一种抽象,接口和类之间并没有很强的关联关系,举个例子来说,所有的类都可以实现 Serializable 接口,从而具有序列化的功能,但不能说所有的类和 Serializable 之间是 is-a 的关系。
抽象类作为很多子类的父类,它是一种模板式设计。而接口是一种行为规范,它是一种辐射式设计。
- 什么是模板式设计?最简单例子,大家都用过 ppt 里面的模板,如果用模板 A 设计了 ppt B 和 ppt C,ppt B 和 ppt C 公共的部分就是模板 A 了,如果它们的公共部分需要改动,则只需要改动模板 A 就可以了,不需要重新对 ppt B 和 ppt C 进行改动。
- 而辐射式设计,比如某个电梯都装了某种报警器,一旦要更新报警器,就必须全部更新。也就是说对于抽象类,如果需要添加新的方法,可以直接在抽象类中添加具体的实现,子类可以不进行变更;而对于接口则不行,如果接口进行了变更,则所有实现这个接口的类都必须进行相应的改动。