黑馬程序員_類加載器

----------- android培訓、java培訓、java學習型技術博客、期待與您交流! ------------

1、定義:簡單說,類加載器就是加載類的工具。

當出現一個類,用到此類的時候,Java虛擬機首先將類字節碼加載進內存,通常字節碼的原始信息放在硬盤上的classpath指定的目錄下。

2、類加載器作用:將.class文件中的內容加載進內存進行處理,處理完後的結果就是字節碼。

3、默認類加載器:

1)Java虛擬機中可安裝多個類加載器,系統默認的有三個主要的,每個類負責加載特定位置的類:BootStrap、ExtClassLoader、AppClassLoader

2)BootStrap--頂級類加載器:

       類加載器也是Java類,因爲其他是java類的類加載器本身也要被類加載器加載,顯然必須有第一個類加載器不是java類,這正是BootStrap。。它是嵌套在Java虛擬機內核中的,已啓動即出現在虛擬機中,是用c++寫的一段二進制代碼。所以不能通過java程序獲取其名字,獲得的只能是null。

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

代碼示例:

package cn.itcast.text2;
import java.util.Date;
public class ClassLoadTest{
	public static void main(String[] args) throws Exception{
		System.out.println(
				ClassLoadTest.class.getClassLoader().
				getClass().getName());//爲AppClassLoader
		System.out.println(
				System.class.getClassLoader());//爲null
	}
}

二、類加載器的委託機制:

1、加載類的方式

當Java虛擬機要加載一個類時,到底要用哪個類加載器加載呢?

1)首先,當前線程的類加載器去加載線程中的第一個類。

2)若A引用類B(繼承或者使用了B),Java虛擬機將使用加載類的類加載器來加載類B。

3)還可直接調用ClassLoader的LoaderClass()方法,來制定某個類加載器去加載某個類。

2、加載器的委託機制:每個類加載器加載類時,又先委託給上級類加載器。

       當所有祖宗類加載器沒有加載到類,回到發起者類加載器,還加載不了,則拋ClassNotFoundException,不是再去找發起者類加載器的兒子,因爲沒有getChild方法,即使有,那有多個兒子,找哪一個呢?對着類加載器的層次結構圖和委託加載原理,解釋先前將ClassLoaderTest輸出成jre/lib/ext目錄下的itcast.jar包中後,運行結果爲ExtClassLoader的原因。

3、委託機制的優點:可以集中管理,不會產生多字節碼重複的現象。

補充:面試題

可不可以自己寫個類爲:java.lang.System呢?

回答:第一、有一道面試,能不能自己寫個類叫java.lang.System,爲了不讓我們寫System類,類加載採用委託機制,這樣可以保證爸爸們優先,也就是總是使用爸爸們能找到的類,這樣總是使用java系統提供的System

第二、但是還是有辦法加載這個自定義的System類的,此時就不能交給上級加載了,需要用自定義的類加載器加載,這就需要有特殊的寫法才能去加載這個自定義的System類的。

三、自定義類加載器

1、自定義的類加載器必須繼承抽象類ClassLoader,要覆寫其中的findClass(String name)方法,而不用覆寫loadClass()方法。

2、覆寫findClass(String name)方法的原因:

1)是要保留loadClass()方法中的流程,因爲loadClass()中調用了findClass(String name)這個方法,此方法返回的就是去尋找父級的類加載器。

2)在loadClass()內部是會先委託給父級,當父級找到後就會調用findClass(String name)方法,而找不到時就會用子級的類加載器,再找不到就報異常了,所以只需要覆寫findClass方法,那麼就具有了實現用自定義的類加載器加載類的目的。

流程:

父級-->loadClass-->findClass-->得到Class文件後轉化成字節碼-->defind()。

3、編程步驟:

1)編寫一個對文件內容進行簡單加盟的程序

2)編寫好了一個自己的類加載器,可實現對加密過來的類進行裝載和解密。

3)編寫一個程序,調用類加載器加載類,在源程序中不能用該類名定義引用變量,因爲編譯器無法識別這個類,程序中除了可使用ClassLoader的load方法外,還能使用放置線程的上線文類加載器加載或系統類加載器,然後在使用forName得到字節碼文件。

import java.util.Date;

public class ClassLoaderAttachment extends Date {
	//對此類進行加密
		public String toString(){
			return "hello world";
		}
		public static void main(String [] args){
			
		}
}

//自定義類加載器
package cn.itcast.text2;

import java.io.*;
//繼承抽象類ClassLoader
public class MyClassLoader  extends ClassLoader {
	public static void main(String[] args) throws Exception {
		//傳入兩個參數,源和目標
		String scrPath = args[0];
		String destDir = args[1];
		//將數據讀取到輸入流中,並寫入到輸出流中
		FileInputStream fis = new FileInputStream(scrPath);
		String destFileName = 
				scrPath.substring(scrPath.lastIndexOf('\\')+1);
		String destPath = destDir + "\\" + destFileName;
		FileOutputStream fos = new FileOutputStream(destPath);
		//加密數據
		cypher(fis,fos);
		fis.close();
		fos.close();
	}
	//定義加密數據的方法
	private static void cypher(InputStream ips,OutputStream ops)throws Exception{
		int b = 0;
		while((b=ips.read())!=-1){
			ops.write(b ^ 0xff);
		}
	}
	//定義全局變量
	private String classDir;
	@Override//覆寫findClass方法,自定義類加載器
	protected Class<?> findClass(String name) throws ClassNotFoundException {
		String classFileName = classDir + "\\" + name + ".class"; 
		try {
			//將要加載的文件讀取到流中,並寫入字節流中
			FileInputStream fis = new FileInputStream(classFileName);
			ByteArrayOutputStream bos = new ByteArrayOutputStream();
			cypher(fis,bos);
			fis.close();
			byte[] bytes = bos.toByteArray();
			return defineClass(bytes, 0, bytes.length);
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		//如果沒找到類,則用父級類加載器加載
		return super.findClass(name);
	}
	//構造函數
	public MyClassLoader(){}
	public MyClassLoader(String classDir){
		this.classDir = classDir;
	}
}

----------- android培訓、java培訓、java學習型技術博客、期待與您交流! ------------

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章