歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

Java動態性: 類加載時的延遲初始化

誤區:值得注意的是,需要搞清楚 對符號引用的解析 和類的初始化的區別。

《Java類的裝載(Loading)、鏈接(Linking)和初始化(Initialization) 》一文中提到了, 鏈接的最後一步是resolution  , 即對符號引用的解析,但這不是必須的,可以等到相應的符號引用第一次使用時再解析。而類的初始化是在鏈接之後的(注意了,根據不同JVM有不同的實現方式,在類初始化的時候,可能已經完成了所有的符號引用的解析,也可能沒有),本文所寫的就是 類的初始化的時機問題。

Java類的動態加載機制規定,在類被主動使用(active use)之前,必須已經完成類的初始化。

既然有主動調用,那麼就有被動調用了。這兩者有哪些區別呢?

主動使用的情況:

1. 創造該類的一個新的實例

2.調用這個類中的靜態方法

3.獲取類或者接口中的非常量的靜態變量

4.利用反射調用方法。

5.初始該類的某子類。

6.被制定為JVM開始運行時必須初始化的類。

注意:

首先,3中為何是“非常量的靜態變量”。如果是常量,即聲明為final的話,並不會出現對類的構造,雖然調用時有類名出現,但實際調用會直接使用常量,繞過了類的限制(詳情見相關constant pool 和 runtime constant pool的知識)。

只有當一個非常量的靜態變量被顯示的在類或接口中聲明時,它的調用才屬於主動調用。對於父類中某非常兩靜態變量的調用屬於被動使用(positive use),

如下代碼

  1. public class Parent {  
  2.      static int i = 10 ;  
  3.   
  4.      static{  
  5.             System.out.println("Parent initiate");  
  6.      }  
  7.        
  8.      public static void func(){  
  9.         System.out.println("func");  
  10.      }  
  11.       
  12.   
  13. }  

 

  1. public class Son extends Parent{  
  2.     static{  
  3.         System.out.println("Son initiate");  
  4.     }  
  5.       
  6.       
  7. }  
  1. public class Test {  
  2.   
  3.     static{  
  4.         System.out.println("Test initiate");  
  5.     }  
  6.       
  7.     public static void main(String[] args){  
  8.         System.out.println(Son.i);  
  9.         Son.func();  
  10.     }  
  11.       
  12. }  

運行的結果是:

  1. Test initiate  
  2. Parent initiate  
  3. 10  
  4. func  

雖然有出現Son,但Son.i訪問的是父類的非常量靜態變量。於是沒有對Son類進行初始化,而只是初始化了明確的聲明靜態變量的Parent類。

由此可見,一般的,我們在某個類中定義了其他類的成員變量引用,只要該變量沒有 new 出一個新的 對象,則JVM也不會初始化這個類,類此時只是被加載了而已,而沒有鏈接 和初始化。

Copyright © Linux教程網 All Rights Reserved