问题说明
求以下输出:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25public class Window {
Window(int marker){
System.out.println("Window(" + marker + ")");
}
}
public class House {
Window w1 = new Window(1);
House(){
System.out.println("House()");
w3 = new Window(33);
}
Window w2 = new Window(2);
void f(){
System.out.println("f()");
}
Window w3 = new Window(3);
}
public class OrderOfInitialization {
public static void main(String[] args) {
House h = new House();
h.f();
}
}
错误答案
1 | House() |
错误思路
从主函数出发创建一个House类对象h,构造函数会在创建对象的同时执行,找到House类的构造函数,先输出House(),因为w3是个没被声明的变量,所以先运行了Window w3 = new Window(3)
输出Window(3)然后再完成构造函数,输出Window(33),然后按照顺序完成以下输出。
正确答案
1 | Window(1) |
分析
在类的内部,变量定义的先后顺序决定了初始化的顺序。即使变量定义散布于方法定义之间,它们仍旧会在任何方法(包括构造器)被调用之前得到初始化。在House类中,故意把几个Window对象的定义散布到各处,以整明它们全都会在调用构造器或其他方法之前得到初始化。此外,w3在构造期内再次被初始化。
总结
其实在意识到如果构造函数先执行的话w3 = new Window(33)
这一句中的w3是一个没有被声明的变量时就觉得不太对,但还是没有找出正确的思路。后来在尝试别的情况时把这一句注释掉了,输出顺序还是不变,只是少了一句Window(33)。
2019-09-06
今天在学习多态的过程中又遇到了一个初始化顺序的问题,代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27class Glyph {
public void draw() {
System.out.println("Glyph.draw()");
}
Glyph() {
System.out.println("Glyph() before draw()");
draw();
System.out.println("Glyph() after draw()");
}
}
class RoundGlyph extends Glyph {
private int radius = 1;
RoundGlyph(int r) {
radius = r;
System.out.println("RoundGlyph.RoundGlyph().radius = " + radius);
}
public void draw() {
System.out.println("RoundGlyph.draw().radius = " + radius);
}
}
public class PolyConstructors {
public static void main(String[] args) {
new RoundGlyph(5);
}
}
错误答案:1
2
3
4Glyph() before draw()
RoundGlyph.draw().radius = 1
Glyph() after draw()
RoundGlyph.RoundGlyph.radius = 5
错误思路:
首先调用基类构造器,考虑到新创建的对象是RoundGlyph()类的,我认为此处的draw()方法应该已经被覆盖,应该调用子类的draw方法,radius的值在子类中被初始化为1,因此得到了第二行的输出。最后再调用子类构造器的主体,输出第四行。
正确答案:1
2
3
4Glyph() before draw()
RoundGlyph.draw().radius = 0
Glyph() after draw()
RoundGlyph.RoundGlyph.radius = 5
正确思路:除了第二行radius的值以外都对了。初始化的实际过程是:1)在其他任何事物发生之前,将分配给对象的存储空间初始化为二进制的0。2)如前所述那样调用基类构造器。此时,调用被覆盖后的draw()方法(需要在调用RoundGlyph构造器之前调用),由于步骤1的缘故,我们此时会发现radius的值为0。3)按照声明的顺序调用成员的初始化方法。4)调用导出类的构造器主体。