黑马程序员技术交流社区
标题:
父类调用子类成员变量? 挺有意思
[打印本页]
作者:
耿鑫
时间:
2012-6-19 12:51
标题:
父类调用子类成员变量? 挺有意思
本帖最后由 耿鑫 于 2012-6-19 13:55 编辑
看到一个程序题,拿出来和大家分享一下
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;
}
public void display()
{
System.out.println(i);
}
}
public class Test
{
public static void main(String[] args)
{
new Derived();
}
}
输出的结果是什么? 为什么? 最主要的是为什么 ,放到eclipse中谁都知道结果。
作者:
黄连兵
时间:
2012-6-19 13:38
http://bbs.itheima.com/thread-17286-1-1.html
这个问题前两天李盼同学已经分析过了~!
作者:
邓杰
时间:
2012-6-19 13:51
就我知道的这个问题在论坛里不下5个人问了;
i还没有来的及初始化;这个时候的i仍然是默认初始化的值;成员变量的值在对象的时候才会进行手动初始化;这个在new子类的时候去找到父类;并进行初始化;而这个时候的父类的成员变量都没有进行初始化;实际是打印的是父类的没有被初始化的值;
作者:
耿鑫
时间:
2012-6-19 13:54
好吧,既然有人分析过了 ,那么我把我的分析拿出来和大家分享一下,可能和他有出入
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; //②
}
public void display()
{
System.out.println(i);
}
}
public class Test
{
public static void main(String[] args)
{
new Derived(); //①
}
}
问输出的结果是什么?
为了清楚起见我给某些行代码扁了号,首先当程序在①行代码处创建Derived对象时,系统开始为这个Derived对象分配内存空间,需要指出的是,这个Derived对象并不是只有一个i实例变量,它将拥有两个i实例变量。
为了解释这个程序,首先需要澄清一个概念,java对象是由构造器创建的吗?很多书籍,资料中会说是,但实际情况是:构造器只负责对java对象实例变量执行初始化,在执行构造器之前,该对象所占的内存已经被分配下来了,这些内存的值都默认是空值 ------对与基本类型的变量,默认的空值是0或者是false,对于引用类型而言就是null;当程序执行①代码时,系统先为Derived对象分配内存空间,有两块内存空间分别存放Derived对象的两个i实例变量,一个是属于Base的一个是Derived的,此时这两个i实例变量的值都是0;这个可以用javap工具进行分析,很好用的一个工具。
接下来程序在执行Derived类的构造器之前,首先会执行Base类的构造器,表面上看,Base类的构造器内只有一行代码this.display();,但由于Base类定义了i实例变量时指定了初始值2,因此经过编译器处理后,该构造器应该包含如下两行代码。
i = 2;
this.display();
因此,程序现将Base类中定义的i实例变量赋值为2,再调用this.display();方法。此处有一个关键:this代表的是谁?在回答这个问题之前,先进行一个简单的修改,将Base类的构造器改为如下形式。
public Base()
{
//直接输出this.i
System.out.println(this.i);
this.display();
}
再次运行程序,将看到输出是2和0;看到这个结果,可能有人会更加混乱了,此时的this到底代表谁?
当this在构造器中时,this代表正在初始化的java对象,此时的情况是:从源代码来看,此时的this位于Base()构造器内,但这些代码实际放在Derived()构造器中执行---是Derived构造器隐式调用了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.sub();
}
上面程序调用this.getClass()来获取this代表对象的类,将看到输出的是Derived类,这表名此时this引用代表的是Derived对象,但接下来,程序通过this调用sub方法时,则无法通过编译,这就是因为this的编译时类型是Base的缘故。
当变量的编译时类型和运行时类型不同时,通过该变量访问它所引用的对象的实例变量时,该实例变量的值由声明该变量的类型决定,但通过该变量调用它引用的对象的实例方法时,该方法行为由它实际所引用的对象来决定,一次当程序访问this.i时,它将访问Base类中定义的i实例变量,也就是将输出2;但执行this.display()时则实际表现出Derived对象的行为,也就是输出Derived对象的i实例变量,即为0;
作者:
周朋飞
时间:
2012-6-26 16:26
其实你不用去网上找那么多,你也说了eclipse下一运行就知道结果了,你Debug一下就知道具体执行的步骤了,然后就知道为什么了
首先会调用子类的构造函数 第一步执行:Derived(),但是并没有进方法内部,而是直接调用父类的构造函数Base(),这时候也不会进方法内部,而是先初始化变量i的值,但是注意了:这个i是父类的i,而不是子类内部的i,子类内部的i还是0,没有被初始化,然后进父类构造函数内部,疑惑就出在这里,这要弄明白这个this指的是谁,这个问题就好理解了
Debug中 this指的是子类对象,而不是父类,所以调用的肯定是自己的disPlay方法了 ,但是子类里的i的值还是0 所以打印出来的就是0 了
作者:
周朋飞
时间:
2012-6-26 16:35
我的理解和你的是一样的,查再多资料不如自己下手试一下,我debug了之后,发现输出结果是0 主要原因就是这个this代表的不是父类自己而是子类对象自己,并且在执行new的时候 子类里面有两个变量i 一个是继承下来的 一个是自己的,当new 的时候会先到Derived()这一步,但是不会进方法体,直接调用父类的构造函数Base(),这时候用debug就能很清楚看到 this指的 是子类对象 并且里面有两个变量i 当执行到父类构造函数时,会先执行变量i赋值这一步,这个时候父类的i就变成了22,然后进父类构造函数内部,注意了,因为this指的是子类,所以当然调用的是子类的disPlay函数,但是子类这时候i还没有没赋值 ,所以会输出0
欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/)
黑马程序员IT技术论坛 X3.2