内容参考《疯狂Java程序员的基本修养》李刚 著
一、情况描述
子类的方法可以访问父类的实例变量,是因为子类继承父类会获得父类的成员变量和方法;
但父类的方法不能访问子类的实例变量,因为子类不知道它将被哪个子类继承;
但在极端情况下,可能出现父类访问子类变量的情况。
代码说明:
Base 类:
package com.tide.test;
/**
* Created by wengliemiao on 16/3/31.
*/
public class Base {
private int i = 2;
public Base() {
System.out.println("Base 无参构造器");
this.display();
}
public void display() {
System.out.println("Base display: " + i);
}
}
Derived 类:
package com.tide.test;
/**
* Created by wengliemiao on 16/3/31.
*/
public class Derived extends Base {
private int i = 22;
public Derived() {
System.out.println("Derived 无参构造器");
i = 222;
}
public void display() {
System.out.println("Derived display: " + i);
}
public static void main(String[] args) {
new Derived(); // (1)
}
}
输出结果:
说明:
1. Java 对象不是由构造器创建的,构造器只是负责对Java对象实例变量执行初始化。即在构造器之前,该对象所占的内存已经被分配出来了,且内存中的值默认为空值(基本类型变量默认为0或false,引用类型变量默认为null);
2. 当程序调用(1)处代码时,系统会先为Derived对象分配内存空间。此时系统内存为Derived对象分配两块内存:Derived 类定义的变量 i 和 Base 类定义的变量 i。
二、分析
由于定义变量时的初始化顺序在构造器初始化之前(参看:Java变量的初始化时机),因此Base 类构造器相当于:
public Base() {
i = 2;
this.display();
}
输出 i 的值:
public Base() {
System.out.println("Base 无参构造器, i = " + this.i);
this.display();
}
输出为:
此处 this 代表谁?
当 this 在构造器中时,this 代表正在初始化的Java对象。从代码中可看出 Derived 类的构造器隐式调用了 Base 类的构造器,因此 this 是 Derived 对象。为Derived类增加一个 sub() 方法。
代码验证如下:
public Base() {
System.out.println("Base 无参构造器, i = " + this.i);
System.out.println(this.getClass());
this.display();
// this.sub(); // this 的编译时类型是 Base,因此不能调用sub() 方法
}
输出结果为:
输出 class com.tide.test.Derived 说明 this 引用代表的是 Derived 对象;程序无法调用 sub() 方法,说明 this 的编译时类型是 Base 对象。
三、结论
- 当变量的编译时类型和运行时类型不同时,通过该变量访问它引用的对象的实例变量时,该实例变量的值由声明该变量的类型决定;
- 通过该变量调用引用的对象的实例方法时,该方法行为由它实际所引用的对象决定。
因此,程序访问 this.i 时,将会访问 Base 类中的 i,即输出 2;执行 this.display() 时,实际表现出 Derived 对象的行为,即输出 Derived 对象的实例变量 i,结果为 0.