1.接口是什么

在面向对象编程中,抽象类可以定义抽象方法,但 Java 提供的 接口(Interface) 进一步强化了“规范与实现分离”的思想。它定义了一组方法签名,但不包含方法的具体实现。
接口不是类,而是一组方法规范的集合。编写接口的方式和类很相似,但是它们属于不同的概念。

2.接口的实现

接口和类之间是实现关系,一个类使用 implements 关键字可以实现一个(单实现)或多个(多实现)接口。

2.1 单实现(Single Interface Implementation)

  • 规则:类可以实现一个接口。

  • 语法

    // 单实现语法
    public class [类名] implements [接口名] {
    	// 必须实现接口中定义的所有抽象方法
    }
    
  • 说明:实现类如果是普通类(非抽象类),必须 重写接口中的所有抽象方法;如果实现类本身是抽象类,则可以选择性实现部分方法,剩下的留给子类实现。

2.2 多实现(Multiple Interface Implementation)

  • 规则:类可以实现 多个接口,接口之间用逗号分隔。

  • 语法

    // 多实现语法
    public class [类名] implements [接口名1], [接口名2]{
    	// 必须实现所有接口中未实现的抽象方法
    }
    
  • 示例

    public interface Flyable {
    	void fly();
    }
    
    public interface Swimmable {
    	void swim();
    }
    
    public class Duck implements Flyable, Swimmable {
    	public void fly() { System.out.println("Duck flies"); }
    	public void swim() { System.out.println("Duck swims"); }
    }
    
    
  • 说明:这种方式可以让一个类同时拥有多个接口的功能。

2.3 继承与实现结合

  • 规则:一个类可以继承另一个类的同时实现一个多个接口

  • 语法

    public class [子类名] extends [父类名] implements [接口名1], [接口名2] {
    // 必须实现所有接口的抽象方法(若类非抽象)
    }
    
    
  • 说明:在这种情况下,类继承得到父类的行为,同时通过接口获得额外的功能约束。

3.接口的成员及其特点(Java 8 及以后)

1.接口中可以包含常量方法

  • 变量默认是 public static final 常量
  • 方法默认是 public abstract(即使省略修饰符)。

2.接口不能声明构造方法,因为接口不能被直接实例化

在早期(JDK8 之前),接口里只能放常量和抽象方法,这导致一个问题:如果接口需要增加新功能,那么所有实现类都必须去修改代码,否则编译会报错。为了解决这种“接口扩展困难”的问题:

JDK 8 引入默认方法和静态方法

默认方法可以让实现类选择性覆盖;静态方法则用来放置和接口相关的工具方法

这两个特性主要解决了向后兼容性接口演进的问题:接口带上方法实现,避免因为接口扩展而破坏已有实现类。

默认方法

默认方法使用关键字 default修饰,允许在接口中提供一个带方法的实现。这样在接口中添加新功能时,已有的实现类可以直接继承这个默认实现,无需修改;也可以选择性地覆盖(Override)这个方法。
最典型的应用: Java 8 中对 Collection 接口的增强(例如加入了 forEach 方法)就是通过默认方法实现的。

接口中默认方法的定义格式:

  • public default [返回值类型] [方法名](参数列表){ }

示例:

  • public default void show(){ }

接口中默认方法的注意事项:

  • 默认方法不是抽象方法,所以不强制被重写。但是如果被重写,重写的时候去掉default关键字
  • public可以省略,default不能省略
  • 如果实现了多个接口,多个接口中存在相同名字的默认方法,子类就必须对该方法进行重写
在这里插入代码片// 定义接口 A
interface A {
    default void show() {
        System.out.println("A: show()");
    }
}

// 定义接口 B
interface B {
    default void show() {
        System.out.println("B: show()");
    }
}

// 实现类同时实现 A 和 B
public class Demo implements A, B {
    // 注意:
    // 1. 必须重写 show(),因为 A 和 B 的默认方法冲突
    // 2. 重写时去掉 default 关键字
    @Override
    public void show() {
        // 可以选择调用其中一个接口的默认实现
        A.super.show(); 
        // 也可以加上自己的逻辑
        System.out.println("Demo: show()");
    }

    public static void main(String[] args) {
        Demo d = new Demo();
        d.show(); // 输出:A: show() \n Demo: show()
    }
}

默认方法解决了在不破坏现有实现类的情况下,向接口中添加新方法的难题:想象一个已被成百上千个类实现的大型接口。如果直接在其中添加一个抽象方法,那么所有现有的实现类都会立即编译报错,因为它们没有实现这个新方法。

静态方法

静态方法使用关键字static修饰,调用时,必须通过 接口名 直接调用。

接口中静态方法的定义格式:

  • public static [返回值类型] [方法名](参数列表){ }

示例:

  • public static void show(){ }

接口中静态方法的注意事项:

  • 静态方法只能通过接口名调用,不能通过实现类名或者对象名调用
  • public可以省略,static不能省略
  • 静态方法不能被重写,非要写的话属于是重新定义。因为staticprivatefinal修饰的不在虚方法表中,不能被重写。
// 定义接口
interface Animal {
    static void info() {
        System.out.println("Animal interface static method");
    }
}

// 实现类
class Dog implements Animal {
    // 注意:这并不是“重写”,而是重新定义了一个静态方法
    static void info() {
        System.out.println("Dog class static method");
    }
}

public class Main {
    public static void main(String[] args) {
        // 正确调用接口的静态方法:只能用接口名
        Animal.info();

        // 不能通过对象调用接口的静态方法
        Animal a = new Dog();
        // a.info(); // 编译错误:静态方法不能通过实例调用

        // 不能通过实现类调用接口的静态方法(调用到的其实是类自己的方法)
        Dog.info(); // 调用的是 Dog 自己定义的静态方法
    }
}

输出:
Animal interface static method
Dog class static method

JDK 9 引入私有方法

这个特性主要是为了提高接口内部的代码复用,当接口中有多个默认方法或静态方法需要共享逻辑时,就可以把公共部分抽取到私有方法里,实现代码复用。方法对外不可见

接口中私有方法的定义格式:

  • 普通私有方法,为默认方法服务 private [返回值类型] [方法名](参数列表){ }
  • 静态私有方法,为静态方法服务 private static [返回值类型] [方法名](参数列表){ }

示例:

  • private void show(){ }
  • private static void method(){ }
public interface DataProcessor {

    // 默认方法 1:处理一个字符串
    default void processString(String data) {
        logStart("processString");
        String processedData = sanitize(data);
        System.out.println("String Result: " + processedData.toUpperCase());
    }

    // 默认方法 2:处理一个数字
    default void processNumber(int number) {
        logStart("processNumber");
        String data = "Number:" + number;
        String processedData = sanitize(data);
        System.out.println("Number Result: " + processedData.length());
    }

    // --- 私有普通方法 (为默认方法复用逻辑) ---

    private String sanitize(String input) {
        if (input == null || input.trim().isEmpty()) {
            return "DEFAULT_DATA";
        }
        return input.trim();
    }

    // --- 私有静态方法 (为默认方法和静态方法复用通用逻辑) ---

    private static void logStart(String methodName) {
        System.out.println("--- Starting " + methodName + " ---");
    }

    // --- 静态方法 1 ---

    public static boolean isValidKey(String key) {
        return isCommonValid(key, 5);
    }

    // --- 静态方法 2 ---

    public static boolean isStrongKey(String key) {
        return isCommonValid(key, 10);
    }

    // --- 私有静态方法 (为静态方法复用逻辑) ---

    private static boolean isCommonValid(String value, int minLength) {
        if (value == null || value.length() < minLength) {
            System.out.println("Key too short (Min: " + minLength + ")");
            return false;
        }
        return true;
    }
}

私有普通方法 sanitize(String input):它被两个 默认方法 (processStringprocessNumber) 调用,实现了对输入数据统一清洗逻辑的复用。

私有静态方法 isCommonValid(String value, int minLength):它被两个 静态方法 (isValidKeyisStrongKey) 调用,实现了统一的关键码长度校验逻辑的复用。

总结

成员类型 默认修饰符 特点 示例
常量 public static final 必须在声明时初始化。 int MAX_COUNT = 10;
抽象方法 public abstract 没有方法体,由实现类提供具体实现。 void doSomething();
默认方法 (Java 8以后) public default 有方法体,实现类可以选择性覆盖。 default void log() { ... }
静态方法 (Java 8以后) public static 有方法体,只能通过接口名直接调用。 static void utility() { ... }
私有方法 (Java 9以后) private 有方法体,用于接口内部默认方法和静态方法的代码重用。 private void helper() { ... }

4.接口的应用

4.1 将多个类会用到的规则定义为接口

接口代表规则,是行为的抽象。想要让哪个类拥有一个行为,就让这个类实现对应的接口就可以了
// 规则定义:可飞行的接口
public interface Flyable {
    // 规则:所有可飞行的东西都必须能执行 fly() 这个动作
    void fly(); 
}

// 实现类 1: 飞机类遵循 Flyable 规则
public class Airplane implements Flyable {
    @Override
    public void fly() {
        System.out.println("飞机在喷气动力的驱动下飞行。");
    }
}

// 实现类 2: 鸟类遵循 Flyable 规则
public class Bird implements Flyable {
    @Override
    public void fly() {
        System.out.println("鸟在扇动翅膀的驱动下飞行。");
    }
}

4.2 接口作为方法参数

当一个方法的参数是接口时,可以传递接口所有实现类的对象,这种方式称之为接口多态

public class Observer {
    /**
     * 方法参数是接口类型 (Flyable)。
     * 它可以接受任何实现了 Flyable 接口的类的对象。
     * @param target 任何一个可飞行的对象
     */
    public void watch(Flyable target) {
        System.out.println("观察者开始观察一个可飞行的目标...");
        // 尽管不知道传入的是飞机还是鸟,但可以确定它有 fly() 行为
        target.fly(); 
        System.out.println("观察结束。");
    }
}

// 测试类
public class TestApplication {
    public static void main(String[] args) {
        Observer observer = new Observer();
        
        // 传入 Airplane 对象
        Airplane myPlane = new Airplane();
        observer.watch(myPlane);
        // 输出:
        // 观察者开始观察一个可飞行的目标...
        // 飞机在喷气动力的驱动下飞行。
        // 观察结束。

        System.out.println("--------------------");

        // 传入 Bird 对象
        Bird myBird = new Bird();
        observer.watch(myBird);
        // 输出:
        // 观察者开始观察一个可飞行的目标...
        // 鸟在扇动翅膀的驱动下飞行。
        // 观察结束。
    }
}

补充: 接口与抽象类的对比

接口和抽象类都是实现抽象(Abstraction) 的重要手段,但它们在定义、设计意图和使用上存在关键区别。

相同点

  • 不能直接实例化: 两者都不能被直接创建对象(new)。它们都必须由子类或实现类来完善和使用。

  • 抽象性: 它们都可以包含抽象方法(没有方法体的方法),要求子类/实现类去实现。

  • 多态性: 都可以作为类型引用,指向其子类/实现类的实例,从而实现多态。

不同点(关键区别)

特性 接口(Interface) 抽象类(Abstract Class)
关键字 interface abstract class
继承关系 类可以实现(implements多个接口。 类只能单继承(extends一个抽象类。
构造器 不能有构造器。 可以有构造器(但不能直接调用 new,仅供子类调用)。
成员变量 只能有 public static final 常量 可以有各种访问修饰符的变量
方法 JDK 7:只能有抽象方法 JDK 8+:可有 default、static 方法 JDK 9+:支持私有方法 可以包含 抽象方法普通方法(有方法体的方法)。
设计意图 定义能力/行为规范做什么),适用于不相关的类共享同一行为。 定义类族共同的基类是什么),适用于有密切关系的类。
实现细节 在 Java 8 以前,不能包含任何实现细节。 可以包含部分实现细节(有方法体的方法)。
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐