Java 中的继承与多态是面向对象编程的核心机制。通过 extends 关键字实现类之间的继承关系,通过 @Override 注解明确标识方法重写行为,二者协同工作,使代码具备更强的复用性与扩展性。
理解继承:用 extends 构建父子关系
创建一个父类(也称超类或基类),定义通用属性和行为。
定义子类时使用 extends 关键字继承父类,自动获得其非私有成员。
// 文件: Animal.java
public class Animal {
protected String name;
public Animal(String name) {
this.name = name;
}
public void speak() {
System.out.println("Animal makes a sound");
}
}
// 文件: Dog.java
public class Dog extends Animal {
public Dog(String name) {
super(name); // 调用父类构造器
}
}
注意:
- 子类只能直接继承一个父类(Java 不支持多继承)。
- 使用
super(...)调用父类构造方法,且必须放在子类构造器的第一行。 protected修饰的成员可在子类中直接访问,比private更开放,比public更安全。
实现多态:用 @Override 重写方法
多态指同一个方法调用在不同对象上表现出不同行为。要实现这一点,子类需重写(override)父类的方法。
在子类中重新定义父类已有的方法,方法名、参数列表、返回类型必须完全一致。
添加 @Override 注解,让编译器检查是否真的在重写父类方法(防止拼写错误等)。
// 继续 Dog.java
public class Dog extends Animal {
public Dog(String name) {
super(name);
}
@Override
public void speak() {
System.out.println(name + " says Woof!");
}
}
测试多态效果:
// 文件: Main.java
public class Main {
public static void main(String[] args) {
Animal myPet = new Dog("Buddy");
myPet.speak(); // 输出: Buddy says Woof!
}
}
尽管变量 myPet 声明为 Animal 类型,但实际指向的是 Dog 对象。运行时 JVM 会自动调用 Dog 类中重写后的 speak() 方法——这就是多态。
@Override 的三大作用
- 明确意图:告诉其他开发者“此处是有意重写父类方法”。
- 编译检查:如果方法签名与父类不匹配(比如拼错方法名),编译器会报错。
- 防止意外遮蔽:避免因方法名相同但参数不同而无意中定义了新方法(即重载而非重写)。
删除 @Override 注解不会影响程序运行,但会失去上述安全保障。始终保留它。
常见错误与避坑指南
-
试图重写
private或static方法private方法对子类不可见,无法重写。static方法属于类而非实例,重写无效(实际是隐藏)。
确保被重写的方法是非private、非static、非final的。
-
构造器不能被重写
构造器名字必须与类名相同,因此不存在“重写构造器”的概念。子类通过super(...)调用父类构造器。 -
返回类型协变例外
正常情况下重写要求返回类型完全相同,但有一个例外:如果父类方法返回某个类A,子类可返回A的子类。
例如:class Parent { Object getValue() { return new Object(); } } class Child extends Parent { @Override String getValue() { return "hello"; } // 合法!String 是 Object 的子类 }
设计建议:何时使用继承?
| 场景 | 是否推荐继承 |
|---|---|
| 子类确实是父类的一种(如 Dog 是 Animal) | ✅ 强烈推荐 |
| 仅为复用代码而无“is-a”关系 | ❌ 应优先考虑组合(has-a) |
| 需要多继承行为 | ❌ 改用接口(implements) |
记住:继承表达的是“是一个”(is-a)关系,不是“用得到”就继承。滥用继承会导致类层次臃肿、难以维护。
完整示例:动物叫声系统
// Animal.java
public class Animal {
protected String name;
public Animal(String name) { this.name = name; }
public void speak() { System.out.println("..."); }
}
// Cat.java
public class Cat extends Animal {
public Cat(String name) { super(name); }
@Override
public void speak() { System.out.println(name + " says Meow!"); }
}
// Bird.java
public class Bird extends Animal {
public Bird(String name) { super(name); }
@Override
public void speak() { System.out.println(name + " chirps!"); }
}
// Main.java
public class Main {
public static void main(String[] args) {
Animal[] animals = {
new Dog("Rex"),
new Cat("Whiskers"),
new Bird("Tweety")
};
for (Animal a : animals) {
a.speak(); // 多态:各自调用重写后的方法
}
}
}
运行结果:
Rex says Woof!
Whiskers says Meow!
Tweety chirps!
利用数组存储不同子类对象,统一调用 speak(),无需关心具体类型——这正是多态的价值所在。

暂无评论,快来抢沙发吧!