靜態內部類何時初始化


靜態內部類不持有外部類的引用

這個觀點是眾所周知的。雖然明白是因為其構造函數內沒有傳入外部類的引用。可是為什么靜態類可以沒有傳入引用呢,靜態內部類的加載又是什么樣的過程呢?

這幾天找到的答案,似乎都不能讓我有一種豁然開朗的感覺。於是一次新探索開始了~

 


一開始,我是這樣想的:

靜態類和靜態對象,靜態變量,靜態塊等等一樣,是在類初始化時就被加載的,所以可以不需要傳入當前類的引用。 
(關於非靜態內部類,就不需要多說,一定需要外部類先實例化后才會加載。)

通過網上一個代碼的思路,我寫出了以下demo:

import java.util.Random;

public class OuterClass { public static long OUTER_DATE = System.currentTimeMillis(); static { System.out.println("外部類靜態塊加載時間:" + System.currentTimeMillis()); } public OuterClass() { timeElapsed(); System.out.println("外部類構造函數時間:" + System.currentTimeMillis()); } static class InnerStaticClass { public static long INNER_STATIC_DATE = System.currentTimeMillis(); } class InnerClass { public long INNER_DATE = 0; public InnerClass() { timeElapsed(); INNER_DATE = System.currentTimeMillis(); } } public static void main(String[] args) { OuterClass outer = new OuterClass(); System.out.println("外部類靜態變量加載時間:" + outer.OUTER_DATE); System.out.println("非靜態內部類加載時間"+outer.new InnerClass().INNER_DATE); System.out.println("靜態內部類加載時間:"+InnerStaticClass.INNER_STATIC_DATE); } //單純的為了耗時,來擴大時間差異 private void timeElapsed() { for (int i = 0; i < 10000000; i++) { int a = new Random(100).nextInt(), b = new Random(100).nextInt(); a = a + b; } } }

 

假如我的推想沒有錯誤的話,我想應該會這樣的:

外部類常量加載時間 = 外部類靜態塊加載時間 = 靜態內部類加載時間 < 外部類構造函數時間< 非靜態內部類加載時間

Ok,我以為我離走到人生顛覆只差一個Run了 ! ✧(๑•̀ㅂ•́)و✧

結果卻是: 
這里寫圖片描述

 

不過,從上面我們可以分析出來結果是:

靜態內部類和非靜態內部類一樣,都是在被調用時才會被加載

不信的同學可以自己試試copy以上的代碼。隨機調換main()函數里的方法調用順序,來驗證以上的規律。


后來我這么想:

靜態內部類其實和外部類的靜態變量,靜態方法一樣,只要被調用了都會讓外部類的被加載。不過當只調用外部類的靜態變量,靜態方法時,是不會讓靜態內部類的被加載

嗯哼~還是來一個demo,不過是改動了點上面的東西:

import java.util.Random;

public class OuterClass { public static long OUTER_DATE = System.currentTimeMillis(); public static int a = 1; static { System.out.println("外部類靜態塊加載時間:" + System.currentTimeMillis()); } public OuterClass() { timeElapsed(); System.out.println("外部類構造函數事件:" + System.currentTimeMillis()); } static class InnerStaticClass { static { System.out.println("內部類靜態塊加載時間:" + System.currentTimeMillis()); } public static double INNER_DATE = System.currentTimeMillis(); } class InnerClass { public long INNER_DATE = 0; public InnerClass() { timeElapsed(); INNER_DATE = System.currentTimeMillis(); } } public static void Hello(){System.out.println("Hello");} public static void main(String[] args) { //System.out.println("外部類常量加載時間:" + OuterClass.OUTER_DATE); OuterClass.Hello(); OuterClass outer = new OuterClass(); System.out.println("外部類靜態變量加載時間:" + OuterClass.OUTER_DATE); System.out.println("外部類靜態變量加載時間:" + outer.OUTER_DATE); } //單純的為了耗時而已 private void timeElapsed() { for (int i = 0; i < 10000000; i++) { int a = new Random(100).nextInt(), b = new Random(100).nextInt(); a = a + b; } } }

 

結果如下:

這里寫圖片描述

觀點1:我說的沒錯吧~,調用外部類的靜態變量,靜態方法可以讓外部類得到加載,不過這里靜態內部類沒有被加載

再看看下面的這段:

import java.util.Random;

public class OuterClass { public static long OUTER_DATE = System.currentTimeMillis(); public static int a = 1; static { System.out.println("外部類靜態塊加載時間:" + System.currentTimeMillis()); } public OuterClass() { timeElapsed(); System.out.println("外部類構造函數事件:" + System.currentTimeMillis()); } static class InnerStaticClass { static { System.out.println("內部類靜態塊加載時間:" + System.currentTimeMillis()); } public static long INNER_STATIC_DATE = System.currentTimeMillis(); } class InnerClass { public long INNER_DATE = 0; public InnerClass() { timeElapsed(); INNER_DATE = System.currentTimeMillis(); } } public static void Hello(){System.out.println("Hello");} public static void main(String[] args) { System.out.println("內部類靜態變量加載時間:" + InnerStaticClass.INNER_STATIC_DATE ); System.out.println("外部類靜態變量加載時間:" + OuterClass.OUTER_DATE ); } //單純的為了耗時而已 private void timeElapsed() { for (int i = 0; i < 10000000; i++) { int a = new Random(100).nextInt(), b = new Random(100).nextInt(); a = a + b; } } }

 

結果: 
這里寫圖片描述 
觀點2:可以看出,我們其實加載靜態內部類的時候,其實還會先加載外部類,才加載靜態內部類

由觀點1與觀點2聯立,以上論點得證!


好,回歸主題,為什么靜態內部類可以不傳入引用呢?

因為其本質就是針對外部類的內部類,而不是對象的內部類,不必用this來調用。 

首先,從靜態的概念出發理解,靜態修飾過后的一切物件都只與類相關,不與對象引用相關

As we known,靜態變量,靜態方法,靜態塊等都是類級別的屬性,而不是單純的對象屬性。他們在類第一次被使用時被加載(記住,是一次使用,不一定是實例化)。我們可以簡單得用 類名.變量 或者 類名.方法來調用它們。與調用沒有被static 修飾過變量和方法不同的是:一般變量和方法是用當前對象的引用(即this)來調用的,靜態的方法和變量則不需要。從一個角度上來說,它們是共享給所有對象的,不是一個角度私有。這點上,靜態內部類也是一樣的。

 


靜態內部類的加載過程:

靜態內部類的加載不需要依附外部類,在使用時才加載。不過在加載靜態內部類的過程中也會加載外部類。以上花了很多功夫來說明了


 


免責聲明!

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



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