Java類的載入過程與ClassLoader的理解及測試
阿新 • • 發佈:2020-02-23
先了解下在程式準備執行某個類,但是該類還沒被載入到記憶體中,會經過以下三個步驟:
類的載入(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);
}