子类重写父类方法时访问权限不能降低,这是为保障多态合法性和里氏替换原则;编译器强制执行该契约,确保父类声明的接口能力在子类中完整保留。

这是为了保障多态调用的合法性与程序行为的一致性。父类方法对外暴露的可见范围,是所有子类必须共同遵守的契约——子类可以更开放,但绝不能收窄。
核心原因:里氏替换原则(LSP)
任何能用父类对象的地方,都必须能无缝替换成子类对象,且不改变原有逻辑。如果子类把一个 public 方法改成 protected,那么外部代码通过父类引用调用该方法时就会编译失败:
- 父类声明:
public void save() - 子类错误重写:
protected void save() - 调用处:
Parent p = new Child(); p.save();→ 编译报错:Cannot reduce the visibility
权限等级不可逆,编译器提前拦截
Java 中访问权限严格按宽严排序:public > protected > 默认(包私有)> private。这个顺序决定了重写时只能持平或向上调整:
- 父类是 public → 子类只能是 public
- 父类是 protected → 子类可选 protected 或 public,但不能是默认或 private
- 父类是默认(包私有)→ 子类可为默认、protected 或 public(前提是子类与父类在同一包)
- 父类是 private → 根本不可重写,子类同名方法只是独立声明,与继承无关
不是运行时问题,而是设计契约的硬约束
这个限制在编译期强制执行,不是靠文档约定或开发自觉。它确保了:
- 父类定义的接口能力,子类必须完整提供
- 上层模块无需关心具体子类类型,仅依赖父类声明即可安全调用
- 避免因权限收缩导致的“方法突然不可见”,破坏封装边界和调用链稳定性
特殊情形不适用该规则
以下两类方法不参与重写,因此不受此权限限制:
- static 方法:属于类级别,子类中同名方法是“隐藏”(hiding),非重写
-
final 方法:明确禁止重写,尝试重写会直接报
method is final











