子类重写父类方法时访问权限只能持平或放宽,不可收窄——这是java编译期强制的硬性规则,旨在保障多态调用安全和里氏替换原则;父类public方法子类必须public,protected可升为public,包私有可升为protected或public,private不可重写。

子类重写父类方法时,访问权限只能放大或保持不变,不能缩小——这不是编码风格问题,而是Java为保障多态安全设下的硬性规则。核心就一句话:父类对外承诺了“谁可以调用这个方法”,子类必须遵守甚至做得更好,不能偷偷收紧。
为什么不能缩小?因为多态调用会直接失效
Java允许用父类类型引用指向子类对象,比如 Animal a = new Dog();,然后调用 a.sound();。此时实际执行的是 Dog 重写后的方法。但编译器只认 Animal 类中该方法的声明权限。如果 Animal.sound() 是 public,而 Dog 把它改成 protected,外部代码通过 Animal 引用调用就会在编译期报错:Cannot reduce the visibility——不是运行时报异常,是根本编译不过。
- 父类声明 public → 子类必须 public(改 protected 或包私有,立刻报错)
- 父类是 protected → 子类可选 protected 或 public,但不能降为包私有或 private
- 父类是包私有(没写修饰符)→ 子类可升为 protected 或 public,但不能是 private
- 父类是 private → 根本不参与继承,子类同名方法只是独立定义,不算重写
为什么允许放大?这是安全的增强
权限放大不会破坏原有契约,反而让子类方法更易被使用。比如父类方法是 protected,子类重写为 public,原来能通过父类引用调用的地方依然能调,包外代码也能直接用子类引用调用了——这不违反里氏替换原则,只是扩展了能力边界。
- protected → public 合法,且常见于框架抽象类到具体实现类的演进
- 包私有 → protected 或 public 也合法,适合把内部工具方法开放给继承链下游
- 但注意:public 不能再扩大——它已是最高权限,保持即可
初学者最容易踩的坑
很多问题不是写错了,而是“没写对”。IDE 自动生成 @Override 方法时默认加 public,手动删掉后毫无提示;单元测试又常和类同包运行,包内调用一切正常,上线后容器(如 Spring、Tomcat)通过反射跨包调用就失败,报 IllegalAccessError 或静默跳过。
- 接口方法隐式 public,实现类中 @Override 必须显式写 public
- Servlet、Filter 等规范类的方法,即使父类没重复写 public,你也得补上
- 以为 protected “更安全”而主动降级,结果编译直接拦住——这不是建议,是语言铁律
本质是守住里氏替换原则
所有规则都服务于一个目标:让 Animal a = new Dog(); a.sound(); 这样的调用永远成立、行为可预期。权限收窄会让父类引用“看不见”子类方法,等于撕毁契约;权限放大则是在契约基础上做增量,完全安全。Java选择在编译期就拦截违规操作,而不是等运行时崩溃。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











