进阶Java编程(11)ClassLoader类加载器【待完成】
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之中针对于类加载器提供有双亲加载机制,如果现在要加载的类是由系统提供的类则会由系统类进行加载,如果现在开发者定义的类与系统类定义的名称相同,那么【为了保证系统的安全性】绝对不会加载。