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


先了解下在程序准備運行某個類,但是該類還沒被加載到內存中,會經過以下三個步驟:

類的加載(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);
    }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM