歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Java動態性: 類加載時的延遲初始化

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

日期:2017/3/1 10:17:53   编辑:Linux編程

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

《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. static{
  4. System.out.println("Parent initiate");
  5. }
  6. public static void func(){
  7. System.out.println("func");
  8. }
  9. }

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

運行的結果是:

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

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

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

Copyright © Linux教程網 All Rights Reserved