1. 接口说明

接口和类是一种并列关系,由于类的继承是单继承的,所以一个子类不能具有多个父类的特征,接口就可以用来解决这个问题,接口可以进行多实现!同时,在逻辑上,接口没有子父类一说,而是是否实现一说。可以认为接口定义了很多功能,而当类实现这个接口时,就代表这个类具有这些功能。
image.png


2. 接口定义

使用关键字 interface 来定义接口,interface 和 class 是一种并列关系,定义接口的方式和定义类的方式类似。

  • 在JDK 7以及之前:只能定义全局常量抽象方法。没有可变属性
    • 全局常量:public static final 的
    • 抽象方法:public abstract
  • 在JDK 8 以及之后:除了定义全局常量和抽象方法之外,还可以定义静态方法默认方法

先看 JDK 7:
image.png
上述接口定义了一个全局常量和一个抽象方法,可以看到,public static final 和 public abstract 两处修饰符都成了灰色,是因为接口中默认变量均是全局常量,方法均是抽象方法,因此加了修饰符和不加修饰符是一样的。则上述代码等价于:
image.png
注意,接口中是不能定义可变属性的。
image.png
更重要的,接口不能定义构造器,这也是接口与抽象类最本质的区别。没有构造器,说明接口无法被实例化,也没有子类一说。更一般点,接口不能含有任何实体方法。
image.png


3. 接口实现

在 Java 开发中,接口通过让类去实现(implements)的方式来使用。如果实现类覆盖了接口中的所有抽象方法,则此实现类就可以实例化。

  1. package pkg5;
  2. public interface Flyable {
  3. // public static final
  4. int speed = 7900 ;
  5. // public abstract
  6. void fly() ;
  7. void speedUp() ;
  8. void speedDown() ;
  9. }

image.png
这样一来,类Jet就实现了接口Flyable,可以进行实例化。
同理抽象类,如果接口的抽象方法没有完全实现,在该类必须定义为abstract,否在无法通过编译。不过这样没什么意义,一般而言,就用实体类来实现一个接口的所有方法。
image.png
当一个类实现了某一个接口时,它的对象就可以作为该接口的类型进行传参

  1. package pkg5;
  2. public class InterfaceTest {
  3. public static void main(String[] args) {
  4. Jet J20 = new Jet(100,"歼20");
  5. InterfaceTest i1 = new InterfaceTest();
  6. i1.test(J20);
  7. }
  8. public void test(Flyable f){
  9. f.fly();
  10. }
  11. }

image.png
就这点来说,接口更像是一种规范,代表“只有完成了….才算作…..”或者“只要完成了….就算作…..”。这点在应用举例那里会细说。


4. 接口可以多实现

接口一个非常非常重要的功能,就是类可以一次实现多个接口,也就是多实现性,这样就弥补了子父类单继承的局限性。比如,定义一个接口 Flyable(如上述),再定义一些接口如Attackable(可攻击的),Refitable(可改装的),那么一个类 Jet(喷气机)就可以同时实现上述三个接口,代表它同时具有 可飞行、可攻击、可改装这三样功能。
若想完成接口的多实现,则实体类必须要实现其待实现的所有接口的所有方法。在类声明处,只需要在 implements 关键字后一次写入待实现接口然后用逗号分隔即可。

  1. public class Jet implements Flyable,Attackable,Refitable
  1. 具体实现过程如下所示<br />Flayable接口:
  1. package pkg5;
  2. public interface Flyable {
  3. // public static final
  4. int speed = 7900 ;
  5. // public abstract
  6. void fly() ;
  7. void speedUp() ;
  8. void speedDown() ;
  9. }

Attackable接口:

  1. package pkg5;
  2. public interface Attackable {
  3. // 子弹数量
  4. int bulletNum = 9999 ;
  5. void shot() ; // 射击
  6. void bomb() ; // 轰炸
  7. }

Refitable接口:

  1. package pkg5;
  2. public interface Refitable {
  3. void changeColor() ; // 换色
  4. void upgrade() ; // 升级
  5. }

Jet类来实现上述接口:

  1. package pkg5;
  2. public class Jet implements Flyable,Attackable,Refitable{
  3. @Override
  4. public void shot() {
  5. System.out.println("jetShout");
  6. }
  7. @Override
  8. public void bomb() {
  9. System.out.println("jetBomb");
  10. }
  11. @Override
  12. public void fly() {
  13. System.out.println("jetFly");
  14. }
  15. @Override
  16. public void speedUp() {
  17. System.out.println("jetSpeedUp");
  18. }
  19. @Override
  20. public void speedDown() {
  21. System.out.println("jetSpeedDown");
  22. }
  23. @Override
  24. public void changeColor() {
  25. System.out.println("jetChangeColor");
  26. }
  27. @Override
  28. public void upgrade() {
  29. System.out.println("jetUpgrade");
  30. }
  31. }
  1. 这样一来,Jet就可以实例化了,Jet对象就拥有了三个接口的全部功能。<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/2643809/1626604885736-e49d1853-8ab3-4593-a698-de5786ceaa12.png#clientId=u6d164588-3b6f-4&from=paste&height=266&id=u1c71a675&margin=%5Bobject%20Object%5D&name=image.png&originHeight=402&originWidth=952&originalType=binary&ratio=1&size=55148&status=done&style=none&taskId=u9a0c11c3-cdea-44ed-8f9c-f618e1b92cb&width=629.9942932128906)<br />可以看出,接口的实现与类的继承在结构上非常相似,但逻辑上却大不相同。对于类的继承而言,可以理解为 A是B的什么什么...,而对于接口的实现而言,可以理解为 A能够完成什么什么...。前者是“是不是”的关系,后者是“能不能”的关系,二者相互弥补。<br />既然说了二者是相互弥补的,那么二者就可以同时进行。也即,一个类在定义时能够同时继承一个父类并实现多个接口。<br />Aircraft 类:
  1. package pkg5;
  2. public class Aircraft {
  3. private int size ;
  4. private String name ;
  5. public Aircraft(int size, String name) {
  6. this.size = size;
  7. this.name = name;
  8. }
  9. public int getSize() {
  10. return size;
  11. }
  12. public String getName() {
  13. return name;
  14. }
  15. public void drive(){
  16. }; // 可以驾驶
  17. }

Jet 继承 Aircraft 并实现三个接口:

  1. package pkg5;
  2. public class Jet extends Aircraft implements Flyable,Attackable,Refitable{
  3. // 实现父类的构造器
  4. public Jet(int size, String name) {
  5. super(size, name);
  6. }
  7. // 实现接口,代码同上,略
  8. // ...
  9. }
  1. 这样,父类继承与多接口实现就同时完成了。<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/2643809/1626605650567-99af3e05-37ae-4599-84a6-a8cdeac783b1.png#clientId=u6d164588-3b6f-4&from=paste&height=274&id=ud82efc91&margin=%5Bobject%20Object%5D&name=image.png&originHeight=341&originWidth=693&originalType=binary&ratio=1&size=45092&status=done&style=none&taskId=u97d07e74-ef4a-4583-bf34-394bb535883&width=556.491455078125)<br />可以理解为,J20是一个 Jet 喷气机,它属于 Aircraft 因此拥有 Aircraft 拥有的所有属性和功能,同时,它又实现了 Flyable、Attackable、Refitable 三个接口,具有飞行、攻击、改装三个功能。

5. 接口可以多继承

继承并不是类的专属特性,接口之间也有继承操作,且允许多继承
父接口:

package pkg5;

public interface Move {
    void move() ;  // 可以动
}

子接口:

package pkg5;

public interface Run extends Move{
    void run(); // 可以跑
}

当一个类要实现一个接口时,这个接口的所有父接口的所有方法,这点也很好理解

package pkg5;

public class Person implements Run{
    @Override
    public void move() {
    }

    @Override
    public void run() {

    }
}

6. 应用举例

在 Java 开发中,常用接口作为一种行为规范,或是协议。比如,前文中定义了接口 Flyable 意为能飞行的,这个接口下有方法 fly、speedUp、speedDown。这就代表着我们认为,当一个类实现了fly、speedUp和speedDown,才将其看作是 Flyable 的。也即,只有一个类能做到飞、加速、减速,我们才认为他是符合Flyable规范的。
再举一个更形象的例子 :

  1. USB接口: // 定义USB传输的规范与行为

    public interface USB {
     // 常量,长、宽、高什么的
     double length = 2.0 ;
     double width = 1.5 ;
     double height  = 1.0 ;
    
     // public static
     void start();
     void stop();
    }
    
  2. Flash类: // 模拟U盘 ```java package pkg7;

public class Flash implements USB{ public void start(){ System.out.println(“U盘开始工作”); } public void stop(){ System.out.println(“U盘停止工作”); } }


3. Printer: // 模拟打印机
```java
package pkg7;

public class Printer implements USB{
    public void start(){
        System.out.println("打印机开始工作");
    }
    public void stop(){
        System.out.println("打印机停止工作");
    }
}
  1. Computer: // 模拟电脑, 接收USB传输的数据 ```java package pkg7;

public class Computer { public void usbTransfer(USB usb){ usb.start(); System.out.println(“数据传输中”); usb.stop(); } }


5. USBTest测试 :
```java
package pkg7;

public class USBTest {
    public static void main(String[] args) {
        Computer com = new Computer();
        Flash flash = new Flash();
        Printer printer = new Printer();

        com.usbTransfer(flash);
        System.out.println("****************");
        com.usbTransfer(printer);
    }
}

7. 匿名实现类

创建一个接口的匿名实现类的非匿名对象,和创建抽象类的匿名对象格式基本一样:

USB phone = new USB() {
    @Override
    public void start() {
    }

    @Override
    public void stop() {

    }
};
或者直接用匿名对象也可以.
com.usbTransfer(new USB() {
    @Override
    public void start() {

    }

    @Override
    public void stop() {

    }
});

8. JDK8 中的接口新特性

在 JDK8 中,除了能定义全局常量和抽象方法之外,还可以定义静态方法默认方法。

package pkg7;

public interface USB {

    // 静态方法
    public static void staticMethod(){

    }
    // 默认方法
    public default void defaultMethod(){

    }
}
同理,public可以省略(接口中能定义的所以的结构都默认为 **public**,都可以省略)<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/2643809/1626663418790-bee092db-1b06-435a-9488-7135a38b4f8e.png#clientId=u8842635d-e6ae-4&from=paste&height=206&id=uc03f3f06&margin=%5Bobject%20Object%5D&name=image.png&originHeight=287&originWidth=831&originalType=binary&ratio=1&size=19872&status=done&style=none&taskId=u29238279-3d70-4806-bad7-a5bd6954cfb&width=596.5)<br />静态方法和默认放法都是不需要类来实现的。<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/2643809/1626671934138-91812835-dece-4694-9263-1d9570ef696b.png#clientId=u8842635d-e6ae-4&from=paste&height=106&id=u1c9a7b16&margin=%5Bobject%20Object%5D&name=image.png&originHeight=140&originWidth=658&originalType=binary&ratio=1&size=11615&status=done&style=none&taskId=u54cd6903-a903-44ec-98be-fea9860580c&width=496)

8.1 静态方法

接口的静态方法无法被实现类调用,也无法被实现类的对象调用。
image.png
但是,接口的静态方法能被接口直接调用,这种特性非常像工具类。
image.png
总结】
接口中可以用 static 关键字定义一个静态方法,该方法必须具有方法体。外界能够且只能通过接口直接调用该静态方法,并执行其方法体。我们经常在相互一起使用的类中使用静态方法。可以在标准库中找到像 Collection \ Collections 或者 Path \ Paths 这样成对的接口和类。

8.2 默认方法

【知识点1.】
通过实现类的对象,可以调用接口中的默认方法
image.png
而且只有这一种调用方式,直接通过接口调用或者通过实现类来调用都不行!
image.png
默认方法是可以被实现类重写的。换句话说,除了 静态方法 不能被重写、抽象方法 被称作实现。其余的方法都是能被重写的。

package pkg7;

public class Printer implements USB{
    @Override
    public void defaultMethod() {
        System.out.println("Printer重写了USB的defaultMethod");
    }
}

image.png

【知识点2.】
如果子类(实现类)继承的父类中声明了和实现接口中的默认方法同名同参数的方法,那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参数方法,而不是实现接口中的默认方法。如下例所示:

USB接口:

package pkg7;

public interface USB {

    // 静态方法
    public static void staticMethod(){

    }
    // 默认方法
    public default void defaultMethod(){
        System.out.println("Method in USB");
    }
}

SubClass父类:

package pkg7;

public class SubClass {
    public void defaultMethod(){
        System.out.println("Method in SubClass");
    }
}

Printer继承了SubClass同时又实现了USB,但是没有重写方法:

package pkg7;

public class Printer extends SubClass implements USB{

}

测试:
image.png
说明子类调用的是父类中同名同参的方法,而不是接口中的默认方法。(类优先原则

【知识点3.】
如果实现了类同时实现了多个接口,而这多个接口中定义了同名同参数的默认方法。如果实现类重写了这个方法,那就没事,覆盖了;但如果实现类没有重写该方法,编译会直接报错 ==> 接口冲突
image.png