類加載器負責將.class文件加載到內存中,并為類生成一個java.lang.Class實例。
一旦一個類被加載入JVM中,同一個類就不會被再次加入了。在JVM中用來判斷類的唯一性標識是:類名、類所在的包名和類加載器。
當JVM啟動時,會形成由三個類加載器組成的初始類加載器層次結構:
根類加載器,負責加載java的核心類(即JAVA_HOME/jre/lib下的jar包)。當使用-Xbootclasspath選項或使用-D選項指定sun.boot.class.path系統屬性也可以指定加載附加的類。根類加載器比較特殊,他并不是java.lang.ClassLoader的子類,而是由JVM自身實現的。
擴展類加載器,負責加載JRE的擴展目錄(即JAVA_HOME/jre/lib/ext目錄)下的jar包。
系統類加載器,負責在JVM啟動時,加載來自命令java中的-classpath選項或java.class.path系統屬性,或CLASSPATH環境變量所指定的jar包和類路徑,或者是當前類路徑。程序可以通過ClassLoader的靜態方法getSystemClassLoader()方法獲取系統類加載器。
除了Java提供的這三種類加載器,開發者也可以實現自己的類加載器,自定義的類加載器通過繼承ClassLoader來實現。
JVM中這四種類加載器的層次結構如圖:
如圖所示:根類加載器是擴展類加載器的父類加載器;擴展類加載器是系統類加載器的父類加載器;如果沒有特別指定,用戶自定義的類加載器都以系統類加載器作為父加載器。
需要提下,類加載器之間的父子關系并不是類繼承上的父子關系,而是類加載器實例之間的關系。
類加載機制JVM的類加載機制主要有如下三種機制:
類加載器加載類大致要經過8個步驟:
JVM中除了根類加載器之外的類加載器都是ClassLoader的子類的實例。開發者可以通過繼承ClassLoader,并重寫ClassLoader的方法來實現自定義類加載器。
ClassLoader類有三個關鍵方法:
要實現自定義的ClassLoader,可以通過重寫loadClass或findClass來實現。不過一般推薦重寫findClass,而不是loadClass,看一下loadClass的執行步驟:
從上面看來,采用重寫findClass的方法可以避免覆蓋默認類加載器的父類委托和緩存機制兩種策略。
以下是一個自定類加載器的實例:
package com.zhyea.test;import java.io.File;import java.io.FileInputStream;import java.io.IOException;import java.io.InputStream;import java.lang.reflect.Method;/** * 自定義類加載器實例 * @author robin * */public class MyClassLoader extends ClassLoader { /** * 讀取類文件 * * @param filePath * 類文件路徑 * @return * @throws IOException */ PRivate byte[] getBytes(String filePath) throws IOException { InputStream in = null; try { File file = new File(filePath); long length = file.length(); in = new FileInputStream(file); byte[] buffer = new byte[(int) length]; int hasRead = in.read(buffer); if (hasRead != length) { throw new IOException("無法讀取全部文件:" + hasRead + "!=" + length); } return buffer; } finally { if (null != in) in.close(); } } /** * 編譯Java文件 * * @param javaFile * Java文件 * @return * @throws IOException * @throws InterruptedException */ private boolean compile(String javaFile) throws IOException, InterruptedException { // 調用系統javac命令 Process p = Runtime.getRuntime().exec("javac " + javaFile); // 強制其他線程等待這個線程完成 p.waitFor(); // 獲取javac線程的退出值 int rtn = p.exitValue(); // 返回編譯是否成功 return rtn == 0; } /** * 重寫ClassLoader的findClass類 */ @Override protected Class<?> findClass(String name) throws ClassNotFoundException{ Class<?> clazz = null; String javaPath = name.replace(".", "/"); String javaFileName = javaPath + ".java"; String classFileName = javaPath = ".class"; File javaFile = new File(javaFileName); File classFile = new File(classFileName); if(javaFile.exists() && (!classFile.exists() || javaFile.lastModified()>classFile.lastModified())){ try{ if(!compile(javaFileName) || !classFile.exists()){ throw new ClassNotFoundException("Class not found : " + javaFileName); } }catch(Exception e){ e.printStackTrace(); } } if(classFile.exists()){ try{ byte[] raw = getBytes(classFileName); clazz = defineClass(name, raw, 0, raw.length); }catch(Exception e){ e.printStackTrace(); } } if(null==clazz){ throw new ClassNotFoundException(name); } return clazz; } public static void main(String[] args) throws Exception{ String javaName = "com.zhyea.test.MyTest"; MyClassLoader mcl = new MyClassLoader(); Class<?> clazz = mcl.loadClass(javaName); Method main = clazz.getMethod("main", (new String[0]).getClass()); Object[] arrs = {args}; main.invoke(null, arrs); } }
再附上演示用的測試類,其實很簡單了:
package com.zhyea.test;/** * 自定義類加載器演示用的測試類 * @author robin * */public class MyTest { public static void main(String[] args) { System.out.println("This is a Test!"); }}
使用自定義的類加載器,可以實現如下功能:
URLClassLoader是擴展類加載器類和系統類加載器類的父類。URLClassLoader功能比較強大,可以從本地獲取java文件來加載類,也可獲取遠程文件來加載類。
演示下:
package com.zhyea.test;import java.net.URL;import java.net.URLClassLoader;public class UCLTest { public static void main(String[] args) throws Exception { URL[] urls = {new URL("file:com.zhyea.test.MyTest.java")}; URLClassLoader ucl = new URLClassLoader(urls); MyTest mt = (MyTest) ucl.loadClass("com.zhyea.test.MyTest").newInstance(); mt.test(); ucl.close(); }}package com.zhyea.test;/** * URLClassLoader演示用的測試類 * @author robin * */public class MyTest { public void test(){ System.out.println("This is a Test 2!"); }}
新聞熱點
疑難解答