關於java類加載的一個面試點分析

首先明確一點,類初始化加載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類,這樣引用程序就會出現一片混亂。


 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章