10年老兵带你学Java(第5课):接口与抽象类 - 抽象与契约

0 阅读7分钟

一、抽象类:抽象概念的类

1.1 什么是抽象类?

抽象类就是用 abstract 修饰的类,不能直接实例化(不能 new)。

**为什么需要抽象类? **

举个例子:

  • "动物"是一个抽象概念,你不能创造一个"动物",只能说"这是猫"或"这是狗"

  • "形状"是一个抽象概念,你不能说"给我画一个形状",只能说"画个圆"或"画个方"

**抽象类的特点: **

  1. abstract 修饰

  2. 不能直接 new 创建对象

  3. 可以有抽象方法(没有方法体的方法)

  4. 也可以有普通方法和属性

1.2 抽象类的语法


// 抽象类:用abstract修饰

public abstract class Animal {

    protected String name;

  


    // 抽象方法:没有方法体,子类必须实现

    public abstract void sound();

  


    // 普通方法:有方法体,子类可以直接继承

    public void eat() {

        System.out.println(name + "在吃东西");

    }

  


    // 构造方法(子类调用)

    public Animal(String name) {

        this.name = name;

    }

}

**子类继承抽象类: **


// 用extends继承抽象类

public class Cat extends Animal {

  


    public Cat(String name) {

        super(name);  // 调用父类构造

    }

  


    // 必须实现父类的抽象方法

    @Override

    public void sound() {

        System.out.println(name + "喵喵喵");

    }

}

**使用: **


public class Main {

    public static void main(String[] args) {

        // 不能直接new抽象类

        // Animal a = new Animal();  // 错误!

  


        // 只能创建子类对象

        Animal cat = new Cat("小花");

        cat.eat();     // 继承来的方法

        cat.sound();   // 重写的方法

    }

}

1.3 抽象方法

抽象方法是只有方法签名、没有方法体的方法。


public abstract class Animal {

    // 抽象方法:没有{}

    public abstract void sound();

    public abstract void move();

}

**子类必须实现所有抽象方法,否则子类也必须是抽象类。 **


二、接口:行为的契约

2.1 什么是接口?

接口是一种行为规范,它定义了一组"能做什么"的方法,但不关心"怎么做"。

**打个比方: **

  • USB接口:定义了供电和数据传输的规范,不管你是U盘还是鼠标,只要符合这个接口就能用

  • Java接口:定义了一组方法的规范,不管哪个类实现,只要符合接口就能互换

2.2 接口的语法


// 定义接口:用interface

public interface Flyable {

    // 接口中的方法默认是抽象的(Java 7以前)

    // 抽象方法:没有方法体

    void fly();

  


    // 接口中的属性默认是常量(public static final)

    int MAX_SPEED = 1000;

}

类实现接口:用 implements


// 鸟实现飞接口

public class Bird implements Flyable {

    @Override

    public void fly() {

        System.out.println("鸟儿扇动翅膀飞翔");

    }

}

  


// 飞机实现飞接口

public class Airplane implements Flyable {

    @Override

    public void fly() {

        System.out.println("飞机发动机推动飞翔");

    }

}

**使用: **


public class Main {

    public static void main(String[] args) {

        // 接口引用指向实现类对象

        Flyable f1 = new Bird();

        Flyable f2 = new Airplane();

  


        f1.fly();  // 鸟儿扇动翅膀飞翔

        f2.fly();   // 飞机发动机推动飞翔

    }

}

2.3 接口的特点

  1. 接口不能实例化(不能new)

  2. 接口的方法默认是抽象的(Java 7及以前)

  3. 接口的属性默认是常量(public static final)

  4. 一个类可以实现多个接口(弥补单继承的不足)

  5. 接口可以继承接口(多继承)

2.4 接口的多实现


// 接口1

public interface Runnable {

    void run();

}

  


// 接口2

public interface Eatable {

    void eat();

}

  


// 一个类可以实现多个接口

public class Person implements Runnable, Eatable {

    @Override

    public void run() {

        System.out.println("人在跑步");

    }

  


    @Override

    public void eat() {

        System.out.println("人在吃饭");

    }

}

**这解决了单继承的问题——类只能继承一个父类,但可以实现多个接口。 **

2.5 接口的默认方法(Java 8)

**Java 8开始,接口可以有默认方法: **


public interface Flyable {

    // 抽象方法

    void fly();

  


    // 默认方法:用default修饰,有方法体

    default void land() {

        System.out.println("安全降落");

    }

}

**作用: ** 如果接口需要新增方法,旧实现类不用全部重写。


public class Bird implements Flyable {

    @Override

    public void fly() {

        System.out.println("鸟儿飞翔");

    }

    // 不需要实现land(),可以直接用默认的

}

  


// 使用

Flyable b = new Bird();

b.land();  // 输出:安全降落

2.6 接口的静态方法(Java 8)


public interface MathUtil {

    static int max(int a, int b) {

        return a > b ? a : b;

    }

}

  


// 调用:直接用接口名调用

int m = MathUtil.max(10, 20);  // 20


三、抽象类 vs 接口

3.1 核心区别

| 对比项 | 抽象类 | 接口 |

|--------|--------|------|

| 关键字 | abstract class | interface |

| 继承/实现 | extends(单继承) | implements(多实现) |

| 属性 | 无限制 | 默认常量 public static final |

| 方法 | 抽象 + 普通都可以 | Java 7:抽象方法;Java 8+:抽象 + 默认 + 静态 |

| 构造方法 | 有 | 没有 |

| 什么时候用 | "是什么"的关系 | "能做什么"的关系 |

3.2 选择原则

**用抽象类: **

  • 类之间有明显的"是...的"关系(猫是动物)

  • 需要共享代码(普通方法)

  • 需要控制属性访问权限

**用接口: **

  • 类之间有相同的行为但没有继承关系

  • 需要多实现(一个类实现多个接口)

  • 需要解耦(面向接口编程)

3.3 经典例子:门


// 抽象类:门(抽象概念)

abstract class Door {

    protected String material;

  


    public Door(String material) {

        this.material = material;

    }

  


    // 开门(普通方法)

    public void open() {

        System.out.println("门打开了");

    }

  


    // 关门(普通方法)

    public void close() {

        System.out.println("门关上了");

    }

  


    // 报警(抽象方法,子类实现)

    public abstract void alarm();

}

  


// 木门

class WoodenDoor extends Door {

    public WoodenDoor() {

        super("木头");

    }

  


    @Override

    public void alarm() {

        System.out.println("木头门发出警报:哔哔哔");

    }

}

  


// 铁门

class IronDoor extends Door {

    public IronDoor() {

        super("铁");

    }

  


    @Override

    public void alarm() {

        System.out.println("铁门发出警报:滴滴滴");

    }

}


四、综合练习:宠物店系统

4.1 需求

用接口和抽象类,实现一个宠物店系统:

  • 抽象类 Pet:宠物的基本属性

  • 接口 Jumpable:能跳的动作

  • 接口 Eatable:能吃的动作

4.2 代码


// 抽象类:宠物

abstract class Pet {

    protected String name;

    protected int age;

  


    public Pet(String name, int age) {

        this.name = name;

        this.age = age;

    }

  


    // 普通方法

    public void showInfo() {

        System.out.println("宠物名:" + name + ",年龄:" + age);

    }

}

  


// 接口:能跳

interface Jumpable {

    void jump();

}

  


// 接口:能吃

interface Eatable {

    void eat();

}

  


// 狗:继承Pet,实现Jumpable和Eatable

class Dog extends Pet implements Jumpable, Eatable {

    public Dog(String name, int age) {

        super(name, age);

    }

  


    @Override

    public void jump() {

        System.out.println(name + "跳起来了!");

    }

  


    @Override

    public void eat() {

        System.out.println(name + "在吃狗粮");

    }

}

  


// 猫:继承Pet,实现Jumpable和Eatable

class Cat extends Pet implements Jumpable, Eatable {

    public Cat(String name, int age) {

        super(name, age);

    }

  


    @Override

    public void jump() {

        System.out.println(name + "轻盈地跳上了桌子");

    }

  


    @Override

    public void eat() {

        System.out.println(name + "在吃猫粮");

    }

}

  


// 主程序

public class Main {

    public static void main(String[] args) {

        Pet[] pets = {

            new Dog("旺财", 3),

            new Cat("小白", 2)

        };

  


        System.out.println("=== 宠物店 ===");

        for (Pet pet : pets) {

            pet.showInfo();

  


            // 强制类型转换后调用接口方法

            if (pet instanceof Jumpable) {

                ((Jumpable) pet).jump();

            }

            if (pet instanceof Eatable) {

                ((Eatable) pet).eat();

            }

            System.out.println();

        }

  


        // 统一管理:能跳的宠物

        System.out.println("=== 能跳的宠物 ===");

        for (Pet pet : pets) {

            if (pet instanceof Jumpable) {

                Jumpable j = (Jumpable) pet;

                j.jump();

            }

        }

    }

}

4.3 运行结果


=== 宠物店 ===

宠物名:旺财,年龄:3

旺财跳起来了!

旺财在吃狗粮

  


宠物名:小白,年龄:2

小白轻盈地跳上了桌子

小白在吃猫粮

  


=== 能跳的宠物 ===

旺财跳起来了!

小白轻盈地跳上了桌子


五、常见错误

❌ 错误1:抽象类能直接new


public abstract class Animal {

    public abstract void sound();

}

  


// 错误!抽象类不能实例化

Animal a = new Animal();

  


// 正确

Animal a = new Cat();

❌ 错误2:接口属性不是常量


public interface Flyable {

    // 错误!这是常量,默认是 public static final

    int MAX_SPEED = 1000;

  


    // 如果写:

    int speed;  // 等价于 public static final int speed = 1000;

}

❌ 错误3:接口方法漏写public


public interface Flyable {

    // Java 7以前,必须写public

    void fly()// 等价于 public abstract void fly();

  


    // 错误!子类实现时需要public,这里不写会报错

    void land();

}

❌ 错误4:抽象方法没有全部实现


public abstract class Animal {

    public abstract void sound();

    public abstract void move();

}

  


// 错误!子类必须实现所有抽象方法

class Cat extends Animal {

    @Override

    public void sound() {

        System.out.println("喵");

    }

    // 缺少move()的实现,报错!

}

  


// 正确

class Cat extends Animal {

    @Override

    public void sound() {

        System.out.println("喵");

    }

  


    @Override

    public void move() {

        System.out.println("猫在走");

    }

}


六、本课总结

**抽象类: **

  • abstract class 定义

  • 单继承,有构造方法

  • 适合"是什么"的关系

  • 可以有抽象方法和普通方法

**接口: **

  • interface 定义

  • 多实现,无构造方法

  • 适合"能做什么"的关系

  • Java 7:抽象方法;Java 8+:默认方法、静态方法

**选择: **

  • 有继承关系,用抽象类

  • 需要多实现,用接口

  • 既要继承又要多实现:先extends一个类,再implements多个接口


七、下节课预告

第6课:常用类

  • String 字符串

  • StringBuilder 可变字符串

  • 日期时间 API

  • 包装类

学完这课,你就能更高效地处理数据了。


**关注我,跟着老兵学Java,少走弯路。 **

💬 **评论区聊聊:抽象类和接口,哪个更容易搞混? **