【JAVA】static塊到底什麼時候執行?

個人測試:

1.

Class c = staticPack.Word.class;

與 Class.forName("staticPack.Word",false,off.getClass().getClassLoader());

結果一致,顯示結果爲:

Loaded Office
類別準備載入
類別準備實例化
Word static initialization!

2.

Class c = Class.forName("staticPack.Word");

與 Class.forName("staticPack.Word",true,off.getClass().getClassLoader());

結果一致,顯示結果爲:

Loaded Office
類別準備載入
Word static initialization!
類別準備實例化

3.異常報錯,顯示調用的類

3.1 Class c = Class.forName("staticPack.Word",true,off.getClass().getClassLoader());

在Word。java中報錯

static{
		String a = null;
		System.out.println(a.equals("")+"Word static initialization!");
	}

顯示錯誤信息:

Loaded Office
類別準備載入
Exception in thread "main" java.lang.ExceptionInInitializerError
 at java.lang.Class.forName0(Native Method)
 at java.lang.Class.forName(Unknown Source)
 at staticPack.Office.main(Office.java:13)
Caused by: java.lang.NullPointerException
 at staticPack.Word.<clinit>(Word.java:7)
 ... 3 more

說明沒有調用默認調用了Class。forName()這個方法

3.2 Class c = Class.forName("staticPack.Word2",false,off.getClass().getClassLoader());

顯示錯誤信息:

Loaded Office
類別準備載入
類別準備實例化
Exception in thread "main" java.lang.ExceptionInInitializerError
 at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
 at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
 at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
 at java.lang.reflect.Constructor.newInstance(Unknown Source)
 at java.lang.Class.newInstance0(Unknown Source)
 at java.lang.Class.newInstance(Unknown Source)
 at staticPack.Office.main(Office.java:17)
Caused by: java.lang.NullPointerException
 at staticPack.Word.<clinit>(Word.java:7)
 ... 7 more

出錯信息是在調用newInstance0的時候出錯,要是沒有調用。程序正常運行。只是加載了類的信息。

總結:

那說明了靜態塊,只是在調用newInstance0方法時候纔會去觸發。

========================================================

 

"java深度歷險"一書在講解“類裝載”的一章中,舉了以下的例子:

引用

Java代碼 複製代碼 收藏代碼
  1. public interface Assembly{ 
  2.     public void start();; 
  3.  
  4. public class Word implements Assembly{ 
  5.     static
  6.         System.out.println("Word static initialization!");; 
  7.     }     
  8.  
  9.     public  void start();{ 
  10.         System.out.prinlnt("Word starts");; 
  11.     } 
  12.  
  13. public class Office{ 
  14.     public static void main(String args[]); throws Exception{ 
  15.         Office off = new Office();; 
  16.         System.out.println("類別準備載入");; 
  17.         Class c = Class.forName(args[0],true,off.getClass();.getClassLoader(););; 
  18.         System.out.println("類別準備實例化");; 
  19.         Object o = c.newInstance();; 
  20.         Object o2= c.newInstance();; 
  21.    } 
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代碼 複製代碼 收藏代碼
  1. public class TestA{ 
  2.     static
  3.        System.out.println("Static block executed!");; 
  4.     }     
  5.  
  6. public class Test{ 
  7.     public static void main(String args[]);{ 
  8.        Test test = new Test();; 
  9.        Class.forName("TestA",true,test.getClass();.getClassLoader(););; 
  10.    } 
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塊的絕對不是什麼“只是在類被第一次實體化的時候纔會被僅僅調用一次”,而應該是在類被初始化的時候,僅僅調用一次。   

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章