第 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 + ")";
}
}