Java基礎(chǔ)加強(qiáng)之類加載器
學(xué)習(xí)概述:本模塊深入講解了Java類加載方面的知識(shí),Java類加載器和類加載機(jī)制以及類加載原理
學(xué)習(xí)目標(biāo):掌握類加載機(jī)制和原理,能夠獨(dú)立開發(fā)自己的類加載器。
1.類的加載
什么是類加載? 類加載是指將類的class文件讀入內(nèi)存,并為之創(chuàng)建一個(gè)Java.lang.Class對(duì)象,也就是說當(dāng)程序中使用任何類時(shí),系統(tǒng)都會(huì)為之建立一個(gè)java.lang.Class對(duì)象。
類加載器負(fù)責(zé)加載所有類,系統(tǒng)為所有被載入內(nèi)存中的類生成一個(gè)java.lang.Class實(shí)例。一旦一個(gè)類被載入JVM中,同一個(gè)類不會(huì)再被再次載入。
思考問題:怎么樣才算同一個(gè)類?
當(dāng)JVM啟動(dòng)時(shí),會(huì)形成三個(gè)類加載器組成的原始類加載器層次結(jié)構(gòu):
【BootStrap ClassLoader】根類加載器 這是一個(gè)特殊的加載器,他并不是有Java編寫,而是JVM自身實(shí)現(xiàn)的
【Extension Classloader】擴(kuò)展類加載器
【System Classloader】系統(tǒng)類加載器
類加載器的父子關(guān)系:
實(shí)驗(yàn)獲得類加載器以及了解類加載器的層次結(jié)構(gòu):
public class ClassloaderDemo{ public static void main(String[] args){ System.out.printlb(ClassLoaderDemo.class.getClassLoader().getName()); System.out.println(System.class.getClassloader()); ClassLoader classloader = ClassLoaderDemo.class.getClassLoader()); while(loader!=null){ System.out.println(loader.getClass().getName()); loader=loader.getParent(); } } |
注意:程序會(huì)拋出異常,因?yàn)镴VM根類加載器不是Java類。
2.類的加載機(jī)制,如圖所示:
<1>全盤負(fù)責(zé):所謂全盤負(fù)責(zé),就是說當(dāng)一個(gè)類加載器負(fù)責(zé)加載某個(gè)Class的時(shí)候,該Class所依賴的和引用的其他Class也將由該類加載器負(fù)責(zé)載入,除非顯式使用另外一個(gè) 類加載器來實(shí)現(xiàn)載入。
<2>父類委托:意思是先讓父類加載器試圖加載該Class,只有父類加載器無法加載該類是才嘗試從自己的路徑中加載該類。
<3>緩存機(jī)制:緩存機(jī)制將會(huì)保證所有被加載過的Class都會(huì)被緩存,當(dāng)程序中需要使用某個(gè)Class時(shí),類加載器先從緩存中搜索該Class,只有當(dāng)緩存中不存在該Class對(duì)象 時(shí),系統(tǒng)才重新讀取該類對(duì)應(yīng)的二進(jìn)制數(shù)據(jù)。這就是為什么我們修改Class后,JVM必須重新啟動(dòng),修改才生效的原因。
類加載器的父子關(guān)系:用戶類加載器—>系統(tǒng)類加載器—>擴(kuò)展類加載器—>根類加載器
類加載機(jī)制:
<1>當(dāng)JVM需要加載一個(gè)類是,到底指派哪個(gè)類加載器去加載呢?
首先當(dāng)前線程的類加載器去加載線程中的第一個(gè)類,如果A類中引用了B類,JVM將使用加載A類的加載器來加載B類,最后還可以調(diào)用ClassLoader。loadeClass方法指定 某個(gè)類加載器去加載某個(gè)類。
<2>每個(gè)類加載器在加載類時(shí),先委托給其上級(jí)加載器。
注意兩點(diǎn):
當(dāng)所有的祖宗類加載器都沒有加載到類,回到發(fā)起類加載器,還加載不了,那么程序?qū)伋鯟lassNotFoundExcetpion,而不是去找發(fā)起類加載器的兒子,因?yàn)闆]有 getChild ()方法,即使有,那么選擇哪一個(gè)兒子加載器呢?
面試題:能不能自己寫一個(gè)類叫Java.lang.System?
答案:可以寫,但是因?yàn)镴VM委托機(jī)制的存在,會(huì)先找到JVM根類加載器,我自己寫也可以,那么我要拋開委托加載機(jī)制,我自己指定一個(gè)ClassLoader。
3.自定義類加載器
JVM中除了根類加載器之外的所有類加載器都是classloader的子類實(shí)例,我們完全可以通過擴(kuò)展ClassLoader的子類,并重寫ClassLoader所包含的的方法來實(shí)現(xiàn)自定義類 加載器,ClassLoader有兩個(gè)關(guān)鍵的方法:loadClass(),findClass()。
不過我們一般推薦重寫findClass()方法,而不是loadClass()方法,因?yàn)橹貙慺indClass()可以避免覆蓋默認(rèn)類加載器的父類委托,緩存機(jī)制兩種策略。
下面是我自己編寫的一個(gè)類加載器:
package snippet; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; /** * * @author Administrator *自定義類加載器 */ public class MyClassLoader extends ClassLoader { // 獲取java源文件的二進(jìn)制碼 public byte[] getBytes(String filename){ File file = new File(filename); InputStream ips=null; byte[] b = new byte[(int) file.length()]; try { ips = new FileInputStream(file); int raw =ips.read(b); if(raw!=file.length()){ throw new IOException("無法完整讀取文件"); } } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally{ if(ips!=null){ try { ips.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } return b; } public boolean compile(String javaFile){ System.out.println("正在編譯"); Process p=null; try { //調(diào)用系統(tǒng)javac命令 p=Runtime.getRuntime().exec("javac" + javaFile); try { p.waitFor(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } int ret = p.exitValue(); return ret==0; } @Override protected Class<?> findClass(String name) { Class<?> clazz=null; String fileStub = name.replace(".", "/"); String javaFileName = fileStub + ".java"; String classFileName = fileStub + ".class"; File javaFile = new File(javaFileName); File classFile = new File(classFileName); //如果java源文件存在并且class文件不存在,或者java源文件比class文件修改的時(shí)間晚 if(javaFile.exists()&&(!classFile.exists()||javaFile.lastModified()>classFile.lastModified())){ if(!compile(javaFileName)||!classFile.exists()){ try { throw new ClassNotFoundException("未發(fā)現(xiàn)class文件"); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } //如果class文件已經(jīng)存在,那么直接生成字節(jié)碼 if(classFile.exists()){ byte[] b = getBytes(classFileName); clazz = defineClass(name, b, 0, b.length); } //如果為空,標(biāo)明加載失敗 if(clazz==null){ try { throw new ClassNotFoundException(name); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } return clazz; } } |
上面代碼重寫了findClass方法,通過重寫該方法就可以實(shí)現(xiàn)自定義的類加載機(jī)制。
學(xué)習(xí)總結(jié):1.了解了JVM三種類加載器(根類加載器,系統(tǒng)類加載器,擴(kuò)展類加載器),明白了三種類加載器的作用和范圍
2.學(xué)習(xí)了JVM三種類加載機(jī)制(父類委托,緩存,全盤負(fù)責(zé))
3.學(xué)習(xí)了如何自定義類加載器,通過繼承ClassLoader類,特別要注意兩個(gè)關(guān)鍵方法:loadClass()和findClass()兩種方法的機(jī)制和不同。
posted on 2013-12-24 11:50 順其自然EVO 閱讀(181) 評(píng)論(0) 編輯 收藏