Java 子类初始化的疑问,感觉是大牛的人进!

来源:百度知道 编辑:UC知道 时间:2024/09/24 14:24:15
本问题涉及Java底层堆内存以及栈内存的设计,如果没有合理的解释,请不要妄下判断。
给出代码,
public class Test{
public static void main(String[] args){
B b=new B();
b.run();
}
}
class A{
int i=10;
public A(){
run();
}
public void run(){
System.out.println(i);
}
}
class B extends A{
int i=100;
public B(){
run();
}
public void run(){
System.out.println(i);
}
}

结果为:
0
100
100
请给出合理解释!
如果是

A ab=new B();
ab.run();

输出结果一样

真正的难点是,是为什么第一个输出为0?
创建子类对象时,调用B的构造方法,其中第一句隐含调用super();而这一句才是真正关键,这就考虑到,创建对象的步骤。这个时候已经分配了B类的 i 的空间,要不然执行super()中的run()时,肯定实际上是B的run(),而此时B的i虽已经分配了空间,却没有初始化,所以采用实例变量的默认值 0, 所以我的最终疑问是,在调用子类构造方法之前 究竟发生了什么,哪些空间被分配了,哪些空间又被初始化了?这些步骤的先后顺序是什么?
我所知道的顺序只有 分配父静->初始父静,->分配子静->初始子静>-分配父非静-父构造->初始父非静(在构造方法中或其调用的方法中)->子非静-子构造->初始子非静(在构造方法中或其调用的方法中),不知道大牛们对于我对非静态属性的初始化的理解有无异议? 我认为真正的原

B b = new B();
∵B继承A
∴B的run()方法覆盖了A的run()方法
∵new B()首先隐含调用了super()
∴执行了public A(){run();}方法
∵这个run()方法是子类B的方法
又∵这时候子类B的构造方法还没有执行
∴子类B的属性i还没有值
∴现在i是默认的0

楼主的困惑就在于没有理清那个输出0的run()方法是谁的run(),你分别在两个run()方法里加入System.out.println("class A");System.out.println("class B")就能明白了。你可能会发现没打印class A。

1.B b=new B();因为B继承A, 因此会先调用A的无参构造函数run();.但接下来就不是多态了, 因为多态的产生条件之一是父类引用指向子类对象.比如说楼主说的A ab=new B();. 此时A构造函数中的run方法调用的是B中的run方法,(这应该是由于继承和重写run方法的原因).此时B对象已经生成,不然的话是怎么调用B中的run方法的呢?实际上, 在调用B的构造方法之前, 系统会先为该对象分配内存空间并执行默认初始化(是默认).也就是说,当系统开始执行构造方法的执行体之前,系统已经创建了一个B类对象,只是这个对象还不能被外部程序访问.当然,在随后的调用A类构造方法的时候,也会产生一个A类对象,不过,这个A类对象是包含在B类对象里面的,整个对象的实质是B类对象.这也就是为什么调用的是B类对象的run方法而不是A类对象的run方法了.在B类对象调用run方法后,它要执行run方法,打印i值,而由于此时B类对象还不能被外部程序访问,所以此时i值不是100,而是默认初始化时的值0.因此打印出的是默认值0.

2.随后执行B的构造函数,由于构造方法的 执行体执行结束后,B类对象会作为构造方法的返回值返回.这个对象可以对外部程序访问.此时B类对象的属性i已经被赋值为100了,因此执行run的时候打印输出为100

3.随后执行b.run();打印出当前i值为100
所以除了第一步,我与liush