深入理解和探究Java類加載機(jī)制----1.java.lang.ClassLoader類介紹java.lang.ClassLoader類的基本職責(zé)就是根據(jù)一個指定的類的名稱,找到或者生成其對應(yīng)的字節(jié)代碼,然后從這些字節(jié)代碼中定義出一個Java 類,即 java.lang.Class類的一個實(shí)例。 ClassLoader提供了一系列的方法,比較重要的方法如: 2.JVM中類加載器的樹狀層次結(jié)構(gòu)Java 中的類加載器大致可以分成兩類,一類是系統(tǒng)提供的,另外一類則是由 Java 應(yīng)用開發(fā)人員編寫的。 引導(dǎo)類加載器(bootstrap class loader): 它用來加載 Java 的核心庫(jre/lib/rt.jar),是用原生C++代碼來實(shí)現(xiàn)的,并不繼承自java.lang.ClassLoader。 加載擴(kuò)展類和應(yīng)用程序類加載器,并指定他們的父類加載器,在java中獲取不到。 擴(kuò)展類加載器(extensions class loader): 它用來加載 Java 的擴(kuò)展庫(jre/ext/*.jar)。Java 虛擬機(jī)的實(shí)現(xiàn)會提供一個擴(kuò)展庫目錄。該類加載器在此目錄里面查找并加載 Java 類。 系統(tǒng)類加載器(system class loader): 它根據(jù) Java 應(yīng)用的類路徑(CLASSPATH)來加載 Java 類。一般來說,Java 應(yīng)用的類都是由它來完成加載的??梢酝ㄟ^ ClassLoader.getSystemClassLoader()來獲取它。 自定義類加載器(custom class loader): 除了系統(tǒng)提供的類加載器以外,開發(fā)人員可以通過繼承 java.lang.ClassLoader類的方式實(shí)現(xiàn)自己的類加載器,以滿足一些特殊的需求。
以下測試代碼可以證明此層次結(jié)構(gòu): public class testClassLoader { @Test public void test(){ //application class loader System.out.println(ClassLoader.getSystemClassLoader()); //extensions class loader System.out.println(ClassLoader.getSystemClassLoader().getParent()); //bootstrap class loader System.out.println(ClassLoader.getSystemClassLoader().getParent().getParent()); } } 輸出為: 可以看出ClassLoader類是由AppClassLoader加載的。他的父親是ExtClassLoader,ExtClassLoader的父親無法獲取是因?yàn)樗怯肅++實(shí)現(xiàn)的。 3.雙親委派機(jī)制某個特定的類加載器在接到加載類的請求時,首先將加載任務(wù)委托交給父類加載器,父類加載器又將加載任務(wù)向上委托,直到最父類加載器,如果最父類加載器可以完成類加載任務(wù),就成功返回,如果不行就向下傳遞委托任務(wù),由其子類加載器進(jìn)行加載。 雙親委派機(jī)制的好處: 保證java核心庫的安全性(例如:如果用戶自己寫了一個java.lang.String類就會因?yàn)殡p親委派機(jī)制不能被加載,不會破壞原生的String類的加載) 代理模式 與雙親委派機(jī)制相反,代理模式是先自己嘗試加載,如果無法加載則向上傳遞。tomcat就是代理模式。
4.自定義類加載器public class MyClassLoader extends ClassLoader{ private String rootPath; public MyClassLoader(String rootPath){ this.rootPath = rootPath; } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { //check if the class have been loaded Class<?> c = findLoadedClass(name); if(c!=null){ return c; } //load the class byte[] classData = getClassData(name); if(classData==null){ throw new ClassNotFoundException(); } else{ c = defineClass(name,classData, 0, classData.length); return c; } } private byte[] getClassData(String className){ String path = rootPath+"/"+className.replace('.', '/')+".class"; InputStream is = null; ByteArrayOutputStream bos = null; try { is = new FileInputStream(path); bos = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int temp = 0; while((temp = is.read(buffer))!=-1){ bos.write(buffer,0,temp); } return bos.toByteArray(); } catch (Exception e) { e.printStackTrace(); }finally{ try { is.close(); bos.close(); } catch (Exception e) { e.printStackTrace(); } } return null; } } 測試自定義的類加載器 創(chuàng)建一個測試類HelloWorld package testOthers; public class HelloWorld { } 在D盤根目錄創(chuàng)建一個testOthers文件夾,編譯HelloWorld.java,將得到的class文件放到testOthers文件夾下。 利用如下代碼進(jìn)行測試 public class testMyClassLoader { @Test public void test() throws Exception{ MyClassLoader loader = new MyClassLoader("D:"); Class<?> c = loader.loadClass("testOthers.HelloWorld"); System.out.println(c.getClassLoader()); } } 輸出: 說明HelloWorld類是被我們的自定義類加載器MyClassLoader加載的
5.類加載過程詳解JVM將類加載過程分為三個步驟:裝載(Load),鏈接(Link)和初始化(Initialize) 1) 裝載: 查找并加載類的二進(jìn)制數(shù)據(jù); 2)鏈接: 驗(yàn)證:確保被加載類信息符合JVM規(guī)范、沒有安全方面的問題。 準(zhǔn)備:為類的靜態(tài)變量分配內(nèi)存,并將其初始化為默認(rèn)值。 解析:把虛擬機(jī)常量池中的符號引用轉(zhuǎn)換為直接引用。 3)初始化: 為類的靜態(tài)變量賦予正確的初始值。 ps:解析部分需要說明一下,Java 中,虛擬機(jī)會為每個加載的類維護(hù)一個常量池【不同于字符串常量池,這個常量池只是該類的字面值(例如類名、方法名)和符號引用的有序集合。 而字符串常量池,是整個JVM共享的】這些符號(如int a = 5;中的a)就是符號引用,而解析過程就是把它轉(zhuǎn)換成指向堆中的對象地址的相對地址。
類的初始化步驟: 1)如果這個類還沒有被加載和鏈接,那先進(jìn)行加載和鏈接 2)假如這個類存在直接父類,并且這個類還沒有被初始化(注意:在一個類加載器中,類只能初始化一次),那就初始化直接的父類(不適用于接口) 3)如果類中存在static標(biāo)識的塊,那就依次執(zhí)行這些初始化語句。
|
|
來自: liang1234_ > 《jvm》