1. 程式人生 > >Java類的載入過程與ClassLoader的理解及測試

Java類的載入過程與ClassLoader的理解及測試

先了解下在程式準備執行某個類,但是該類還沒被載入到記憶體中,會經過以下三個步驟:

類的載入(Load)→類的連線(Link)→類的初始化(Initialize)

  • 載入:類經過javac.exe編譯的.class位元組碼檔案讀入記憶體(將靜態資料轉換成堆中方法區的執行時資料結構),併為之建立一個java.lang.Class物件作為方法區中類資料的訪問入口(引用的地址),需要訪問和使用類資料只能通過這個Class物件;此過程由類的載入器完成;
  • 連結:將java類的二進位制程式碼合併到JVM的執行狀態中的過程;
    • 驗證:確保載入的類符合JVM規範;
    • 準備:正式為類變數(static)分配記憶體並設定變數預設初始值(非任何顯示賦值),這些記憶體都在方法區中分配;
    • 解析:虛擬機器常量池內的符號引用(常量名)替換為直接引用(地址)的過程
  • 初始化:JVM負責對類進行初始化;
    • 執行類構造器
    • 如其父類為進行初始化,則初始化操作從先從父類進行;
    • 虛擬機器會保證一個類的

      類載入器ClassLoader的作用:

      除了上面提到的作用,還有一個類快取機制:一旦某個類被載入到記憶體中,將位置載入(快取)一段時間,相當於一個快取了一個Class物件,無論此類建立多少個例項,都是從這唯一的結構中獲取資訊;GC也可以回收這些Class物件;
      JVM規範定義的類的載入器型別如下:

載入器關係測試:

@Test
    public void test1() {
        //1.獲取一個系統類載入器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader);
        //2.獲取系統類載入器的父類載入器,即擴充套件類載入器
        ClassLoader extensionClassLoader = systemClassLoader.getParent();
        System.out.println(extensionClassLoader);
        //3.獲取擴充套件類載入器的父類載入器,即引導類載入器
        ClassLoader bootstapClassLoader = extensionClassLoader.getParent();
        //引導類載入器用於載入java核心庫,無法直接獲取,故輸出null
        System.out.println(bootstapClassLoader);
        //4.測試當前類由哪個類載入器進行載入
        ClassLoader classLoader = null;
        try {
            classLoader = Class.forName("Reflection.ClassLoaderTest").getClassLoader();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        System.out.println(classLoader);//結果為系統類載入器
        //5.測試JDK提供的Object類由哪個類載入器完成
        ClassLoader objClassLoader = null;
        try {
            objClassLoader = Class.forName("java.lang.Object").getClassLoader();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        System.out.println(objClassLoader);//結果為null(說明是用的引導類載入器,我們無法獲取)
        //6.關於類載入器的一個主要方法:getResourceAsStream(String str):獲取路徑下的指定檔案的輸入流
        InputStream is = null;
        is = this.getClass().getClassLoader().getResourceAsStream("Reflection\\test.properties");
        System.out.println(is);
        //可用於讀取配置檔案,下面單獨拿來測試
    }

讀取.properties配置檔案:

    @Test
    public void test2(){
        Properties properties = new Properties();//表示一個持久的屬性集,可儲存在流中或從流中載入
//        //1.獲取輸入流
//        //方式一:(此時的檔案預設路徑在Module下)
//        FileInputStream fis = null;
//        try {
//            fis = new FileInputStream("test.properties");
//        } catch (FileNotFoundException e) {
//            e.printStackTrace();
//        }
        //方式二:使用ClassLoader方式(此時的檔案預設路徑在當前Module的src下)
        //獲取當前類的Class例項物件-獲取類載入器-獲取指定指定路徑下的檔案輸入流
        InputStream is = this.getClass().getClassLoader().getResourceAsStream("test1.properties");

        //2.讀取配置檔案
        try {
            //從輸入流中讀取屬性列表(鍵和元素對)
            properties.load(is);
        } catch (IOException e) {
            e.printStackTrace();
        }
        //匹配對應key的屬性,獲取key對應的元素值
        String user = properties.getProperty("user");
        String password = properties.getProperty("password");
        System.out.println("user = " + user + " , password = " + password);
    }