下面用阿里2013校招笔试最后的选做题作为例子:
首先,输出是:
1:i i=0 n=0
2:j i=1 n=1
3:构造块 i=2 n=2
4:构造函数 t1 i=3 n=3
5:Second i=4 n=4
6:j i=5 n=5
7:构造块 i=6 n=6
8:构造函数 Second_Builder i=7 n=7
9:s_j i=8 n=8
10:Second 构造体 i=9 n=9
11:静态块1 i=10 n=10
12:j i=11 n=11
13:构造块 i=12 n=12
14:构造函数 t2 i=13 n=13
15:静态块2 i=14 n=99
16:静态块3 i=15 n=100
17:j i=16 n=101
18:构造块 i=17 n=102
19:构造函数 init i=18 n=103
函数以及解析如下:
package basic;
/**
* 重点分析:
* 一般来说,实例化的顺序像下面这样:
* 1. 父类--静态变量
* 2. 父类--静态初始化块
* 3. 子类--静态变量
* 4. 子类--静态初始化块
* 5. 父类--变量
* 6. 父类--初始化块
* 7. 父类--构造器
* 8. 子类--变量
* 9. 子类--初始化块
* 10. 子类--构造器
*
* 但是注意!!如果静态属性实例化了自身,那么将不会调用他后面的静态物
* 再注意!!如果静态属性实例化的其他类又回来实例化这个类,那么也还是不会调用他后面的静态物
*
* @author Feng
*
*/
public class Builder {
private static int k = 0;
private static int i = print("i"); //输出第1行
private static Builder t1 = new Builder("t1"); //输出第2.3.4行,t1的构造流程不会调用后续的静态物
private static Second ss = new Second(); //输出第5.6.7.8.9.10行,Second类回调的Builder对象依然不会调用后续的静态物
static {
print("静态块1"); //输出第11行
}
private static Builder t2 = new Builder("t2"); //输出第12.13.14行,t2的构造流程和t1一样
private static int n = 99; //这个时候n才被设置成99,在此之前是从0开始的
private int j = print("j"); //属性变量,每次构件对象都会调用,出现在2.6.12.17
{
print("构造块"); //构造快,每次构件对象都会调用,出现在3.7.13.18
}
public Builder(String str){ //构造函数,最后才轮到构造函数来执行,分别在4.8.14.19
System.out.println(++k + ":构造函数 " + str + " i=" + i + " n=" + n);
i++;
n++;
}
static {
print("静态块2"); //输出第15行,终于轮到了
}
public static int print(String str){
System.out.println(++k + ":" + str + " i=" + i + " n=" + n);
n++;
return ++i;
}
public static void main(String[] args){
Builder b = new Builder("init"); //输出第17.18.19行,经历一系列初始化之后终于可以开始实例化对象了
//Second.f1(); //如果不执行上一句,执行这句,唯一的不同就是少了最后3行输出,因为一旦调用了某个类的静态方法,这个类的初始化就要开始了
}
static {
print("静态块3"); //输出第16行,静态块从此之后不会再初始化
}
}
class Second{
int s_j = Builder.print("s_j"); //执行3,输出第9行
public Second(){
Builder.print("Second 构造体"); //执行4,输出第10行
}
static int s2 = Builder.print("Second"); //执行1,输出第5行
static Builder s1 = new Builder("Second_Builder"); //执行2,输出第6.7.8行
public static void f1(){
}
}