靜態內部類不持有外部類的引用
這個觀點是眾所周知的。雖然明白是因為其構造函數內沒有傳入外部類的引用。可是為什么靜態類可以沒有傳入引用呢,靜態內部類的加載又是什么樣的過程呢?
這幾天找到的答案,似乎都不能讓我有一種豁然開朗的感覺。於是一次新探索開始了~
一開始,我是這樣想的:
靜態類和靜態對象,靜態變量,靜態塊等等一樣,是在類初始化時就被加載的,所以可以不需要傳入當前類的引用。
(關於非靜態內部類,就不需要多說,一定需要外部類先實例化后才會加載。)
通過網上一個代碼的思路,我寫出了以下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)來調用的,靜態的方法和變量則不需要。從一個角度上來說,它們是共享給所有對象的,不是一個角度私有。這點上,靜態內部類也是一樣的。
靜態內部類的加載過程:
靜態內部類的加載不需要依附外部類,在使用時才加載。不過在加載靜態內部類的過程中也會加載外部類。以上花了很多功夫來說明了