首先明確一點,類初始化加載static修飾的屬性/代碼塊的時候是按照從上到下加載的,
實例:
package com.wm.jasypt.service;
/**
* @author 半卷流年
* @date 2020-6-3 15:56
*/
public class Singleton {
private static Singleton singleton = new Singleton();
public static int counter1;
public static int counter2 = 0;
private Singleton() {
counter1++;
counter2++;
}
public static Singleton getSingleton() {
return singleton;
}
public static void main(String[] args) {
Singleton singleton = Singleton.getSingleton();
System.out.println("counter1="+singleton.counter1); //輸出什麼?
System.out.println("counter2="+singleton.counter2); //輸出什麼?
}
}
輸出的結果如下:
counter1=1
counter2=0
原因分析:
1.執行main方法的時候,由於Singleton 類沒有被加載,首先觸發類加載器,初始化該類,注意類的初始化是隻會初始化一次的
singleton = null;
counter1 = 0;
counter2 = 0;
2.按照static順序加載,先進入new Singleton()初始化,進入構造函數的加載, 執行完成之後
counter1 = 1;
counter2 = 1;
3.在執行 public static int counter1;
由於沒有指定初始值,故 counter1位之前初始化的值:
counter1 = 1;
4.執行 public static int counter2 = 0;
所以counter2的值爲:
counter2 = 0;
綜合得出結果: counter1 = 1; counter2 = 0;
下面debug如下: 依次的順序如下:
1.
2.
3.
下面將上面的例子更換一個順序,輸出的是什麼:
package com.wm.jasypt.service;
/**
* @author 半卷流年
* @date 2020-6-3 15:56
*/
public class Singleton {
public static int counter1;
public static int counter2 = 0;
private static Singleton singleton = new Singleton();
private Singleton() {
counter1++;
counter2++;
}
public static Singleton getSingleton() {
return singleton;
}
public static void main(String[] args) {
Singleton singleton = Singleton.getSingleton();
System.out.println("counter1="+singleton.counter1); //輸出什麼?
System.out.println("counter2="+singleton.counter2); //輸出什麼?
}
}
結果爲:
counter1=1
counter2=1
原因:
是先初始化counter1,2賦值,在進入構造,故輸出都是1
類加載機制,主動初始化化的幾種情況:
主動初始化的6種方式
(1)創建對象的實例:我們new對象的時候,會引發類的初始化,前提是這個類沒有被初始化。
(2)調用類的靜態屬性或者爲靜態屬性賦值
(3)調用類的靜態方法
(4)通過class文件反射創建對象
(5)初始化一個類的子類:使用子類的時候先初始化父類
(6)java虛擬機啓動時被標記爲啓動類的類:就是我們的main方法所在的類
只有上面6種情況纔是主動使用,也只有上面六種情況的發生纔會引發類的初始化。
關於類加載器的雙親委派機制:
關於類加載器,我們不得不說一下雙親委派機制。聽着很高大上,其實很簡單。比如A類的加載器是AppClassLoader(其實我們自己寫的類的加載器都是AppClassLoader),AppClassLoader不會自己去加載類,而會委ExtClassLoader進行加載,那麼到了ExtClassLoader類加載器的時候,它也不會自己去加載,而是委託BootStrap類加載器進行加載,就這樣一層一層往上委託,如果Bootstrap類加載器無法進行加載的話,再一層層往下走。
爲什麼使用雙親委派機制:
判斷兩個類相同的前提是這兩個類都是同一個加載器進行加載的,如果使用不同的類加載器進行加載同一個類,也會有不同的結果。
如果沒有雙親委派機制,會出現什麼樣的結果呢?比如我們在rt.jar中隨便找一個類,如java.util.HashMap,那麼我們同樣也可以寫一個一樣的類,也叫java.util.HashMap存放在我們自己的路徑下(ClassPath).那樣這兩個相同的類採用的是不同的類加載器,系統中就會出現兩個不同的HashMap類,這樣引用程序就會出現一片混亂。