在啃《The Java Programming Language 4th Edition》時看到的一個小知識點。先描述一下問題。
一個類中,靜態(tài)初始代碼塊中的代碼會在類加載時自動運行。考慮下面這種情況:
ClassA定義了靜態(tài)初始代碼塊,其中調(diào)用了ClassB的一個方法m(靜態(tài)非靜態(tài)均可)。而在ClassB的m方法中,又使用了ClassA類的信息。則,當虛擬機在沒有ClassB類的情況下,加載ClassA類時,會遇到這樣一條線索:
加載ClassA --> 調(diào)用ClassA的靜態(tài)初始化代碼塊 --> 調(diào)用ClassB的m方法 --> 加載ClassB --> 使用ClassA的信息
注意這條線索的一頭一尾,我們要在對ClassA還沒完成加載時,使用ClassA的信息!
示例代碼:

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的代碼,也就無法檢查是否存在靜態(tài)初始化代碼塊循環(huán)問題。事實上,上述程序在java中是能夠編譯通過的。
其次,運行時的結(jié)果。當程序運行到第3行時,JVM加載ClassA類,此時,會執(zhí)行ClassA類中的靜態(tài)初始化代碼塊。當程序執(zhí)行到第12行時,調(diào)用ClassB的print方法,此時,程序跳轉(zhuǎn)到18行。
關(guān)鍵在這兒:此時的print方法需要調(diào)用ClassA的信息,并打印其靜態(tài)屬性。而ClassA的信息正在加載過程中。此時,JVM采用的策略是:在print方法中使用ClassA不完整的信息。在print方法中ClassA的信息,是在第12行對ClassB.print方法之前的信息。此時ClassA.a1已經(jīng)被賦值為10,而ClassA.a2還未被賦值,它的值為默認值。因此,最后打印出的是10、0。