静态方法不能被重写,只能被隐藏;调用取决于引用类型而非实际对象类型,编译期静态绑定,与多态无关。

静态方法不能被重写,只能被隐藏
Java里static方法不参与多态,子类声明同签名的static方法时,不是重写(override),而是隐藏(hiding)。调用哪个版本,完全取决于**引用类型**,而不是实际对象类型。
常见错误现象:Parent p = new Child(); p.staticMethod(); 调用的是Parent.staticMethod(),哪怕Child里写了同名static方法——很多人误以为这是“重写失败”,其实是设计如此。
- 使用场景:工具类继承、配置类扩展、避免实例化但需分层提供默认行为
- 参数差异:签名必须完全一致(包括返回类型、参数列表、异常声明),否则是独立方法,不构成隐藏
- 性能影响:无虚方法表查找开销,比实例方法调用略快,但和继承无关,纯编译期绑定
- 兼容性注意:JDK 8+ 允许接口中定义
static方法,但接口static方法不能被类实现或隐藏;只有类继承链中才发生隐藏
实例方法重写必须满足协变返回与访问权限约束
非static、非private、非final的实例方法才能被重写。重写不是简单“名字一样”,而是一套编译期校验规则。
典型翻车点:@Override加了但编译报错,或者没加却意外“覆盖”了父类方法(比如父类方法是protected,子类写成public没问题,但写成private就变成新方法了)。
立即学习“Java免费学习笔记(深入)”;
- 返回类型可以是父类返回类型的子类型(协变),如父类返回
Object,子类可返回String - 子类方法访问修饰符不能比父类更严格(
public→protected不合法,protected→public合法) - 子类不能抛出比父类方法更多或更宽泛的检查异常(
IOException→Exception报错,但IOException→FileNotFoundException可以) -
private方法不能被重写(子类同名方法只是新方法),final方法编译直接拒绝
如何一眼区分是隐藏还是重写
看方法有没有static修饰符,再看调用方式。没有歧义的方法,就不存在“误判”问题。
最容易混淆的场景:子类里把父类实例方法改成static,或者反过来。这时根本不是重写/隐藏关系,而是两个完全无关的方法——IDE 通常会标黄警告“method does not override or hide a method from a supertype”。
- 判断依据只有一条:方法签名是否匹配 + 是否含
static - 如果父类是
static,子类同签名也是static→ 隐藏 - 如果父类是非
static,子类同签名也是非static→ 重写(前提是满足上一条的约束) - 父
static/ 子非static,或父非static/ 子static→ 编译通过,但两者毫无关系,只是巧合同名
运行时行为差异:变量类型决定调用路径
这是最常被忽略的底层逻辑。重写走的是动态绑定(JVM查对象实际类的虚方法表),隐藏走的是静态绑定(编译器按变量声明类型决定)。
示例:Parent p = new Child(); p.doWork(); —— 若doWork()是实例方法,执行Child.doWork();若是static方法,则执行Parent.doWork(),哪怕p指向Child实例也无用。
- 这个区别在反射中依然成立:
p.getClass().getMethod("doWork")拿到的是Child的实例方法,但Parent.class.getMethod("doWork")才能拿到static版本 - 单元测试里容易漏掉:用父类引用测试子类行为时,如果误用了
static,断言会莫名其妙失败 - Spring等框架依赖动态代理时,
static方法天然无法被AOP拦截,因为压根不进代理链
真正麻烦的从来不是记概念,而是当父类方法从实例改成static(或反之)时,所有下游调用点的语义全变了,而且编译器不一定报错——得靠人盯住每个调用处的变量类型。








