Java执行顺序

众所周知,java程序入口为main方法,但java程序首先执行的并不一定是main程序的第一句话。

1
2
3
4
5
6
7
8
9
10
11
public class Test {

static{
System.out.println("构造块");
}

public static void main(String[] args) {
System.out.println("main start");
Test t = new Test();
}
}
1
2
3
输出:
构造块
main start

在上面这个例子中,可以看到构造块先于main start输出。这是因为静态部分是属于类,而不是属于对象的,jvm首先加载类,而后在运行过程中有需要再进行类的实例化。

在一般情况下,代码执行顺序如下:

  1. 父类的静态属性 静态方法声明 静态块
  2. 静态属性 静态方法声明 静态块
  3. 父类的动态属性 普通方法声明 构造块
  4. 父类构造方法
  5. 动态属性 普通方法声明 构造块
  6. 构造方法

当加载一个类时,jvm首先为静态属性一并赋予默认值,按静态相关代码出现顺序执行(静态属性初始化,静态方法的声明,静态块的加载),且静态代码仅执行一次(仅加载一次,双亲委托避免重复加载类)。

当新建一个对象时,会调用该对象的构造方法。但在这之前,会先为动态属性一并赋予默认值(此时类加载已完成,除非因静态代码中实例化被打断而暂停),然后按代码出现顺序执行(动态属性初始化,构造块,方法声明)。最后执行构造方法。

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public class Test {//父类

public static int i = 0;
private static int j = print();

public static int print(){
System.out.println("print i:" + i);
return ++i;
}

static{
System.out.println("Test的构造块");
}

public Test(){
System.out.println("Test的构造方法 i:" + print());
}

}

public class TestParent extends Test{//子类

static int p = print();
public static int j = 10;

public static int print(){
System.out.println("print j:" + j);
return ++j;
}

static{
System.out.println("TestParent的构造块");
}

public TestParent(){
System.out.println("TestParent的构造方法 j:" + print());
}

public static void main(String[] args) {//执行入口
System.out.println("main start");
TestParent t = new TestParent();
}
}

执行结果如下:

1
2
3
4
5
6
7
8
9
print i:0
Test的构造块
print j:0
TestParent的构造块
main start
print i:1
Test的构造方法 i:2
print j:10
TestParent的构造方法 j:11

在类加载过程中,如果静态部分有实例对象操作,则会暂停类加载而进行类实例化,来看下面这一段代码:

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
27
28
29
30
31
public class Text {
public static int k = 0;
public static Text t1 = new Text("t1");
public static Text t2 = new Text("t2");
public static int i = print("i");
public static int n = 99;
public int j = print("j");

{
print("构造块");
}
static {
print("静态块");
}

public Text(String str) {
System.out.println((++k) + ":" + str + " i=" + i + " n=" + n);
++i;
++n;
}

public static int print(String str) {
System.out.println((++k) + ":" + str + " i=" + i + " n=" + n);
++n;
return ++i;
}

public static void main(String args[]) {
Text t = new Text("init");
}
}

执行结果如下:

1
2
3
4
5
6
7
8
9
10
11
1:j   i=0    n=0
2:构造块 i=1 n=1
3:t1 i=2 n=2
4:j i=3 n=3
5:构造块 i=4 n=4
6:t2 i=5 n=5
7:i i=6 n=6
8:静态块 i=7 n=99
9:j i=8 n=100
10:构造块 i=9 n=101
11:init i=10 n=102

在这里先进行了public int j = print(“j”);的执行,是因为在类加载时遇到了public static Text t1 = new Text(“t1”);实例化操作,所以就出现了以上执行结果。