V
Vel·ToolKit
简洁 · 高效 · 即开即用
ZH
第 8 章 / 共 20 章

继承与多态

extends、super、覆盖、final、Object 三件套、转型

extends 单继承

Java 是单继承——一个类只能 extends 一个父类(但能 implements 多个接口)。子类自动获得父类的非 private 成员;可以增加新成员、覆盖父类方法。

// Inherit.java
public class Inherit {
    public static void main(String[] args) {
        Dog d = new Dog("Rex", "Husky");
        d.eat();   // 来自 Animal
        d.bark();  // 来自 Dog
    }
}

class Animal {
    String name;
    Animal(String name) { this.name = name; }
    void eat() { System.out.println(name + " is eating"); }
}

class Dog extends Animal {
    String breed;
    Dog(String name, String breed) {
        super(name);     // 调用父类构造器
        this.breed = breed;
    }
    void bark() { System.out.println(name + " (" + breed + ") barks"); }
}

super 与构造链

super(...) 调用父类构造器,必须是子类构造器的第一行(如果没显式调用,编译器自动插入 super()——这要求父类有无参构造器)。super.method() 调用被覆盖的父类方法。

// SuperDemo.java
public class SuperDemo {
    public static void main(String[] args) {
        new B().greet();
    }
}

class A {
    A() { System.out.println("A()"); }
    void greet() { System.out.println("Hi from A"); }
}

class B extends A {
    B() {
        super();              // 不写也会自动调(要求 A 有无参构造器)
        System.out.println("B()");
    }
    @Override
    void greet() {
        super.greet();        // 调用父类的 greet
        System.out.println("Hi from B");
    }
}

方法覆盖与 @Override

子类用相同签名重写父类方法称为"覆盖"。强烈建议加 @Override 注解:万一签名敲错,编译器会立即报错,避免变成无关的新方法。

// Override.java
public class Override {
    public static void main(String[] args) {
        new Square(3).describe();
    }
}

class Shape {
    double area() { return 0; }
    void describe() {
        System.out.println("area = " + area());
    }
}

class Square extends Shape {
    double side;
    Square(double side) { this.side = side; }

    @Override
    double area() { return side * side; }
}

多态:父类引用指向子类对象

Java 的方法调用基于运行时实际对象类型(动态分派):父类引用变量持有子类对象时,调用方法走子类版本。这是面向对象设计的核心。

// Polymorphism.java
public class Polymorphism {
    public static void main(String[] args) {
        Shape[] shapes = { new Circle(5), new Square(3) };
        for (Shape s : shapes) {
            System.out.println(s.area()); // 调谁的 area?看运行时类型
        }
    }
}

class Shape {
    double area() { return 0; }
}
class Circle extends Shape {
    double r;
    Circle(double r) { this.r = r; }
    @Override double area() { return Math.PI * r * r; }
}
class Square extends Shape {
    double side;
    Square(double side) { this.side = side; }
    @Override double area() { return side * side; }
}

final 类与 final 方法

final 类不能被继承(String、Integer 等都是 final)。final 方法不能被子类覆盖。用于保证某些行为不被改变。

// FinalDemo.java
public class FinalDemo {
    public static void main(String[] args) {
        System.out.println(new B().tag());
    }
}

class A {
    final String tag() { return "A"; }   // 子类不能覆盖
}

class B extends A {
    // @Override String tag() { return "B"; } // 编译错误
}

// final class C 表示禁止 C 被继承
final class C {}

向上转型 / 向下转型

向上转型(子→父)总是安全且隐式;向下转型(父→子)需要显式 cast 且运行时可能 ClassCastException,先用 instanceof 判断。Java 16+ 的模式变量让这个流程更简洁(见第 3 章)。

// Casting.java
public class Casting {
    public static void main(String[] args) {
        Animal a = new Dog();  // 向上转型,隐式
        a.eat();

        // 向下转型必须显式 + 安全判断
        if (a instanceof Dog d) {     // Java 16+ 模式变量
            d.bark();
        }

        Animal a2 = new Animal();
        // ((Dog) a2).bark();  // 运行时 ClassCastException
    }
}

class Animal { void eat() { System.out.println("eat"); } }
class Dog extends Animal { void bark() { System.out.println("bark"); } }

Object 与 equals / hashCode / toString

所有类都默认继承 java.lang.Object。三个最常被覆盖的方法:equals(内容相等判断)、hashCode(哈希值,用于 HashMap/HashSet)、toString(字符串表示,便于打日志)。一旦覆盖 equals,必须**一起**覆盖 hashCode,否则集合类会出错。

// EqualsHashCode.java
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;

public class EqualsHashCode {
    public static void main(String[] args) {
        Set<Point> set = new HashSet<>();
        set.add(new Point(1, 2));
        System.out.println(set.contains(new Point(1, 2))); // true(覆盖了 equals/hashCode)
        System.out.println(new Point(1, 2));               // Point(1, 2)
    }
}

class Point {
    final int x;
    final int y;
    Point(int x, int y) { this.x = x; this.y = y; }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Point p)) return false;
        return x == p.x && y == p.y;
    }

    @Override
    public int hashCode() {
        return Objects.hash(x, y);
    }

    @Override
    public String toString() {
        return "Point(" + x + ", " + y + ")";
    }
}