ClassLoader类加载器
在Java语言找那个提供一个系统的环境变量CLASSPATH,这个环境属性的作用主要是在JVM进程启动的时候进行类加载路径的定义,在JVM中可以根据类加载器进行指定路径中类的加载,也就是说找到了类加载器就找到了类的来源。
类加载器结构-自上向下
ClassLoader类加载器简介
若想要获得类加载器,那么一定要通过ClassLoader来获取,而要想获取ClassLoader类对象必须使用Class类(反射的根源)实现,方法:
- 获取类加载器对象:
public ClassLoader getClassLoader();
- 父类加载器对象:
public final ClassLoader getParent();
ClassLoader
package Basic;
class Message{}
public class ClassLoader_study {
public static void main(String[] args) {
Class<?> clazz = Message.class;
System.out.println(clazz.getClassLoader()); //获取类加载器
System.out.println(clazz.getClassLoader().getParent()); //获取父类加载器
System.out.println(clazz.getClassLoader().getParent().getParent()); //获取祖父类加载器 - null
}
}
运行结果:
jdk.internal.loader.ClassLoaders A p p C l a s s L o a d e r @ 15 d b 9742 j d k . i n t e r n a l . l o a d e r . C l a s s L o a d e r s AppClassLoader@15db9742 jdk.internal.loader.ClassLoaders AppClassLoader@15db9742jdk.internal.loader.ClassLoadersPlatformClassLoader@28a418fc
null
从JDK1.8之后的版本(JDK1.9/10)提供有一个“PlatformClassLoader”类加载器,而在JDK1.8及以前的版本中提供的加载器为“ExtClassLoader”,因为在JDK安装目录中提供一个ext目录,开发者可以将*.jar文件拷贝到此目录中这样就可以直接执行了,但是这样的处理开发并不安全,最初的时候也是不提倡使用的,从JDK1.9开始彻底废除,同时为了与系统类加载器和应用类加载器之间保持设计的平衡,提供有平台类加载器。
自定义ClassLoader处理类
清楚了类加载器的功能之后就可以根据自己的需要实现自定义类加载器,谨记:自定义类加载器其加载顺序是在所有系统类加载器最后。系统中类加载器都是根据CLASSPATH路径进行类加载的,而如果有了自定义类的加载器就可以由开发者任意指派类的加载位置。
- 随意编写一个程序类,并且将这个类保存在磁盘上。
package cn.mldn.util;
public class Message {
public void send() {
System.out.println("********test********");
}
}
- 将此类直接拷贝到某个目录中,并且不打包(javac Message.java),所以这个类无法通过CLASSPATH正常加载。
- 自定义一个类加载器,并且继承自ClassLoader类,在ClassLoader类中为用户提供一个字节转换为类结构的方法。
3.1. 定义类:protected final Class<?> defineClass(String name,byte[] b,int off,int len) throws ClassFormatError
3.2 编写测试类package cn.mldn.util; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; public class MClassLoader extends ClassLoader { private static final String MESSAGE_CLASS_PATH = "C:\\Project\\Java_study\\src\\文件\\Message.class"; /** * 进行指定类的加载 * @param className 类的完整名称:“包.类” * @return 返回一个指定类的Class对象 * @throws Exception 如果文件不存在无法加载 */ public Class<?> loadData(String className) throws Exception{ byte data[] = this.loadClassData(); //读取二进制数据文件 if(data != null) { super.defineClass(className, data, 0, data.length); } return null; } private byte[] loadClassData() throws Exception{ //通过文件进行类的加载 InputStream input = null; ByteArrayOutputStream bos = null; byte data [] = null; try { bos = new ByteArrayOutputStream(); //实例化内存流对象 input= new FileInputStream(new File(MESSAGE_CLASS_PATH)); //文件流加载 input.transferTo(bos); //读取数据 data = bos.toByteArray(); //将所有读取到的字节数据全部取出 } catch(Exception e) { e.printStackTrace(); } finally { if(input != null) { input.close(); } if(bos != null) { bos.close(); } } return data; } }
package cn.mldn.util; import java.lang.reflect.Method; import cn.mldn.util.MClassLoader; public class main_test { public static void main(String[] args) throws Exception { MClassLoader classLoader = new MClassLoader(); //实例化自定义类加载器 Class<?> cls = classLoader.loadData("cn.mldn.util.Message"); System.out.println(cls); Object obj = cls.getDeclaredConstructor().newInstance(); Method method = cls.getDeclaredMethod("send"); method.invoke(obj); } }
在以后结合网络程序开发时,就可以通过一个远程服务器来确定类的功能。
MClassLoader classLoader = new MClassLoader(); //实例化自定义类加载器
Class<?> cls = classLoader.loadData("cn.mldn.util.Message");
System.out.println(cls.getClassLoader());
System.out.println(cls.getClassLoader().getParent());
System.out.println(cls.getClassLoader().getParent().getParent());
执行结果:
cn.mldn.util.MClassLoader@5305068a //自定义类加载器一定是最后执行的
jdk.internal.loader.ClassLoaders A p p C l a s s L o a d e r @ 15 d b 9742 j d k . i n t e r n a l . l o a d e r . C l a s s L o a d e r s AppClassLoader@15db9742 jdk.internal.loader.ClassLoaders AppClassLoader@15db9742jdk.internal.loader.ClassLoadersPlatformClassLoader@2ff4acd0
如果说现在定义了一个类,类的名字为:java.lang.String,并且利用了自定义的类加载进行加载处理,这个类将不会被加载,Java之中针对类加载器提供有双亲加载机制,如果要加载的程序类是由系统提供的则会由系统类进行加载,若现在开发者定义的类与系统类名称相同,那么为了保证系统的安全性绝对不会加载。