在啃《The Java Programming Language 4th Edition》時看到的一個小知識點。先描述一下問題。
一個類中,靜態初始代碼塊中的代碼會在類加載時自動運行。考慮下面這種情況:
ClassA定義了靜態初始代碼塊,其中調用了ClassB的一個方法m(靜態非靜態均可)。而在ClassB的m方法中,又使用了ClassA類的信息。則,當虛擬機在沒有ClassB類的情況下,加載ClassA類時,會遇到這樣一條線索:
加載ClassA --> 調用ClassA的靜態初始化代碼塊 --> 調用ClassB的m方法 --> 加載ClassB --> 使用ClassA的信息
注意這條線索的一頭一尾,我們要在對ClassA還沒完成加載時,使用ClassA的信息!
示例代碼:
1
public class TestStaticInit{
2
public static void main(String args[]){
3
ClassA a= new ClassA();
4
}
5
}
6
7
class ClassA{
8
static int a1;
9
static int a2;
10
static{
11
a1 = 10;
12
ClassB.print();
13
a2 = 30;
14
}
15
}
16
17
class ClassB{
18
public static void print(){
19
System.out.println(ClassA.a1);
20
System.out.println(ClassA.a2);
21
}
22
}
23
24

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

首先,編譯器無法解決這個問題,因為在編譯ClassA類時,無法找到ClassB的代碼,也就無法檢查是否存在靜態初始化代碼塊循環問題。事實上,上述程序在java中是能夠編譯通過的。
其次,運行時的結果。當程序運行到第3行時,JVM加載ClassA類,此時,會執行ClassA類中的靜態初始化代碼塊。當程序執行到第12行時,調用ClassB的print方法,此時,程序跳轉到18行。
關鍵在這兒:此時的print方法需要調用ClassA的信息,并打印其靜態屬性。而ClassA的信息正在加載過程中。此時,JVM采用的策略是:在print方法中使用ClassA不完整的信息。在print方法中ClassA的信息,是在第12行對ClassB.print方法之前的信息。此時ClassA.a1已經被賦值為10,而ClassA.a2還未被賦值,它的值為默認值。因此,最后打印出的是10、0。