重写规则
方法重写看似简单——子类覆盖父类方法。但 Java 定义了一套严格的规则,违反任何一条都会导致编译错误。面试中也经常考察这些细节。
规则一览
| 规则 | 具体要求 |
|---|---|
| 方法名 | 必须与父类相同 |
| 参数列表 | 必须与父类完全相同(类型和个数) |
| 返回类型 | 可以是父类返回类型的子类型(协变返回) |
| 访问修饰符 | 不能比父类更严格 |
| 异常声明 | 不能抛出新的或更宽的受检异常 |
代码示例
基本规则
java
public class OverrideRulesDemo {
static class Parent {
Object method(String s) {
System.out.println("Parent method");
return null;
}
}
static class Child extends Parent {
// ✅ 重写:返回类型 String 是 Object 的子类型(协变返回)
@Override
String method(String s) {
System.out.println("Child method");
return "result";
}
}
}访问修饰符
访问权限只能扩大,不能缩小:
java
public class AccessModifierDemo {
static class Parent {
protected void display() {
System.out.println("Parent display");
}
}
static class Child extends Parent {
// ✅ protected -> protected(相同)
@Override
protected void display() {
System.out.println("Child display");
}
// ✅ protected -> public(扩大)
@Override
public void anotherMethod() {
System.out.println("Child another method");
}
// ❌ 编译错误:protected -> private(缩小)
// @Override
// private void display() {}
}
}异常声明
受检异常的范围只能缩小,不能扩大:
java
public class ExceptionRuleDemo {
static class Parent {
void method() throws RuntimeException {
System.out.println("Parent method");
}
}
static class Child extends Parent {
// ✅ 不抛出异常(缩小)
@Override
void method() {
System.out.println("Child method");
}
// ✅ 抛出更具体的异常(缩小)
@Override
void anotherMethod() throws ArithmeticException {
System.out.println("Child another method");
}
// ❌ 编译错误:throws Exception(扩大)
// @Override
// void method() throws Exception {}
}
}静态方法:隐藏而非重写
静态方法不属于对象,没有多态,所以是「隐藏」而非「重写」:
java
public class StaticMethodOverrideDemo {
static class Parent {
static void staticMethod() {
System.out.println("Parent static method");
}
void instanceMethod() {
System.out.println("Parent instance method");
}
}
static class Child extends Parent {
// ❌ 这不是重写,是隐藏
static void staticMethod() {
System.out.println("Child static method");
}
@Override
void instanceMethod() {
System.out.println("Child instance method");
}
}
public static void main(String[] args) {
Parent p = new Child();
// 静态方法:取决于引用类型(Parent),不是实际类型
p.staticMethod(); // 输出: Parent static method
// 实例方法:取决于实际类型(Child),这才叫多态
p.instanceMethod(); // 输出: Child instance method
}
}私有方法不能被重写
private 方法对子类不可见,所以子类定义同名方法是一个新的方法,不是重写:
java
public class PrivateMethodDemo {
static class Parent {
private void privateMethod() {
System.out.println("Parent private method");
}
public void callPrivate() {
privateMethod(); // 始终调用父类的版本
}
}
static class Child extends Parent {
// 这不是重写,是子类自己的新方法
private void privateMethod() {
System.out.println("Child private method");
}
}
public static void main(String[] args) {
Parent p = new Child();
p.callPrivate(); // 输出: Parent private method
}
}@Override 注解的作用
强烈建议始终加上 @Override 注解,它有两个作用:
- 编译期检查:如果方法签名写错了,编译器会报错
- 代码可读性:明确表示这是重写方法
java
public class OverrideAnnotationDemo {
static class Parent {
void method(String s) { }
}
static class Child extends Parent {
// ❌ 编译错误:如果没有 @Override,编译器不会报错
// 你以为重写了,实际上是新方法
void method(String s, int i) { }
}
static class Child2 extends Parent {
@Override
void method(String s) { } // ✅ 正确重写
}
}注意事项
- 协变返回:JDK 5 引入,子类方法可以返回父类返回类型的子类型
- 静态方法:没有重写,只有隐藏,调用时看引用类型
- 私有方法:子类不可见,不存在重写一说
- final 方法:父类用
final修饰的方法不能被重写 - 构造方法:不能被重写
