一个关于构造方法的问题

今天看到一帖子。

原文:
http://community.csdn.net/Expert/topic/4657/4657161.xml?temp=.0378992
Abstract class A{
  public A(){draw();}
  abstract draw();
}

class B extends A{
  public B(){}
  void draw(){}
}

public class Test{
  public static void main(String[] args){
    B b=new B();
  }
}
根据java里构造函数的递归调用过程,
1,B构造器构造对象的时候先调用A的构造器,
2,而A的构造器则会调用A的draw方法,
3,而B又是实现A的子类,则A的draw方法会调用B的draw方法,这时B又要调用A的构造器,
这样一来不是死循环了?不知道我这样理解对不对,

-----------------------------------------

雨痕解答:

好像不少人都有这样的疑问,其实该问题在《Thinking in Java》中有记述(《Java编程思想》第二版,239页)。为了便于理解,我采用C#来说明,并添加一点代码。
abstract class A
{
  public A()
  {
    Console.WriteLine("A Constructor...");
    Draw();
  }

  public abstract void Draw();
}

class B : A
{
  private int x = 10;

  public B()
  {
    Console.WriteLine("B Constructor...");
    x = 15;
    Console.WriteLine(x);
  }

  public override void Draw()
  {
    Console.WriteLine(x);
    Console.WriteLine("Draw...");
  }
}

class Program
{
  static void Main()
  {
    B d = new B();
  }
}

输出:
A Constructor...
10
Draw...
B Constructor...
15

仔细思考输出结果,你明白了什么?OK,我们接着看看反编译的IL代码。
.method public hidebysig specialname rtspecialname instance void .ctor() cil managed // B构造方法
{
 .maxstack 8
 L_0000: ldarg.0
 L_0001: ldc.i4.s 10
 L_0003: stfld int32 ConsoleApplication1.B::x // 对x赋值
 L_0008: ldarg.0
 L_0009: call instance void ConsoleApplication1.A::.ctor() // 调用A构造方法
 L_000e: nop
 L_000f: nop
 L_0010: ldstr "B Constructor..."
 L_0015: call void [mscorlib]System.Console::WriteLine(string)
 L_001a: nop
 L_001b: ldarg.0
 L_001c: ldc.i4.s 15
 L_001e: stfld int32 ConsoleApplication1.B::x
 L_0023: ldarg.0
 L_0024: ldfld int32 ConsoleApplication1.B::x
 L_0029: call void [mscorlib]System.Console::WriteLine(int32)
 L_002e: nop
 L_002f: nop
 L_0030: ret
}

.method public hidebysig specialname rtspecialname instance void .ctor() cil managed // A构造方法
{
 .maxstack 8
 L_0000: ldarg.0
 L_0001: call instance void object::.ctor()
 L_0006: nop
 L_0007: nop
 L_0008: ldstr "A Constructor..."
 L_000d: call void [mscorlib]System.Console::WriteLine(string)
 L_0012: nop
 L_0013: ldarg.0
 L_0014: callvirt instance void ConsoleApplication1.A::Draw()
 L_0019: nop
 L_001a: nop
 L_001b: ret
}

有没有发现在B构造方法中,调用A构造方法前,就已经对x进行赋值操作。为什么是这样?难道不是先调用A构造方法吗?其实原贴的作者和很多人都犯了一个错误,那就是构造方法究竟是用来创建对象,还是初始化对象。

严格意义上来说创建一个对象的步骤是:
1. 分配对象空间。
2. 创建该对象成员,包括未"初始化"的字段和方法表指针等。
3. 调用构造方法初始化对象成员。

一个 "new B()" 语句实际上包含了上述三个步骤,也就是说在调用构造方法前,未初始化的对象已经存在,其成员空间和成员引用也已经分配,只不过为了安全原因,构造方法初始化失败,CLR就认为创建对象失败而已。既然B对象已经存在,A()中通过callvirt指令调用B.Test也自然不会调用其构造方法了。理解了这点,我们就很容易明白原贴不会引起死循环的原因了。  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值