Z
ZHANK
面向对象基础

继承与多态

理解 extends、super、方法重写、向上/向下转型

继承与多态

继承和多态是面向对象的两大基石。继承让子类复用父类的代码,多态让同一行代码能处理不同类型的对象。

学完本章你将: 掌握 extends、super、方法重写、向上/向下转型、Object 根类。


继承 —— 代码复用

继承的本质是 "is-a" 关系:Dog is an Animal(狗是动物)。子类拥有父类的所有属性和方法,还能添加自己的特色。

java
// 父类 —— 提取公共特性
public class Animal {
    protected String name;    // protected:子类可见

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

    public void eat() {
        System.out.println(name + " 在吃东西");
    }
}

// 子类 —— 继承父类 + 增加自己的内容
public class Dog extends Animal {
    public Dog(String name) {
        super(name);          // 调用父类构造器(必须放第一行)
    }

    @Override                 // 注解表明"这是重写"
    public void eat() {       // 重写父类的 eat()
        System.out.println(name + " 在吃骨头");
    }

    public void bark() {      // 子类独有的方法
        System.out.println(name + " 汪汪!");
    }
}

⚠️ 注意: Java 是单继承,一个类只能有一个直接父类。但是可以通过接口实现多继承效果。


多态 —— 一个接口,多种形态

多态的核心是:父类引用可以指向子类对象Animal a = new Dog() 这行代码中,变量 a 的类型是 Animal,但它实际指向的是 Dog 对象。调用 a.eat() 时,JVM 会在运行时找到实际对象的方法来执行,而不是看变量类型。这就是动态绑定

java
// 向上转型 —— 自动,安全的(Dog 肯定是 Animal)
Animal animal = new Dog("旺财");
animal.eat();   // "旺财 在吃骨头" —— 调用的是 Dog 的 eat,不是 Animal 的!
// animal.bark(); // ❌ Animal 类型没有 bark() 方法

// 向下转型 —— 需要强制,可能失败
if (animal instanceof Dog) {   // 先检查:animal 真的是 Dog 吗?
    Dog dog = (Dog) animal;    // 安全转型
    dog.bark();                // "旺财 汪汪!"
}

多态的真正威力 —— 统一处理不同类型:

java
Animal[] zoo = {
    new Dog("旺财"),
    new Cat("咪咪"),
    new Bird("小啾"),
};

// 不用管具体是什么动物,统一调用 eat()
for (Animal a : zoo) {
    a.eat();   // 每个动物用自己的方式吃
}
// 输出:旺财 在吃骨头  / 咪咪 在吃鱼  / 小啾 在吃虫子

💡 什么时候用多态? 当你写代码时,如果发现自己在写很多 if (x instanceof Dog) ... else if (x instanceof Cat) ...,说明应该用多态重构成统一接口。


Object —— 所有类的祖先

Java 中每个类都隐式继承 ObjectObject 提供了三个最常被覆盖的方法:

方法默认行为何时重写
`toString()`返回 `类名@哈希值`需要打印有意义的对象信息
`equals()`比较内存地址(等同 `==`)需要按内容比较两个对象
`hashCode()`返回内存地址的哈希重写 equals 时必须一起重写
java
public class User {
    private String name;
    private int age;

    @Override
    public String toString() {
        return "User{name='" + name + "', age=" + age + "}";
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;           // 同一个引用
        if (!(o instanceof User u)) return false; // 类型不匹配
        return age == u.age && Objects.equals(name, u.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);       // 与 equals 保持一致
    }
}

⚠️ 黄金法则: 重写 equals 必须同时重写 hashCode,否则 HashMap/HashSet 会出 bug!