1,ClassLoader類加載器簡介
在Java里面提供一個系統的環境變量:ClassPath,這個屬性的作用主要是在JVM進程啟動的時候進行類加載路徑的定義,在JVM里面可以根據類加載器而后進行指定路徑中類的加載,也就是說找到了類的加載器就意味着找到了類的來源。
系統類的加載器
如果說現在要想獲得類的加載器,那么一定要通過ClassLoader來獲取,而要想獲取ClassLoader類的對象,則必須利用class類【反射的根源】實現,方法:public ClassLoader getClassLoader(),當獲取了ClassLoader之后還可以繼續獲取其父類的ClassLoader類對象:public final ClassLoader getParent();
·范例:觀察類加載器
1 class Message { 2 } 3 public class JavaAPIDemo { 4 public static void main(String[] args) { 5 Class<?> clazz=Message.class; 6 System.out.println(clazz.getClassLoader());//獲取當前類的加載器 7 System.out.println(clazz.getClassLoader().getParent());//獲取父類加載器 8 System.out.println(clazz.getClassLoader().getParent().getParent());//獲取祖父類加載器 9 } 10 } 11 /* 12 jdk.internal.loader.ClassLoaders$AppClassLoader@2437c6dc 13 jdk.internal.loader.ClassLoaders$PlatformClassLoader@1e643faf 14 null 15 */
從JDK1.8之后的版本【JDK1.9和JDK1.10】提供有一個【PlatformClassLoader】類加載器,而在JDK1.8以及以前的版本里面提供的加載器為【ExtClassLoader】,因為在JDK的安裝目錄里面提供又一個ext的目錄,開發者可以將【*.jar】文件拷貝到此目錄里面,這樣就可以直接執行了,但是這樣的處理開發並不安全,最初的時候也是不提倡使用的,所以從JDK1.9開始將其徹底廢除,同時為了和系統加載器與應用類加載器之間保持設計的平衡,提供有平台類加載器。
當你獲得了類加載器之后就可以利用類加載器來實現類的反射加載處理。
2,自定義ClassLoader處理
清楚了類加載器的功能之后,就可以根據自身的需要實現自定義的類加載器,但是千萬要記住一點:自定義類加載器其加載的順序是在所有系統類加載器的最后。系統類中的加載器都是根據CLASSPATH找到的路徑進行類加載的,而如果有了自定義類的加載器,就可以由開發者任意指派類的加載位置。
①隨意編寫一個程序類,並且將這個類保存在磁盤上;
1 import java.io.*; 2 3 public class MufasaClassLoader extends ClassLoader { 4 private static final String MESSAGE_CLASS_PATH="D:"+ File.separator+"Message.class"; 5 /** 6 * 進行指定類的加載 7 * @param className 類的完整名稱【包.類】 8 * @return 返回一個指定類的class對象 9 * @throws Exception 如果類文件不存在則無法加載 10 */ 11 public Class<?> loadData(String className)throws Exception{ 12 byte[] data=this.loadClassData();//讀取二進制數據文件 13 if(data!=null){//讀取到數據 14 return super.defineClass(className,data,0,data.length); 15 } 16 return null; 17 18 } 19 private byte[] loadClassData()throws Exception{//通過文件進行類的加載 20 InputStream input=null; 21 ByteArrayOutputStream bos=null;//將數據加載到內存之中 22 byte[] data=null; 23 try{ 24 bos=new ByteArrayOutputStream();//實例化內存流 25 input=new FileInputStream(new File(MESSAGE_CLASS_PATH));//文件流加載 26 // byte[] data=new byte[1024];//進行讀取方式① 27 input.transferTo(bos);//讀取數據方式② 28 data= bos.toByteArray();//將所有讀取到的字節數組取出 29 }catch (Exception e){ 30 31 }finally { 32 if(input!=null){ 33 input.close(); 34 } 35 if(bos!=null){ 36 bos.close(); 37 } 38 } 39 return data; 40 } 41 }
④編寫測試類實現類加載控制;
1 package cn.mufasa.demo; 2 import cn.mufasa.util.MufasaClassLoader; 3 import java.lang.reflect.Method; 4 5 public class JavaAPIDemo1 { 6 public static void main(String[] args) throws Exception { 7 MufasaClassLoader classLoader=new MufasaClassLoader(); 8 Class<?> cls=classLoader.loadData("cn.mufasa.util.Message");//進行類的加載 9 System.out.println(cls); 10 Object obj=cls.getDeclaredConstructor().newInstance(); 11 Method method=cls.getDeclaredMethod("send"); 12 method.invoke(obj); 13 } 14 } 15 /* 16 讀取到數據 17 class cn.mufasa.util.Message 18 www.cnblogs.com 19 */
如果在以后結合網絡開發的話,就可以通過一個遠程的服務器來確定類的功能。
⑤觀察當前的Message類的加載器的情況
package cn.mufasa.demo; import cn.mufasa.util.MufasaClassLoader; import java.lang.reflect.Method; public class JavaAPIDemo1 { public static void main(String[] args) throws Exception { MufasaClassLoader classLoader=new MufasaClassLoader(); Class<?> cls=classLoader.loadData("cn.mufasa.util.Message");//進行類的加載 // System.out.println(cls); System.out.println(cls.getClassLoader());//獲取當前類的加載器 System.out.println(cls.getClassLoader().getParent());//獲取父類加載器 System.out.println(cls.getClassLoader().getParent().getParent());//獲取祖父類加載器 } } /* cn.mufasa.util.MufasaClassLoader@668bc3d5 jdk.internal.loader.ClassLoaders$AppClassLoader@2437c6dc jdk.internal.loader.ClassLoaders$PlatformClassLoader@3fb6a447 */
如果說你先在定義了一個類,這個類的名稱定義為:java.lang.String,並且利用了自定義類加載器進行加載處理,這個類將不會被加載,原因:Java之中針對於類加載器提供有雙親加載機制,如果現在要加載的類是由系統提供的類則會由系統類進行加載,如果現在開發者定義的類與系統類定義的名稱相同,那么【為了保證系統的安全性】絕對不會加載。