访问子类对象的实例变量

子类的方法可以访问父类的实例变量,这是因为子类继承父类就会获得父类的成员变量和方法;但父类的方法不能访问子类的实例变量,因为父类根本无从知道被哪个子类继承,它的子类会增加怎样的成员变量。

但是在极端的情况下,可能出现父类访问子类变量的情况。

如下面的例子:

class Base{
	private int i=2;
	
	public Base(){
		this.display();
	}
	public void display(){
		System.out.println(i);
	}
}
class Derived extends Base{
	private int i=22;
	public Derived(){
		i=222;                                  //     2
	}
	public void display(){
		System.out.println(i);
	}
}
public class a{
	public static void main(String[] args){
		new Derived();                         //     1
	}
}
上面的程序的main方法里只有一行代码:new Derived();,这行代码将会调用Derived里的构造器。由于Derived类继承了Base父类,而且Derived构造器里没有显示使用super来调用父类的构造器,因此系统将会自动调用Base类中无参数的构造器来执行初始化。

在Base类的无参数的构造器中,只是简单的调用了this.display()方法来输出实例变量i的值,那么这个程序将会输出多少呢?2?22?222?运行这个程序,会发现实际输出的结果是0.也就是说,实例变量i的值不是2,也不是22,更不是222,而是0,这看上去很奇怪。

下面将详细讲解这个程序的运行过程,从内存分配的角度来分析这个程序的输出结果,从而更好地把握程序运行的真实过程。

当程序在1行代码处创建Derived对象时,系统开始为这个Derived对象分配内存空间。需要指出的是,这个Derived对象并不是只有一个实例变量i,它将拥有两个实例变量i

为了解释这个程序,首先需要澄清一个概念:Java对象是由构造器创建的吗?很多书籍、资料中会说是的,

但实际情况是,构造器只是对Java对象实例变量执行初始化(也就是赋值),在执行构造器代码之前,该对象所占的内存已经被分配出来了,也就是为了这些实例变量都分配了内存空间,这些内存中的值都默认是空值——对于基本类型的变量,默认的空值都是0,或false,对于引用类型的变量,默认的空值都是Null。

当程序调用1行代码时,系统会先为Derived对象分配内存空间。此时系统内存需要为这个Derived对象分配两块内存空间,它们分别用于存放Derived对象的两个实例变量i,其中一个属于Base类定义的实例变量i,另一个属于Derived类定义的实例变量i,此时这两个实例变量i的值都为0。

接下来程序在执行Derived类的构造器之前,首先会执行Base类的构造器。从表面上看,Base类的构造器只有一样代码this.display();但由于Base类定义实例变量i时指定了初始值为2,因此经过编译器处理后,该构造器应该包含如下两行代码:

i=2;

this.display();

因此,程序先将Base类中定义的实例变量i赋值为2,再调用this.display();方法。此处有一个关键:this到底代表谁?

在回答这个问题之前,先进行一些简单的修改,将Base类的构造器改为如下形式。

public Base(){

System.out.println(this.i);

this.display();

}

现在,Base构造器里表面上只有两行代码,实际上应该有三行代码,如下所示:

i=2;

System.out.println(this.i);

this.display();

再次运行该程序,将看到输出2,0,看到这样的结果,可能有人会更加混乱了:此时的this到底代表谁?

在《疯狂Java讲义》中指出:当this在构造器中,this代表正在初始化的java对象,次世代情况是,从源代码来看,this位于Base()构造器内的代码,由此可见,此时的this应该是Derived对象,而不是Base对象。

现在问题又出现了,既然this引用代表了Derived对象,那怎么直接输出了this.i时会输出2呢?这是因为:这个this虽然代表了Derived对象,但它却位于Base构造器中,它的编译时类型是Base,而它的实际引用一个Derived对象。为了证明这一点,再次改写程序。

为Derived类增加一个简单的sub()方法,然后将Base构造器改为如下形式。

Public Base(){

System.out.println(this.i);

this.display();

System.out.println(this.getClass());

//因为this的编译时类型是Base,所以依然不能调用sub()方法

//this.sub();

}

上面的 程序调用了this.getClass()来获取this代表的对象的类,将看到Derived类,这表明此时this引用代表的是Derived对象。但接下来,程序通过this调用sub()方法时,则无法通过编译,这就是因为this的编译时类型是Base的缘故。

当变量编译时和运行时类型不同时,通过该变量访问它的引用的对象的实例变量时,该实例变量的值由声明该变量的类型决定。但通过该变量调用它引用的对象的实例的方法时,该方法行为将由实际所引用的对象来决定。因此,当程序访问this.i时,则实际表现出Derived对象的行为,也就是输出Derived对象的实例变量i,即0.

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值