首先看这个:
Super sup = new Sub();
这是sup变量包含两个方面:
它定义的类型是Super
它实际上引用了Sub的实例(对象)
定义的类型是给编译器在编译阶段参考的(用于编译时 Debug 和分配好成员变量的地址等);而实际的引用是在运行时通过调用真实引用的对象的方法实现多态的。
所以sup.field在编译时就确定为Super#field的地址,所以为0;
而sup.getField()在运行时调用Sub#getField(), 所以是得到Sub#field的1.
PS. 你在读这本书时,通常不能指望一次读懂。能看懂多少就看多少,看多几次。不过有个很好的官方教程 The Java Tutorials, 这个教程我觉得是学 Java 最好的资料,就看你习不习惯读英文。
再看一个更加能够说明问题的例子Demo:
class Dad {
public int field = 6666;
public int getField() {
return field;
}
}
class Son extends Dad {
public int field = 9999;
public int getField() {
return field;
}
}
public class Main {
public static void main(String[] args) {
Dad dd = new Dad();
Dad ds = new Son();
Son ss = new Son();
// java.lang.ClassCastException: Dad cannot be cast to Son
// Son sd = (Son) new Dad();
// 如果直接访问成员变量(field),
// 它的值由对象引用(dd, ds, ss)的类型决定,
// 而且它的地址在编译期间就已经分配
System.out.println("Dad new Dad(): " + dd.field); // 6666
System.out.println("Dad new Son(): " + ds.field); // 6666
System.out.println("Son new Son(): " + ss.field); // 9999
// 但是如果通过方法间接访问,
// 那么它得到的值由真实的对象决定,与其定义的类型无关
// 而且这是在运行时才能决定的
System.out.println("Dad new Dad(): " + dd.getField()); // 6666
System.out.println("Dad new Son(): " + ds.getField()); // 9999
System.out.println("Son new Son(): " + ss.getField()); // 9999
// 这种不一致性并不是不能避免,反而是为了提高效率而有意为之的
// 继续放大招... 注意同一个对象转型前后发生的变化
// 转型前:9999 转型后:6666
System.out.println("转型前:" + ss.field + " 转型后:" + ((Dad) ss).field);
// 转型前:6666 转型后:9999
System.out.println("转型前:" + ds.field + " 转型后:" + ((Son) ds).field);
System.out.println(ss.field == ((Dad) ss).field); // false
System.out.println(ds.field == ((Son) ds).field); // false
// 这说明了,不要直接访问成员变量!!!!
// 还有一些重要的问题,你慢慢再掌握,:))
}
}