Java 接口的演变

JDK 7 之前

接口只能包含:

  • public abstract 方法

  • public static fianl 常量

不能有方法实现实例字段

复制代码
interface A {
    int NUM = 10;
    void f(); // abstract
}

接口的主要作用是抽象行为实现多继承

接口中的字段只能是 public static final,我们在接口中写任何接口字段,Java 编译器会自动将修饰符补全。

接口字段是:

  • 共有的,所有实现类都可以使用

  • 静态的,属于接口不属于任何实现类

  • final 修饰,防止被修改

只有这样才能满足接口的作用:定义行为契约

JDK 8

到 JDK 8 时,Java 引入了:

  • 默认方法,方法可以带有实现类。

    复制代码
    default void print() {
        System.out.println("hello");
    }
  • 静态方法,可以充当静态工具类

    复制代码
    static void say() {
        System.out.println("Hi");
    }

Java 8 中的 default 方法是一个兼容性设计。一般来说,实现接口的类需要将接口中声明的方法全部重写,如果这时给接口中添加了新方法的话,下面的所有旧实现类需要该方法重写。这在大系统中是灾难。但是新增 default 之后,在接口中新定义一个 default 方法,旧类不需要修改也能继续使用。

JDK 9

这时 Java 引入了,私有方法,接口可以写私有方法,用于 default 方法之间复用。

复制代码
private void helper() {
    System.out.println("help");
}

我们可以看到,开篇接口中的方法并没有写修饰符,这是因为没有任何修饰符的接口方法,编译器会自动加上 public abstract。如果我们要使用默认方法、私有方法和静态方法等等,都需要手动加上相应的关键字 defaultprivatestatic

接口的主要作用是表示行为契约,因此:

  • **public:**表示所有实现类都可以看到

  • **abstract:**要求实现类必须实现

JDK 16+

接口可以被密封,这样就可以限制无关类实现该接口,增强类型系统的安全性。

复制代码
public sealed interface Shape permits Circle, Rectangle {}

可以发现,这和类内部接口的作用很像,都是限制接口的实现者,规定作用范围。最大的区别就是 sealed 接口时编译器强制规范的,内部类是编写程序时的一种语义。

典型的像 Map.Entry,它就是 JDK 单独为 Map 类型设计的接口。

我们可以单独设计一个类,内部定义一个策略接口

复制代码
class Sorter {
    interface Strategy {
        boolean compare(int a, int b);
    }
​
    private Strategy strategy;
​
    public void setStrategy(Strategy s) {
        this.strategy = s;
    }
}

通过外部为这个类提供不同的策略,实现不同的功能

复制代码
Sorter sorter = new Sorter();
sorter.setStrategy(new Sorter.Strategy() {
    public boolean compare(int a, int b) { return a < b; }
});

这里我们的接口并没有用修饰符,那么编译器认为它的作用域是什么呢?

内部接口不写修饰符默认是 package-private(包级可见)

复制代码
class Outer {
 interface Inner {
     void f();
 }
}
​
class Test implements Outer.Inner {  // 只要在同包就能实现
 public void f() {}
}
复制代码
package other;
​
class Test implements Outer.Inner {} // 无法访问 Inner
​

这就一般的类修饰符规则一样。

  • 顶层类和接口的修饰符只有两种,publicpackage-private

  • 而类中字段属性的修饰规则是

    • public 对所有代码可见

    • protected 子类、同包可见

    • (default) package-private 同包可见

    • private 只有当前类可见

    • static 被它修饰的话表示当前属性属于类,不属于实例

接口和抽象类有什么关系

在讨论二者的关系时,我们先了解一下什么是抽象类:

抽象类是一种不能被创建对象的类,其中可以包含抽象方法(没有方法体的方法),用于强制子类实现某些行为。

抽象类常作为基类,内部定义子类必须重写的抽象方法,实现代码的复用。

复制代码
abstract class Animal {
    protected String name;
​
    public Animal(String name) {
        this.name = name;
    }
​
    abstract void sound();  // 抽象方法
​
    void sleep() {          // 普通方法
        System.out.println("zzz...");
    }
}
​
class Dog extends Animal {
​
    public Dog(String name) {
        super(name);
    }
​
    void sound() {
        System.out.println(name + " barks");
    }
}
​
public class Test {
    public static void main(String[] args) {
        Animal a = new Dog("Buddy");
        a.sound();
        a.sleep();
    }
}

抽象类允许包含变量、构造方法、普通方法等,但是它不能被 final 修饰,因为抽象类就是用来被继承的。它和接口相比起来的话,接口实际上是一种对类行为的规范,让不同的类实现同一套方法。

相关推荐
Geoking.几秒前
【Java】深入理解 Java 枚举(Enum)
java·开发语言
老王熬夜敲代码11 分钟前
C++新特性:string_view
开发语言·c++·笔记
像风一样的男人@13 分钟前
python --生成ico图标
java·python·spring
zhaokuner14 分钟前
06-聚合与一致性边界-DDD领域驱动设计
java·开发语言·设计模式·架构
lsx20240623 分钟前
Ionic 卡片组件深度解析
开发语言
技术小泽25 分钟前
DDD领域设计精讲
java·后端·设计模式·架构
多打代码25 分钟前
2026.1.2 删除二叉搜索树中的节点
开发语言·python·算法
一路往蓝-Anbo29 分钟前
STM32单线串口通讯实战(二):链路层核心 —— DMA环形缓冲与收发切换时序
c语言·开发语言·stm32·单片机·嵌入式硬件·物联网
萧曵 丶30 分钟前
MQ 业务实际使用与问题处理详解
开发语言·kafka·消息队列·rabbitmq·rocketmq·mq
kylezhao201936 分钟前
第三节、C# 上位机面向对象编程详解(工控硬件封装实战版)
开发语言·前端·c#