当变量的编译时类型和运行时类型不同时,通过该变量访问它引用的对象的实例变量时,该实例变量的值由声明该变量的类型决定。但通过该变量调用它引用的对象的实例方法时,该方法行为将由它实际所引用的对象来决定。
public class StudyVisitorSonVar {
public static void main(String[] args) {
new Cricle("圆形");
}
}
class Shap{
public String desc;
public Shap() {
//该变量访问它引用的对象的实例变量时,该实例变量的值由声明该变量的类型即Shap决定
//通过该变量调用它引用的对象的实例方法时,该方法行为将由它实际所引用的对象即Cricle来决定
this.desc = getDesc();
}
public String getDesc() {
this.desc = "形状";
System.out.println("Shap 父类的描述为:" + this.desc);
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
}
class Cricle extends Shap{
public String desc;
public Cricle(String desc) {
this.desc = desc;
}
@Override
public String getDesc() {
System.out.println("Cricle 子类的描述为:" + this.desc);
return this.desc;
}
}
运行结果为:
Cricle 子类的描述为:null
之所以会出现这种结果是因为调用子类构造器前会隐式调用父类无参构造器,this.desc这个this虽然代表Cricle对象,但它却位于Shap构造器中,它的编译时类型是Shap,而它实际引用一个Cricle对象。
当变量的编译时类型和运行时类型不同时,通过该变量访问它引用的对象的实例变量时,该实例变量的值由声明该变量的类型决定,但通过该变量调用它引用的对象的实例方法时,该方法行为将由它实际所引用的对象来决定。这时候就会调用子类的getDesc()方法,因为子类的desc成员变量还没有被初始化所以输出结果为null。
所以需要注意如果父类构造器调用了被子类重写的方法,且通过子类构造器来创建子类对象,不管是显式还是隐式调用了这个父类构造器就会导致子类的重写方法在子类构造器的所有代码之前被执行,从而导致子类的重写方法访问不到子类的实例变量值的情形