在 Java 里,“重写”(override) 不是想怎么改就怎么改,必须遵守 Java 语言规范 §8.4.8 的刚性规则。下面用一句话先给结论,再逐条拆开说。
一句话结论
“签名、返回类型、受检异常的可抛出范围” 只能更宽松或保持不变;可见性只能更高或相等;final/static/private 方法根本不能碰;其余实现代码随便你写。”
---
1. 方法名
必须一字不差地保持相同,否则就是重载(overload)而非重写。
2. 参数列表(签名)
必须完全相同(个数、顺序、类型)。
允许加泛型型参,但擦除后也必须与父类一致(桥方法由编译器自动生成)。
3. 返回类型
从 Java 5 起支持 协变返回类型(covariant return):
子类方法的返回类型可以是父类方法返回类型的子类型。
例:
```java
class A { Number m() { ... } }
class B extends A { @Override Integer m() { ... } } // OK
```
4. 受检异常(checked exception)
子类方法不允许抛出比父类方法声明更宽泛的受检异常,可以:
- 不抛任何受检异常;
- 抛出父类方法声明异常的子类型;
- 抛出更少的异常。
运行时异常(RuntimeException/Error)随便抛,不受限制。
5. 访问修饰符(visibility)
只能放大或相等,不能缩小:
`protected` → `public` ✔
`public` → `protected` ✘
`package-private` → `protected/public` ✔
`private` 方法对外不可见,不存在override问题。
6. final / static / private
- `final` 方法:禁止重写。
- `static` 方法:属于类,不能被重写,只能被隐藏(hide)。
- `private` 方法:对外不可见,子类写同名方法算“新建”,与父类无关。
7. 注解
`@Override` 不是必须,但强烈建议写上,编译器会帮你校验上述规则。
其他注解(如 `@Deprecated`、`@NonNull`)随意加,不影响重写语义。
8. 实现代码
方法体想怎么写就怎么写,只要符合上述约束即可。
---
速记口诀
“名参不变,返回可窄,异常可小,可见可大,final/static/private 别碰它。”
也就是说返回类型 跟异常要 更加明确?
对,但要把“明确”换成 Java 的术语才严谨:
- 返回类型——可以更具体(covariant)
父类返回 `Object`,子类可以返回 `String`;子类的返回值类型必须是父类返回类型的子类型。
- 受检异常——可以更窄(fewer or sub-types)
父类抛出 `Exception`,子类可以不抛、或只抛 `IOException`;子类方法声明的受检异常必须是父类声明异常的子类型或更少。
一句话:
“只能比父类更‘小’或相等,决不能更‘大’。”
而修饰符 范围更宽意味着啥,怎么理解,为啥别的都是变窄,它却是变宽呢?
因为 “可见性” 关系到调用者能不能把子类对象当成父类对象用——这是多态的立身之本。
如果子类把可见性收窄,就会出现“在父类里能调到的方法,换到子类对象突然调不到了”,编译期类型检查直接塌方。
所以 Java 强制:重写时只能放大或相等,绝不能缩小;别的(返回、异常)只是“契约强度”问题,而可见性是“能不能看见”的问题,一旦收窄就多态破裂,因此必须“越晚(子类)越宽”。
2503

被折叠的 条评论
为什么被折叠?



