首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >接口的深度解剖

接口的深度解剖

作者头像
Han.miracle
发布2025-12-22 14:01:15
发布2025-12-22 14:01:15
2800
举报

接口的概念

接口” 是一个在计算机科学、电子工程、软件工程等多个领域高频出现的核心概念,本质是 “不同系统 / 组件之间交互的规则与通道”。 接口就是公共的行为规范标准,大家在实现时,只要符合规范标准,就可以通用。 在Java中,接口可以看作:多个类的公共规范,是一种引用数据类型。

语法规则:

接口的定义和定义类的格式基本相同,将class关键字换成 interface 的关键字

代码语言:javascript
复制
public interface 接口名称{
//抽象方法

}

1.接口不可包含 “方法的具体实现代码” 这是软件接口最核心的禁忌。接口只声明 “方法名、参数、返回值”(即 “方法签名”),不允许写方法体(即 “怎么做” 的逻辑) 错误示例(Java):接口中写方法实现,代码会直接报错

代码语言:javascript
复制
interface Payment {
    // 错误:接口方法不能有方法体({}内的实现)
    void pay(double money) { 
        System.out.println("支付" + money + "元"); 
    }
}

正确示例:只保留方法签名,实现交给具体类

代码语言:javascript
复制
interface Payment {
    void pay(double money); // 仅声明方法,无实现
}
// 由WeChatPay类提供具体实现
class WeChatPay implements Payment {
    @Override
    void pay(double money) {
        System.out.println("微信支付" + money + "元"); // 实现逻辑放在这里
    }
}

2.不可包含 “可修改的成员变量”(非常量) 接口中的成员变量默认是public static final(公共、静态、常量),不允许定义普通变量(如int count)。

代码语言:javascript
复制
interface Payment {
    // 正确:常量名通常大写,必须初始化
    double MIN_PAY_AMOUNT = 0.01; 
    public static final double MIN_PAY_AMOUNT2 = 0.01; 
}

3.不可包含 “构造方法” 构造方法是用于创建 “类的实例” 的,而接口本身不能被实例化(无法new Payment()),因此不需要也不允许定义构造方法.

注意: 1。创建接口时,接口的命名一般时以大写的 I 开头 2.接口的命名一般使用“形容词”的单词,比如在后面加上一个able 3.阿里的编码规范,接口的方法和属性最好不要加任何修饰符号,保持代码的简洁性

接口的使用

接口不可以直接使用,必须要有一个实现类来实现(implements)接口,实现接口中的所有抽象方法。

代码语言:javascript
复制
public  class 
类名称
 implements 
接⼝名称
{
 // ...
 }

注意:子类和父类之间是extends 是继承关系,类和接口之间是implements 实现关系。

接口的特性

1.接口类型是一张引用类型,但是不能直接new接口的对象 2. 接⼝中每⼀个方法都是public的抽象⽅法,即接⼝中的方法会被隐式的指定为public abstract(只能是publicabstract,其他修饰符都会报错) 错误示范:

代码语言:javascript
复制
 public interface USB {
 // Error:(4, 18) java: 此处不允许使⽤修饰符private 
private void openDevice();
 void closeDevice();

正确示范:

代码语言:javascript
复制
public interface USB {
public abstract void openDevice();
}

3.接口的方法是不能在接口中实现的,只能由实现接口的类来实现

代码语言:javascript
复制
```java
public interface USB {
public abstract void openDevice();
}
代码语言:javascript
复制
4.重写接口的方法时,不能使用默认的访问权限,重写接口方法时必须使用public权限。
5.接口中可以有变量,但是接口中的变量会被隐式指定为public static final 

```java
interface MyInterface {
    // 等价于:public static final int MAX_VALUE = 100;
    int MAX_VALUE = 100;
}

6.接口中不能有静态的代码和构造方法 7.接口不是类,但是接口编译完成后字节码文件的后缀也是,class 8.jdk8中,接口还可以包括default方法

实现多个接口

在Java中,类和类之间是单继承的,⼀个类只能有⼀个父类,即Java中不⽀持多继承,但是⼀个类可以实现多个接口。

代码语言:javascript
复制
interface Flyable {
    void fly();
}

interface Swimmable {
    void swim();
}

// 鸭子既能飞也能游,实现两个接口
class Duck implements Flyable, Swimmable {
    @Override
    public void fly() {
        System.out.println("鸭子飞起来了");
    }
    
    @Override
    public void swim() {
        System.out.println("鸭子游起来了");
    }
}

注意:在 Java 中,当一个类实现多个接口时,必须实现所有接口中的抽象方法;如果有任何一个抽象方法未实现,这个类就必须被声明为抽象类。

接口之间的继承

在 Java 中,接口之间支持多继承(一个接口可以同时继承多个接口),这与类之间的单继承形成鲜明对比,也让接口成为实现类似 “多继承” 功能的灵活方式。

代码语言:javascript
复制
// 父接口1
interface A {
    void methodA();
}

// 父接口2
interface B {
    void methodB();
}

// 子接口C同时继承A和B
interface C extends A, B { // 接口多继承,用逗号分隔多个父接口
    void methodC(); // 子接口自己的方法
}

接⼝使⽤实例

对象之间进行大小比较关系

一、使用Comparable接口(“自然排序”) Comparable接口位于java.lang包下,包含一个抽象方法compareTo(T o),用于定义对象的 “自然排序规则”(如数字从小到大、字符串按字典序)。 核心特点: 类通过实现Comparable接口,自身定义比较规则(类似 “自带排序逻辑”); compareTo方法返回值: 正数:当前对象 > 目标对象; 0:当前对象 == 目标对象; 负数:当前对象 < 目标对象。 示例:比较学生对象的年龄

代码语言:javascript
复制
// 学生类实现Comparable接口,按年龄排序
class Student implements Comparable<Student> {
    private String name;
    private int age;

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

    // 实现compareTo方法:按年龄比较
    @Override
    public int compareTo(Student other) {
        // 返回当前对象年龄 - 目标对象年龄(从小到大排序)
        return this.age - other.age;
    }

    // getter和toString方法
    public int getAge() { return age; }
    @Override
    public String toString() { return name + "(" + age + ")"; }
}

// 测试比较
public class Test {
    public static void main(String[] args) {
        Student s1 = new Student("张三", 20);
        Student s2 = new Student("李四", 18);

        if (s1.compareTo(s2) > 0) {
            System.out.println(s1 + " 年龄大于 " + s2); // 输出:张三(20) 年龄大于 李四(18)
        }
    }
}

二、使用Comparator接口(“定制排序”) Comparator接口位于java.util包下,包含compare(T o1, T o2)方法,用于外部定义比较规则(不修改原类的代码,适合对已有类进行排序)。 核心特点: 无需修改被比较的类,通过 “比较器” 类实现比较规则; 可定义多种比较规则(如学生既可按年龄排,也可按姓名排); compare方法返回值规则同compareTo。 示例:为学生类定义多种比较规则

代码语言:javascript
复制
import java.util.Comparator;

// 学生类(未实现Comparable接口)
class Student {
    private String name;
    private int age;

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

    // getter和toString方法
    public String getName() { return name; }
    public int getAge() { return age; }
    @Override
    public String toString() { return name + "(" + age + ")"; }
}

// 比较器1:按年龄从小到大排序
class AgeComparator implements Comparator<Student> {
    @Override
    public int compare(Student s1, Student s2) {
        return s1.getAge() - s2.getAge();
    }
}

// 比较器2:按姓名字典序排序
class NameComparator implements Comparator<Student> {
    @Override
    public int compare(Student s1, Student s2) {
        // 字符串的compareTo方法已实现Comparable接口
        return s1.getName().compareTo(s2.getName());
    }
}

// 测试比较
public class Test {
    public static void main(String[] args) {
        Student s1 = new Student("张三", 20);
        Student s2 = new Student("李四", 18);

        // 使用年龄比较器
        AgeComparator ageComp = new AgeComparator();
        if (ageComp.compare(s1, s2) > 0) {
            System.out.println(s1 + " 年龄大于 " + s2); // 输出:张三(20) 年龄大于 李四(18)
        }

        // 使用姓名比较器
        NameComparator nameComp = new NameComparator();
        if (nameComp.compare(s1, s2) > 0) {
            System.out.println(s1 + " 姓名在 " + s2 + " 之后"); // 输出:张三(20) 姓名在 李四(18) 之后(因"张"的拼音在"李"之后)
        }
    }
}

两种的区别和应用场景

在这里插入图片描述
在这里插入图片描述

Clonable接口和深拷贝

在 Java 中,Cloneable接口和对象拷贝(深拷贝 / 浅拷贝)是实现对象复制的重要机制。Cloneable本身是一个标记接口(无任何抽象方法),仅用于标识 “该类允许被克隆”,而拷贝的深浅则取决于具体实现逻辑。 一、Cloneable接口的核心作用 标记作用:一个类只有实现Cloneable接口,才能调用Object类的clone()方法进行克隆;否则会抛出CloneNotSupportedException异常。 无抽象方法:与普通接口不同,Cloneable内部没有任何需要实现的方法,仅作为 “可克隆” 的标识。 基本使用步骤: 类实现Cloneable接口; 重写Object类的clone()方法(protected改为public); 在clone()方法中调用super.clone(),并处理可能的异常。

代码语言:javascript
复制
class Person implements Cloneable {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // 重写clone()方法
    @Override
    public Person clone() throws CloneNotSupportedException {
        // 调用父类的clone()实现浅拷贝
        return (Person) super.clone();
    }

    // getter、setter和toString
    @Override
    public String toString() {
        return "Person{name='" + name + "', age=" + age + "}";
    }
}

二、浅拷贝(Shallow Copy) super.clone()默认实现的是浅拷贝:

对于基本数据类型(int、double等),会复制其值; 对于引用数据类型(对象、数组等),仅复制引用地址(新对象与原对象共享同一个引用对象)。

代码语言:javascript
复制
class Address {
    private String city;
    public Address(String city) { this.city = city; }
    // getter、setter和toString
    public void setCity(String city) { this.city = city; }
    @Override
    public String toString() { return "Address{city='" + city + "'}"; }
}

class Person implements Cloneable {
    private String name;
    private Address address; // 引用类型

    public Person(String name, Address address) {
        this.name = name;
        this.address = address;
    }

    @Override
    public Person clone() throws CloneNotSupportedException {
        return (Person) super.clone(); // 浅拷贝
    }

    // getter、setter和toString
    @Override
    public String toString() {
        return "Person{name='" + name + "', address=" + address + "}";
    }
}

// 测试浅拷贝
public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        Address addr = new Address("北京");
        Person p1 = new Person("张三", addr);
        Person p2 = p1.clone(); // 克隆p1

        // 修改p2的地址,p1的地址也会被改变(共享引用)
        p2.getAddress().setCity("上海");

        System.out.println(p1); // 输出:Person{name='张三', address=Address{city='上海'}}
        System.out.println(p2); // 输出:Person{name='张三', address=Address{city='上海'}}
    }
}

三、深拷贝(Deep Copy) 深拷贝会完全复制所有层级的对象,包括引用类型的成员变量,使得新对象与原对象完全独立(不共享任何引用)。 实现深拷贝的两种方式: 对引用类型也进行克隆 在clone()方法中,不仅克隆当前对象,还需手动克隆其引用类型的成员变量。

代码语言:javascript
复制
class Address implements Cloneable {
    private String city;
    public Address(String city) { this.city = city; }

    // 地址类也实现克隆
    @Override
    public Address clone() throws CloneNotSupportedException {
        return (Address) super.clone();
    }

    // getter、setter和toString(同上)
}

class Person implements Cloneable {
    private String name;
    private Address address;

    public Person(String name, Address address) {
        this.name = name;
        this.address = address;
    }

    @Override
    public Person clone() throws CloneNotSupportedException {
        // 1. 先克隆当前对象(浅拷贝)
        Person clone = (Person) super.clone();
        // 2. 再克隆引用类型成员(深拷贝关键步骤)
        clone.address = this.address.clone();
        return clone;
    }

    // getter、setter和toString(同上)
}

// 测试深拷贝
public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        Address addr = new Address("北京");
        Person p1 = new Person("张三", addr);
        Person p2 = p1.clone();

        // 修改p2的地址,p1不受影响(引用独立)
        p2.getAddress().setCity("上海");

        System.out.println(p1); // 输出:Person{name='张三', address=Address{city='北京'}}
        System.out.println(p2); // 输出:Person{name='张三', address=Address{city='上海'}}
    }
}

通过序列化实现深拷贝 将对象序列化为字节流,再反序列化为新对象,可自动实现所有层级的复制(需所有类实现Serializable接口)。 四、Cloneable接口与深拷贝的关系 Cloneable仅开启克隆功能,本身不决定拷贝方式(浅 / 深); 浅拷贝是super.clone()的默认行为,适合仅包含基本类型的对象; 深拷贝需要手动实现(克隆所有引用类型成员),适合包含多层引用的复杂对象; 若类中包含final修饰的引用变量,无法通过clone()实现深拷贝(final变量不可重新赋值)。 总结 Cloneable接口是对象克隆的 “开关”,而深拷贝是在其基础上的增强实现:

浅拷贝:简单高效,但可能导致对象共享引用,存在副作用; 深拷贝:完全独立,但实现复杂(需递归克隆所有引用类型)。

选择哪种拷贝方式,需根据对象的结构(是否包含引用类型)和业务需求(是否允许共享引用)决定。

抽象类和接口的区别

抽象类和接口都是Java中多态的常见使用方式,都需要重点掌握.同时又要认清两者的区别 核⼼区别:抽象类中可以包含普通方法和普通字段,这样的普通方法和字段可以被子类直接使用(不必重写), 而接口中不能包含普通普通,子类必须重写所有的抽想方法. 抽象类(abstract class)和接口(interface)是 Java 中实现抽象设计的两大核心机制,虽然很多开发者容易混淆。它们都能定义抽象方法、都不能直接实例化,但在语法规则、设计目标和使用场景上有本质区别。以下从 10 个核心维度详细对比: 一、语法定义与结构 抽象类 用abstract class声明,可包含: 抽象方法(abstract void method();,无方法体); 非抽象方法(有完整方法体,可直接实现逻辑); 各种访问权限的成员变量(public/protected/ 默认 /private); 构造方法(用于子类初始化时调用)。

代码语言:javascript
复制
abstract class Animal {
    // 成员变量(可带初始值)
    protected String name;
    
    // 构造方法
    public Animal(String name) {
        this.name = name;
    }
    
    // 抽象方法(无实现)
    public abstract void shout();
    
    // 非抽象方法(有实现)
    public void eat() {
        System.out.println(name + "在吃东西");
    }
}

用interface声明,Java 8+ 可包含: 抽象方法(默认public abstract,可省略修饰符); 默认方法(default void method() {},带方法体,子类可重写); 静态方法(static void method() {},属于接口本身); 成员变量(默认public static final,必须初始化,本质是常量)

代码语言:javascript
复制
interface Flyable {
    // 常量(默认public static final)
    int MAX_HEIGHT = 1000;
    
    // 抽象方法(默认public abstract)
    void fly();
    
    // 默认方法(带实现)
    default void land() {
        System.out.println("降落");
    }
    
    // 静态方法
    static void info() {
        System.out.println("这是飞行接口");
    }
}

二、继承 / 实现方式 抽象类:子类用extends继承,单继承(一个类只能继承一个抽象类)。 接口:类用implements实现,多实现(一个类可实现多个接口);接口之间用extends多继承(一个接口可继承多个接口)。

在这里插入图片描述
在这里插入图片描述

五、构造方法 抽象类:有构造方法(用于子类继承时初始化父类成员),但不能直接new实例。 接口:无构造方法(接口不涉及实例状态,无需初始化)。 六、设计目标 抽象类: 核心是 “代码复用”,通过抽取多个子类的共性实现(如Animal的eat()方法),让子类继承后直接使用,减少重复代码。 体现 “is-a” 关系(子类是父类的一种,如Dog is a Animal)。 接口: 核心是 “规范定义”,通过声明多个类的共性行为(如Flyable的fly()),让不同类(如Bird、Plane)都能实现,无关乎它们的继承关系。 体现 “can-do” 关系(类具备某种能力,如Bird can fly)。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-09-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 接口的概念
  • 语法规则:
    • 接口的使用
    • 接口的特性
    • 实现多个接口
    • 接口之间的继承
    • 接⼝使⽤实例
    • Clonable接口和深拷贝
    • 抽象类和接口的区别
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档