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

先了解下在程序準備運行某個類,可是該類還沒被加載到內存中,會通過如下三個步驟:java

類的加載(Load)→類的鏈接(Link)→類的初始化(Initialize)

  • 加載:類通過javac.exe編譯的.class字節碼文件讀入內存(將靜態數據轉換成堆中方法區的運行時數據結構),併爲之建立一個java.lang.Class對象做爲方法區中類數據的訪問入口(引用的地址),須要訪問和使用類數據只能經過這個Class對象;此過程由類的加載器完成;
  • 連接:將java類的二進制代碼合併到JVM的運行狀態中的過程;
    • 驗證:確保加載的類符合JVM規範;
    • 準備:正式爲類變量(static)分配內存並設置變量默認初始值(非任何顯示賦值),這些內存都在方法區中分配;
    • 解析:虛擬機常量池內的符號引用(常量名)替換爲直接引用(地址)的過程
  • 初始化:JVM負責對類進行初始化;
    • 執行類構造器 ()方法的: 此方法是由編譯期自動收集類中全部類變量的賦值動做和靜態代碼塊中的語句合併產生的(類構造器是構造類信息的,並不是new對象構造器)
    • 如其父類爲進行初始化,則初始化操做從先從父類進行;
    • 虛擬機會保證一個類的 ()方法在多線程環境中被正確加鎖和同步; 緩存

      類加載器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);
    }
相關文章
相關標籤/搜索