从01开始 从01开始
首页
  • 计算机科学导论
  • 数字电路
  • 计算机组成原理

    • 计算机组成原理-北大网课
  • 操作系统
  • Linux
  • Docker
  • 计算机网络
  • 计算机常识
  • Git
  • JavaSE
  • Java高级
  • JavaEE

    • Ant
    • Maven
    • Log4j
    • Junit
    • JDBC
    • XML-JSON
  • JavaWeb

    • 服务器软件
    • Servlet
  • Spring
  • 主流框架

    • Redis
    • Mybatis
    • Lucene
    • Elasticsearch
    • RabbitMQ
    • MyCat
    • Lombok
  • SpringMVC
  • SpringBoot
  • 学习网课的心得
  • 输入法
  • 节假日TodoList
  • 其他
  • 关于本站
  • 网站日记
  • 友人帐
  • 如何搭建一个博客
GitHub (opens new window)

peterjxl

人生如逆旅,我亦是行人
首页
  • 计算机科学导论
  • 数字电路
  • 计算机组成原理

    • 计算机组成原理-北大网课
  • 操作系统
  • Linux
  • Docker
  • 计算机网络
  • 计算机常识
  • Git
  • JavaSE
  • Java高级
  • JavaEE

    • Ant
    • Maven
    • Log4j
    • Junit
    • JDBC
    • XML-JSON
  • JavaWeb

    • 服务器软件
    • Servlet
  • Spring
  • 主流框架

    • Redis
    • Mybatis
    • Lucene
    • Elasticsearch
    • RabbitMQ
    • MyCat
    • Lombok
  • SpringMVC
  • SpringBoot
  • 学习网课的心得
  • 输入法
  • 节假日TodoList
  • 其他
  • 关于本站
  • 网站日记
  • 友人帐
  • 如何搭建一个博客
GitHub (opens new window)
  • JavaSE

    • 我的Java学习路线
    • 安装Java
    • Java数据类型

    • Java多版本配置
    • 面向对象

      • 枚举类
      • static关键字
      • classpath:JVM查找类的机制
      • 包:Java组织类的方式
      • jar包
      • class版本
      • 抽象类
      • 接口
        • 定义接口
        • 实现接口
        • 接口继承
        • 继承关系
        • default方法
        • 抽象类和接口的区别
        • 什么时候使用接口?
        • 参考
      • 访问性修饰符
      • 非访问性修饰符
      • 内部类
    • Java核心类

    • IO

    • Java与时间

    • 异常处理

    • 哈希和加密算法

    • Java8新特性

    • 网络编程

    • Java
  • JavaSenior

  • JavaEE

  • JavaWeb

  • Spring

  • 主流框架

  • SpringMVC

  • SpringBoot

  • Java并发

  • Java源码

  • JVM

  • 韩顺平

  • Java
  • Java
  • JavaSE
  • 面向对象
2023-02-08
目录

接口

# 18.接口

一个类,如果实现了一个接口,那么它必须实现接口中定义的所有方法。一个类,继承一个抽象类,那么它必须重写抽象类中定义的所有抽象方法,可以不用重写抽象类中定义的普通方法。

# 定义接口

在抽象类中,抽象方法本质上是定义接口规范:即规定高层类的接口,从而保证所有子类都有相同的接口实现,这样,多态就能发挥出威力。

如果一个抽象类没有字段,所有方法全部都是抽象方法:

abstract class Person {
    public abstract void run();
    public abstract String getName();
}
1
2
3
4

就可以把该抽象类改写为接口:interface​。

‍

在Java中,使用interface​可以声明一个接口:

interface Person {
    void run();
    String getName();
}
1
2
3
4

所谓interface​,就是比抽象类还要抽象的纯抽象接口,因为它连字段都不能有。因为接口定义的所有方法默认都是public abstract​的,所以这两个修饰符不需要写出来(写不写效果都一样)。

注意:

  1. interface可以看成是一种特殊的类,源代码文件也是以.java​结尾,编译时也是一个独立的字节码文件。

  2. 所有类共享一个根类Object,但是接口没有根

  3. 和抽象类相似,不能用new操作符创建interface的实例。

  4. 虽然Interface不能有字段,但是可以有常量。

  5. 接口中所有数据域都是public static final​修饰的,所有方法都是public abstract​修饰的,因此可以忽略,如下两个接口定义是等价的:

    public interface T {
        public static final int K =1;
        public abstract void p();
    }
    
    public interface T {
        int K = 1;
        void p();
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

‍

‍

# 实现接口

当一个具体的class​去实现一个interface​时,需要使用implements​关键字。

class Student implements Person {
    private String name;

    public Student(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        System.out.println(this.name + " run");
    }

    @Override
    public String getName() {
        return this.name;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

注意,必须得实现接口中的方法,否则会报错的,例如当我们没有实现getName​方法:

$ javac Student.java
Student.java:1: 错误: Student不是抽象的, 并且未覆盖Person中的抽象方法getName()
public class Student implements Person {
       ^
1 个错误
1
2
3
4
5

一个类实现一个接口,其实也是相当于继承了这个接口。一个接口类型的变量可以引用任何实现该接口的类的实例,这样我们就可以面向抽象编程了(参考上一节的抽象类)

‍

在Java中,一个类只能继承自另一个类,不能从多个类继承。但是,一个类可以实现多个interface​:

class Student implements Person, Hello { // 实现了两个interface
    ...
}
1
2
3

‍

‍

# 接口继承

一个interface​可以继承自另一个interface​。interface​继承自interface​使用extends​,它相当于扩展了接口的方法。例如:

interface Person {
    void run();
    String getName();
}

public interface Hello extends Person {
  
}
1
2
3
4
5
6
7
8

此时,Person​接口继承自Hello​接口,因此,Person​接口现在实际上有3个抽象方法签名,其中一个来自继承的Hello​接口。

注意,接口可以继承其他接口,但不能继承其他类。

综上,一个类只能继承一个父类,但可以同时实现多个接口。

‍

# 继承关系

合理设计interface​​和abstract class​​的继承关系,可以充分复用代码。一般来说,公共逻辑适合放在abstract class​​中,具体逻辑放到各个子类,而接口层次代表抽象程度。

‍

可以参考Java的集合类定义的一组接口、抽象类以及具体子类的继承关系:

┌───────────────┐
│   Iterable    │
└───────────────┘
        ▲                ┌───────────────────┐
        │                │      Object       │
┌───────────────┐        └───────────────────┘
│  Collection   │                  ▲
└───────────────┘                  │
        ▲     ▲          ┌───────────────────┐
        │     └──────────│AbstractCollection │
┌───────────────┐        └───────────────────┘
│     List      │                  ▲
└───────────────┘                  │
              ▲          ┌───────────────────┐
              └──────────│   AbstractList    │
                         └───────────────────┘
                                ▲     ▲
                                │     │
                                │     │
                     ┌────────────┐ ┌────────────┐
                     │ ArrayList  │ │ LinkedList │
                     └────────────┘ └────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

‍

在使用的时候,实例化的对象永远只能是某个具体的子类,但总是通过接口去引用它,因为接口比抽象类更抽象:

List list = new ArrayList(); // 用List接口引用具体子类的实例
Collection coll = list; // 向上转型为Collection接口
Iterable it = coll; // 向上转型为Iterable接口
1
2
3

‍

‍

# default方法

在接口中,可以定义default​方法。例如,定义一个run​方法:

public interface InterfaceWithDefault {
  String getName();
  default void run(){
    System.out.println(getName() + " run ");
  };
}
1
2
3
4
5
6

实现类可以不必覆写default​方法。default​方法的目的是,当我们需要给接口新增一个方法时,会涉及到修改全部子类。如果新增的是default​方法,那么子类就不必全部修改,只需要在需要覆写的地方去覆写新增方法。

‍

‍

然后实现该接口:

public class TestDefault {
  public static void main(String[] args) {
    InterfaceWithDefault stu = new Student("Peter JXL");  
    stu.run();
  }
}

class Student implements InterfaceWithDefault{
  private String name;

  public Student(String name){
    this.name = name;
  }

  public String getName(){
    return this.name;
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

输出结果:

Peter JXL run
1

‍

# 抽象类和接口的区别

抽象类和接口的对比如下:

abstract class interface
继承 只能extends一个class 可以implements多个interface
字段 可以定义实例字段 不能定义实例字段
抽象方法 可以定义抽象方法 可以定义抽象方法
非抽象方法 可以定义非抽象方法 可以定义default方法

‍

还有一点,default​方法和抽象类的普通方法是有所不同的。因为interface​没有字段,default​方法无法访问字段,而抽象类的普通方法可以访问实例字段。

‍

‍

# 什么时候使用接口?

一般情况下,我们会为类名取一个名词,为接口名取一个形容词或名词。

抽象类和接口都是用来明确多个对象的共同特征的。那么该如何确定在什么情况下应该使用接口,什么情况下应该使用类呢?

一般来说,清晰描述父.子关系的强的“是一种” 的关系( strong is-a relationship) 应该用类建模。例如,因为公历是一种日历,所以,类 java.util.GregorianCalendar 和 java.util.Calendar 是用类继承建模的。

弱的 “是一种” 的关系(weak is-a relationship) 也称为类属关系(is-kind-ofrelationship), 它表明对象拥有某种属性,可以用接口来建模。

例如,所有的字符串都是可比较的,而且数字也是可以比较的,因此,Java定义了一个 Comparable 接口,String 类和数值类实现这个接口。

但是如果我们用抽象类的方式来实现,让数值类和字符串都继承某个抽象类,完全是没必要的,因为数字有数字的比较方法,字符串有字符串的比较方法,学生(自己定义的类)也有自己的比较方法,所以子类是必须要覆写父类的方法的,并且让这几个完全没有关联的类继承一个抽象类,从逻辑上不太适合。

统一标准的目的,是大家都知道这个是做什么的,但是具体不用知道具体怎么做。

‍

‍

# 参考

接口 - 廖雪峰的官方网站 (opens new window)

Java 中的接口有什么作用? - 知乎 (opens new window)

Java 中的接口有什么作用? - Dion的回答 - 知乎

在GitHub上编辑此页 (opens new window)
上次更新: 2023/2/9 11:28:11
抽象类
访问性修饰符

← 抽象类 访问性修饰符→

Theme by Vdoing | Copyright © 2022-2023 粤ICP备2022067627号-1 粤公网安备 44011302003646号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式