Java接口的初始化


背景

接口與類真正有所區別的是前面講述的四種“有且僅有”需要開始初始化場景中的第三種:當一個類在初始化時,要求其父類全部都已經初始化過了,但是一個接口在初始化時,並不要求其父接口全部都完成了初始化,只有在真正使用到父接口的時候(如引用接口中定義的常量)才會初始化。 ——《深入理解Java虛擬機:JVM高級特性與最佳實踐》

這里講到引用接口中定義的常量會初始化接口,但是書中也寫到引用類中的常量不會導致類被初始化,因為編譯階段已經將常量移動到常量池中了,兩者的說法有一些矛盾讓我很困惑。

public class InterfaceInitTest {

    public static void main(String[] args) {
        System.out.println(Interface.CONSTANT_INT);
    }

}

interface Interface {

    int CONSTANT_INT = 1;

    Object CONSTANT_OBJECT = new Object();

    Object CONSTANT_OBJECT2 = new Object() {
        {
            System.out.println("interface init");
        }
    };

}

1

Process finished with exit code 0

可以看到沒有接口並沒有被初始化,這和我理解的是一樣的,引用類中的常量不會導致類被初始化,引用接口中的常量也不會被初始化。

但是稍微修改一下main函數的代碼

public class InterfaceInitTest {

    public static void main(String[] args) {
        System.out.println(Interface.CONSTANT_OBJECT);
    }

}

interface init
java.lang.Object@14ae5a5

Process finished with exit code 0

可以看到同樣是引用接口中的常量,有時候接口又會被初始化。

通過在網上找到的一篇文章ConstantValue屬性,里面提到常量池中只能引用到基本類型和String類型的字面量。這也就解答了我的困惑:類和接口在被引用常量的時候是否被初始化,取決於這個常量能夠在編譯時被放進常量池中(排除不支持的類型和運行時常量)。

接口初始化的規則

通過測試發現,以下幾種情況接口會被初始化:

  1. 調用接口中不在常量池中的常量(對static字段的引用引發的初始化只會初始化實際定義的接口(盡管可以通過實現類,子接口的名稱進行引用(而接口中的static方法不能被繼承)))
  2. 調用接口中的靜態方法
  3. 當初始化一個類時,將初始化這個類實現的所有的包含default方法的接口和超接口
  4. java.lang.reflect可能會導致接口初始化

初始化接口本身不會導致任何超接口的初始化(注意和第3條的區別)

對於這個問題我查閱了很多書籍和文章,都有講得不太清楚的地方,所以不太確定結論是否正確,文章存在的疏漏讀者也可以評論指正。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM