字段访问走静态绑定(编译时看声明类型),方法调用走动态绑定(运行时看实际类型);Java不支持字段多态,子类同名字段仅隐藏父类字段,而非重写。

为什么调用的是子类方法,但字段访问的却是父类的?
这是初学者最常卡住的地方:明明 Animal a = new Dog();,a.speak() 调的是 Dog.speak(),可 a.name 却是父类定义的值,甚至子类重写了同名字段也无效。
根本原因在于——方法调用走动态绑定(运行时看实际类型),字段访问走静态绑定(编译时看声明类型)。Java 不支持字段多态。
- 字段不是虚的,不会被“重写”,子类里声明同名字段只是隐藏(shadowing)父类字段
- 如果想让字段行为一致,必须用 getter 方法:把
name改成getName(),并在子类中重写它 - IDE 通常会警告“field hides another field”,别忽略这个提示
父类引用能调用子类新增的方法吗?
不能。比如 Animal a = new Dog();,即使 Dog 有 fetch() 方法,a.fetch() 编译直接报错:cannot resolve symbol fetch。
因为编译器只认 a 的声明类型(Animal),它不知道背后是不是 Dog。这和多态无关,是类型系统的基本规则。
立即学习“Java免费学习笔记(深入)”;
- 想调用子类特有方法,必须强制转型:
((Dog) a).fetch() - 但转型前务必用
instanceof检查,否则可能抛ClassCastException - 过度依赖转型往往说明设计有问题——考虑是否该把方法上提到父类,或用策略/组合替代继承
构造器里调用被重写的方法为什么危险?
在父类构造器中调用一个可被子类重写的方法(如 this.init()),而子类恰好重写了它,结果会出人意料:方法确实执行了子类版本,但此时子类字段还未初始化(仍是默认值)。
class Parent {
String value = "parent";
Parent() {
init(); // 这里会调到 Child.init()
}
void init() { System.out.println("Parent init"); }
}
class Child extends Parent {
String value = "child";
Child() { super(); }
@Override
void init() { System.out.println(value); } // 打印 null!
}
这是因为子类构造器还没开始执行,value 字段尚未赋值。
- 永远避免在构造器中调用
protected或public方法 - 如果必须初始化,用
final方法(无法被重写)或静态工厂模式绕过 - 这个陷阱不报错也不警告,但行为诡异,调试起来特别费时间










