咱们知道,父类是不能访问子类的实例变量的。但这里有一个特殊的案例java
class Base { private int i = 2; public Base(){ System.out.println(this.i); this.display(); //System.out.println(this.getClass()); //this.test(); } public void display(){ System.out.println(i); } } public class Sub extends Base { private int i = 22; public Sub(){ i = 222; } public void display(){ System.out.println(i); } public static void main(String[] args) { new Test(); ① } public void test(){ } }
结果却出人意料,并非2, 2.而是2, 0.函数
分析:在①出代码,新建了Sub对象,在Sub类的构造函数中,先隐式调用Base类的无参构造函数。在分析Base的构造函数以前,咱们首先要明白三点:this
1)Java类的对象并非由构造函数建立的,而是在构造函数赋初值以前,就已经给类成员变量分配了内存空间,而且内存内的值为空值。并且类中的定义时即赋值的成员变量和非静态代码块中初始化元素,都是先分配内存(存储默认值),而后在归并到构造函数中赋初始值,并按前后顺序排列在构造函数本来的代码以前。code
2)关于子类与父类有多个同名实例变量的问题,要知道,子类的实例变量并不能覆盖父类的同名实例变量。因此在实例化Sub类时,要先分配两块内存空间,用来存储父类的i和子类的i,这时两个i的值都为0。对象
3)当this在构造器中时,表明正在初始化的对象。从源代码来看,this在Base的构造器中执行。可是实际上,如今正在初始化的Java类是Sub,由于是在Sub的构造器中隐式地调用了父类的默认构造器,因此this是Sub类,这一点能够从this.getClass()验证。内存
那么问题来了,this表明Sub类,但this.i却输出2,而this.display()却输出0。get
这是由于this位于Base的构造器中,它的编译类型是Base,可是它引用的对象则是一个Sub类型,即它的运行时类型是一个Sub类型,这一点能够由this.test()验证,由于这回编译不经过。编译
总结:当变量的编译时类型和运行时类型不一样时,当经过该变量来访问它所引用的对象的实例变量时,该实例变量的值由声明它的类型决定,但当经过该变量来访问它所引用对象的实例方法时,该方法行为将由它所引用的对象类型决定。class
因此this.i的值为Base中的i,this.display()为Sub中的方法,i为0.test