今天看到一帖子。
原文:
http://community.youkuaiyun.com/Expert/topic/4657/4657161.xml?temp=.0378992
1,B构造器构造对象的时候先调用A的构造器,
2,而A的构造器则会调用A的draw方法,
3,而B又是实现A的子类,则A的draw方法会调用B的draw方法,这时B又要调用A的构造器,
这样一来不是死循环了?不知道我这样理解对不对,
-----------------------------------------
雨痕解答:
好像不少人都有这样的疑问,其实该问题在《Thinking in Java》中有记述(《Java编程思想》第二版,239页)。为了便于理解,我采用C#来说明,并添加一点代码。
输出:
A Constructor...
10
Draw...
B Constructor...
15
仔细思考输出结果,你明白了什么?OK,我们接着看看反编译的IL代码。
有没有发现在B构造方法中,调用A构造方法前,就已经对x进行赋值操作。为什么是这样?难道不是先调用A构造方法吗?其实原贴的作者和很多人都犯了一个错误,那就是构造方法究竟是用来创建对象,还是初始化对象。
严格意义上来说创建一个对象的步骤是:
1. 分配对象空间。
2. 创建该对象成员,包括未"初始化"的字段和方法表指针等。
3. 调用构造方法初始化对象成员。
一个 "new B()" 语句实际上包含了上述三个步骤,也就是说在调用构造方法前,未初始化的对象已经存在,其成员空间和成员引用也已经分配,只不过为了安全原因,构造方法初始化失败,CLR就认为创建对象失败而已。既然B对象已经存在,A()中通过callvirt指令调用B.Test也自然不会调用其构造方法了。理解了这点,我们就很容易明白原贴不会引起死循环的原因了。
原文:
http://community.youkuaiyun.com/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里构造函数的递归调用过程,
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();
}
}
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();
}
}
{
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
}
{
.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也自然不会调用其构造方法了。理解了这点,我们就很容易明白原贴不会引起死循环的原因了。