"java深度歷險"一書在講解“類裝載”的一章中,舉了以下的例子:
引用
Java代碼
- public interface Assembly{
- public void start();;
- }
- public class Word implements Assembly{
- static{
- System.out.println("Word static initialization!");;
- }
- public void start();{
- System.out.prinlnt("Word starts");;
- }
- }
- public class Office{
- public static void main(String args[]); throws Exception{
- Office off = new Office();;
- System.out.println("類別準備載入");;
- Class c = Class.forName(args[0],true,off.getClass();.getClassLoader(););;
- System.out.println("類別準備實例化");;
- Object o = c.newInstance();;
- Object o2= c.newInstance();;
- }
- }
執行java Office Word,運行結果如下:
“Loaded Office”
“類別準備載入”
“Loaded Accembly”
“Loaded Word””
“Word static initialization”
“類別準備實體化”。
但是如果將Office.java中Class.forName(args[0],true,off.getClass().getClassLoader())中的true變爲false,再執行java Office Word結果顯示爲:
“Loaded Office”
“類別準備載入”
“Loaded Accembly”
“Loaded Word””
“類別準備實體化”
“Word static initialization”。
顯然兩次紅字部分順序相反,及static塊執行的順序不同。此書作者提出了原因,原文:
引用
“過 去很多java書上提到靜態初始化(static initializion block)時,都會說靜態初始化區塊只是在類第一次載入的時候纔會被調用僅僅一次。可是上面輸出卻發現即使類被載入了,其靜態初始化區塊也沒有被調用, 而是在第一次調用newInstance方法時,靜態初始化塊才被真正調用,應該改成-靜態初始化塊只是在類被第一次實體化的時候纔會被僅僅調用一次。”
其實,該書作者的上述描述有誤。通過一個試驗,就可以看出謬誤所在。
Java代碼
- public class TestA{
- static{
- System.out.println("Static block executed!");;
- }
- }
- public class Test{
- public static void main(String args[]);{
- Test test = new Test();;
- Class.forName("TestA",true,test.getClass();.getClassLoader(););;
- }
- }
運行一下,相信大家一定可以看到,“Static block executed!”的輸出。這與
引用
而是在第一次調用newInstance方法時,靜態初始化塊才被真正調用
的說法矛盾。
其實我想事實是這樣的:
一
個類的運行,JVM做會以下幾件事情 1、類裝載 2、鏈接 3、初始化
4、實例化;而初始化階段做的事情是初始化靜態變量和執行靜態方法等的工作。所以,當
Class.forName(args[0],true,off.getClass().getClassLoader());中的true變爲
false的時候,就是告訴JVM不需再load
class之後進行initial的工作。這樣,將initial的工作推遲到了newInstance的時候進行。所以,static塊的絕對不是什麼
“只是在類被第一次實體化的時候纔會被僅僅調用一次”,而應該是在類被初始化的時候,僅僅調用一次。