接口

抽象类和接口的区别

  • 抽象类可以有方法体的方法,但接口没有(Java 8 以前)。

  • 接口中的成员变量隐式为 static final,但抽象类不是的。

  • 一个类可以实现多个接口,但只能继承一个抽象类。

// 隐式的abstract
interface Coach {
	// 隐式的public
	void defend();
	void attack();
}
  • 接口是隐式抽象的,所以声明时没有必要使用 abstract 关键字;

  • 接口的每个方法都是隐式抽象的,所以同样不需要使用 abstract 关键字;

  • 接口中的方法都是隐式 public 的。

抽象类是对一种事物的抽象,即对类抽象,继承抽象类的子类和抽象类本身是一种 is-a 的关系。而接口是对行为的抽象。抽象类是对整个类整体进行抽象,包括属性、行为,但是接口却是对类局部(行为)进行抽象。

举个简单的例子,飞机和鸟是不同类的事物,但是它们都有一个共性,就是都会飞。那么在设计的时候,可以将飞机设计为一个类 Airplane,将鸟设计为一个类 Bird,但是不能将 飞行 这个特性也设计为类,因此它只是一个行为特性,并不是对一类事物的抽象描述。

此时可以将 飞行 设计为一个接口 Fly,包含方法 fly(),然后 Airplane 和 Bird 分别根据自己的需要实现 Fly 这个接口。然后至于有不同种类的飞机,比如战斗机、民用飞机等直接继承 Airplane 即可,对于鸟也是类似的,不同种类的鸟直接继承 Bird 类即可。从这里可以看出,继承是一个 "是不是"的关系,而 接口 实现则是 "有没有"的关系。如果一个类继承了某个抽象类,则子类必定是抽象类的种类,而接口实现则是有没有、具备不具备的关系,比如鸟是否能飞(或者是否具备飞行这个特点),能飞行则可以实现这个接口,不能飞行就不实现这个接口。

接口是对类的某种行为的一种抽象,接口和类之间并没有很强的关联关系,举个例子来说,所有的类都可以实现 Serializable 接口,从而具有序列化的功能,但不能说所有的类和 Serializable 之间是 is-a 的关系。

接口定义

注意事项

  • 接口中定义的变量会在编译的时候自动加上 public static final 修饰符

  • 没有使用 privatedefault 或者 static 关键字修饰的方法是隐式抽象的,在编译的时候会自动加上 public abstract 修饰符。也就是说上例中的 getElectricityUse() 其实是一个抽象方法,没有方法体——这是定义接口的本意。

  • 从 Java 8 开始,接口中允许有静态方法

  • 接口中允许定义 default 方法也是从 Java 8 开始的,比如说上例中的 printDescription() 方法,它始终由一个代码块组成,为实现该接口而不覆盖该方法的类提供默认实现。

  • 接口不允许直接实例化,需要定义一个类去实现接口,也因此接口不能是 final

  • 接口可以是空的。最典型的例子就是 Serializable 接口,在 java.io 包下。

  • 接口的抽象方法不能是 private、protected 或者 final

接口的作用

使某些实现类具有我们想要的功能,比如说,实现了 Cloneable 接口的类具有拷贝的功能,实现了 Comparable 或者 Comparator 的类具有比较功能。

Cloneable 和 Serializable 一样,都属于标记型接口,它们内部都是空的。实现了 Cloneable 接口的类可以使用 Object.clone() 方法,否则会抛出 CloneNotSupportedException。

Java 原则上只支持单一继承,但通过接口可以实现多重继承的目的

实现多态。多态可以通过继承(extends)的关系实现,也可以通过接口的形式实现父类引用指向子类对象

接口的三种模式

在使用接口的时候,经常会用到三种模式,分别是策略模式、适配器模式和工厂模式。

策略模式

策略模式的思想是,针对一组算法,将每一种算法封装到具有共同接口的实现类中,接口的设计者可以在不影响调用者的情况下对算法做出改变。

Demo.defend() 方法可以接受不同风格的 Coach,并根据所传递的参数对象的不同而产生不同的行为,这被称为“策略模式”

适配器模式

适配器模式的思想是,针对调用者的需求对原有的接口进行转接。生活当中最常见的适配器就是HDMI线,可以同时发送音频和视频信号。适配器模式的示例如下:

Coach 接口中定义了两个方法(defend()attack()),如果类直接实现该接口的话,就需要对两个方法进行实现。

如果我们只需要对其中一个方法进行实现的话,就可以使用一个抽象类作为中间件,即适配器(AdapterCoach),用这个抽象类实现接口,并对抽象类中的方法置空(方法体只有一对花括号),这时候,新类就可以绕过接口,继承抽象类,我们就可以只对需要的方法进行覆盖,而不是接口中的所有方法。

工厂模式

什么工厂生产什么,比如说宝马工厂生产宝马,奔驰工厂生产奔驰,A 级学院毕业 A 级教练,C 级学院毕业 C 级教练。示例如下:

最后更新于