Java(十三)接口
接口
首先我们来想想为什么需要接口这个东西呢?假设现在有一个业务是:在奥运会上,我们需要定义出一个中国的游泳运动员,假设是这样的
class Chinese { }很显然,作为中国人,他首先是要有人的属性和行为,那么他就要继承Person类
class Chinese extends Person{ } class Person { }事实上,作为游泳运动员,他还要继承一个游泳运动员的抽象类,以获得游泳的行为。但是很显然,Java只会允许单继承的出现,这时候我们就可以使用接口了,因为一个类可以在继承一个类的同时,实现无数个接口。
接口代表着一种规范,它定义了一组抽象方法和常量。用来描述一些实现这个接口的类应该具有哪些行为和属性。接口和类一样,也是一种引用数据类型。
定义方法:
[修饰符列表] interface 接口名{ }示例:
// 1. 定义一个“游泳”接口(技能证书) // 注意:不用 class,用 interface 关键字 interface Swimmable { // 接口里面的方法,默认全是 public abstract(公开且抽象的) // 你甚至不需要写这两个关键字,直接写返回类型和方法名就行 void swim(); } // 2. 定义一个“驾驶”接口 interface Driveable { void drive(); } // -------------------------------------------------- // 3. 见证奇迹的时刻:一个中国人,既是人,又会游泳,又会开车! // 语法:先 extends (继承父类),再 implements (实现接口) class SuperChinese extends Person implements Swimmable, Driveable { // ① 必须还“亲爹”的债 @Override public void greet() { System.out.println("你好!我是全能超人!"); } // ② 必须还“游泳证书”的债 @Override public void swim() { System.out.println("我会用自由泳横渡长江!"); } // ③ 必须还“驾驶证书”的债 @Override public void drive() { System.out.println("我会开小汽车!"); } }接口也有对应需要遵守的规则:
- 绝对抽象:接口里的方法只能是没有大括号的抽象方法(默认自动加
public abstract)。你不能在接口里写具体的逻辑。 - 绝对常量:接口里不能有普通的变量!里面所有的属性,都会被 Java 默认当成
public static final(公开的静态常量)。比如你写int MAX_SPEED = 100;,它其实是个改不了的常量。 - 不能被实例化:和抽象类一样,你永远不能
new Swimmable()。技能证书本身不能变成实体,只有考取了证书的人(类)才能被实例化。 - 类与类之间不能多继承,但是接口和接口之间可以多继承。
- 类和接口的关系我们叫做实现(这里的实现也可以等同看做继承)。使用implements关键字进行接口的实现。一个非抽象的类实现接口必须将接口中所有的抽象方法全部实现。语法是:class 类 implements 接口A,接口B{}
- 注意:Java8之后,接口中允许出现默认方法和静态方法(JDK8新特性)
interface SmartPhone { // 传统的抽象方法(子类必须实现) void call(); // 默认方法(带有具体的代码逻辑) default void takePhoto() { System.out.println("使用默认的 1200 万像素摄像头拍照。"); } } interface Calculator { // 抽象方法 int calculate(int a, int b); // 静态方法(工具方法) static void printHelp() { System.out.println("这是一个计算器接口,用于定义计算规则。"); } }- JDK9之后允许接口中定义私有的实例方法(为默认方法服务的)和私有的静态方法(为静态方法服务的)。
- 所有的接口隐式的继承Object。因此接口也可以调用Object类的相关方法。
还有一个要注意的,那就是我们之前明明已经有了抽象类了,为什么还需要去专门学一个接口呢?明明抽象类也可以定义抽象方法啊!
首先就是我们刚刚说的,类与类之间只有单继承,而接口则可以多继承,且当一个类具体实现时还可以同时实现多个接口;再一个两者的侧重点也不一样,抽象类主要是告诉你一个类的本质是什么,比如上面的游泳抽象类就是在告诉你你需要的是一个会游泳的人,而接口则是告诉你能做什么。一个是“是什么”,一个是“会什么”。
再一个,抽象类的使用场景多是公共代码的提取,比如说一组类有共同的属性和行为,但行为的细节却有所不同,需要子类自己去具体实现。而接口则用于一部分类需要这个方法,但另一部分却不需要使用。主要是为了给需要的去提供扩展。
最后就是二者语法的差异,抽象类是必须要有构造器,虽然二者都不能new出实例对象,但抽象类的子类需要super()调用父类的构造器去完成实例化。且抽象类可以有成员变量和普通方法,而接口则只有抽象方法和静态变量(Java8+之后对接口引入了默认方法,原因是:引入默认方式是为了解决接口演变问题:接口可以定义抽象方法,但是不能实现这些方法。所有实现接口的类都必须实现这些抽象方法。这会导致接口升级的问题:当我们向接口添加或删除一个抽象方法时,这会破坏该接口的所有实现,并且所有该接口的用户都必须修改其代码才能适应更改。这就是所谓的"接口演变"问题。)
