Java 類加載器

一、類加載器


       類加載器(Class Loader)用來加載 Java 類到 Java 虛擬機中。一般來說,Java 虛擬機使用 Java 類的方式如下:Java 源程序(.java 文件)在經過 Java 編譯器編譯之後就轉換成 Java 字節碼(.class 文件)。類加載器負責讀取 Java 字節碼,並轉換成 java.lang.Class 類的一個實例。每個這樣的實例用來表示一個 Java 類。通過實例的 newInstance() 方法就可以創建出該類的一個對象。類加載器有是 Java 類,它也需要被加載器加載,顯然必須有第一個不是 Java 類的類加載器—— BootStrap 加載。


二、類加載器的分類

  • 引導類加載器(BootStrap Class Loder——BootStrap):它用來加載 Java 核心庫,是用 C++ 編寫的一段二進制代碼。
  • 擴展類加載器(Extensions Class Loder——ExtClassLoader):它用來加載 Java 的擴展庫。Java 虛擬機會提供一個擴展庫目錄。該類加載器在此目錄裏面查找並加載 Java 類
  • 系統類加載器(System Class Loder——AppClassLoader):它根據 Java 應用的類路徑(CLASSPATH)來加載 Java 類,一般來說 Java 應用的類都是由它來完成加載的。這就是爲什麼我們要把 .class 文件放在 CLASSPATH 裏面的原因。


三、類加載器的層次關係


                Java 虛擬機中的所有類加載器採用具有父子關係的樹形結構進行組織,在實例化每個類加載器對象時,需要爲其指定一個父類加載器對象過着默認採用系統類加載器爲其父級加載器。


                編程實例:演示類加載器的層次結構關係。

public class A {

	public static void main(String[] args) {

		ClassLoader classLoader = A.class.getClassLoader();
		while (classLoader != null) {	// 因爲頂級加載器爲 BootStrap 並不是 Java 類,所以在獲取加載器對象時會是 null
			System.out.println(classLoader.getClass().getName()); // 獲取當前類的加載器
			classLoader = classLoader.getParent(); // 當前類加載器的父級加載器
		}
	}
}
輸出:

          sun.misc.Launcher$AppClassLoader
          sun.misc.Launcher$ExtClassLoader


四、類加載器的委託機制


                首先當線程的類加載器去加載線程中的第一個類,然後如果類 A 中引用了類 B ,Java 虛擬機將使用加載類 A 的加載器來加載類 B,還可以直接調用 ClassLoader.loadClass() 方法來指定某個類加載器去加載某個類。也就是說,類加載器在嘗試自己去查找某個類的字節碼文件時,會先委託給父級加載器先去嘗試加載這個類,依次類推。這種模式又稱爲模版模式。當所有父級加載器並沒有加載到該類時,會返回給發起者類加載器,如果仍然沒有加載到,則會拋出 ClassNotFoundException 異常,不會再委託給子加載器。


五、Class.forName


                 Class.forName 是一個靜態的方法,同樣可以用來加載類。該方法有兩種形式:Class.forName(String name, boolean initialize, ClassLoader loader) 和 Class.forName(String className)。第一種形式的參數 name 表示類的完全限定名,initialize 表示是否初始化類,loader 表示加載時使用的類加載器。第二種形式則相當於設置了參數 initialize 的值爲 true,loader 的值爲當前類的類加載器。Class.forName 是一個很常見的類加載方法,例如在獲取 jdbc 驅動的時候。


六、自定義類加載器


       自定義加載器類必須繼承 ClassLoader 類。在一般情況下只需要重寫 ClassLoader 類中的 findClass() 方法


       編程實例:編寫一個自己的類加載器,對已經加密的 .class 文件只能用我們自己寫的類加載器加載。


      

public class A {

	public static void main(String[] args) throws Exception {

		String srcPath = args[0];
		String fileName = srcPath.substring(srcPath.lastIndexOf('\\') + 1);
		String desPath = args[1] + "\\" + fileName;
		FileInputStream fis = new FileInputStream(srcPath);
		FileOutputStream fos = new FileOutputStream(desPath);
		Cypher.cypher(fis, fos);
		fis.close();
		fos.close();

		// 用系統加載器加載,顯示不能識別:Exception in thread "main"
		// java.lang.ClassFormatError: Incompatible magic value 889275713 in
		// class file ClassLoaderTest
		
		// Class newClass = A.class.getClassLoader().loadClass("ClassLoaderTest");

		// 用自定義加載器加載:輸出 DriverKing_斌
		Class newClass = new MyClassLoader(desPath)
				.loadClass("ClassLoaderTest");
		Date date = (Date) newClass.newInstance();
		System.out.println(date.toString());
	}

}

class Cypher {
	public static void cypher(InputStream in, OutputStream out)
			throws Exception {
		int len = -1;
		while ((len = in.read()) != -1) {
			out.write(len ^ 0xff);
		}
	}
}

class MyClassLoader extends ClassLoader {

	private String filePath;

	public MyClassLoader() {
	}

	public MyClassLoader(String filePath) {
		this.filePath = filePath;
	}

	@SuppressWarnings("deprecation")
	protected Class<?> findClass(String name) throws ClassNotFoundException {
		try {
			FileInputStream fis = new FileInputStream(filePath);
			ByteArrayOutputStream bos = new ByteArrayOutputStream();
			Cypher.cypher(fis, bos);	//解密
			fis.close();
			byte[] bytes = bos.toByteArray();
			return defineClass(bytes, 0, bytes.length); // 返回字節碼文件
		} catch (Exception e) {
			e.printStackTrace();
		}
		return super.findClass(name);
	}
}

class ClassLoaderTest extends Date {
	public String toString() {
		return "DriverKing_斌";
	}
}


搞了一個多小時!終於搞定了!!!!!淡定啊!!!!!


七、web 容器有關的類加載器


       由於我還沒有對類加載器掌握透徹,還沒有進行到這一步,繼續學習中~!以後吃透了再添加進來





發佈了54 篇原創文章 · 獲贊 6 · 訪問量 15萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章