内容学习于:edu.aliyun.com
1. 类加载器简介
所有的Java程序的执行流程: JVM进程加载字节码文件,随后进行字节码文件的虚拟机解析,并且得到一个结果。
经过分析可以发现,所有的“*.class"文件在磁盘上都会以二进制数据的形式保存(反射机制提供的就是二进制文件的解析能力),但是如果要想进行解析,那么首先要做到类的信息加载,于是在JDK之中针对于信息的加载提供有一个ClassLoader的加载器,如果要想观察ClassLoader可以直接利用Class类的方法来完成:
- public ClassLoader getClassLoader()
获得了ClassLoader之后就可以依据ClassLoader继续获取它之上的各个类加载器,也可以依据此ClassLoader找到对应的二进制字节码数据的信息,这个类里面重要的方法是获取父加载器: public final ClassL oader getParent()。
代码:
class Ball{}
public class JavaAPIDemo {
public static void main(String[] args) throws Exception {
System.out.println(Ball.class.getClassLoader());
System.out.println(Ball.class.getClassLoader().getParent());
System.out.println(Ball.class.getClassLoader().getParent().getParent());
}
}
结果:
jdk.internal.loader.ClassLoaders$AppClassLoader@15db9742
jdk.internal.loader.ClassLoaders$PlatformClassLoader@85ede7b
null
在Java之中为了防止程序可能出现的漏洞,例如:开发者自己定义了一个“java.lang.String”, 并且在里面安装有一些恶意的代码,那么请问,这个时候如何去防止这个恶性程序类被加载呢?所以在类加载器中专门提供了一个双亲加载机制,双亲加载指的是系统类使用系统类自己的加载器处理,而用户的类使用用户的加载器类进行处理,如果该类在系统类加载器中已经加载过了,那么用户加载器将不再加载重复的类。
在JDK 1.8以前,不存在有“PlatformClassLoader", 而使用的是“ExtClassLoader”,主要是可以加载-一个第三方目录中的“*.jar” 文件,但是从JDK 1.9之后该加载器不再使用了。
2. 自定义类加载器
ClassLoader类定义为抽象类,实际上如果有需要的开发者可以自己来定义专属的ClassLoader,假设现在有如下一个程序类。
定义消息处理类:
package com.xzzz.demo;
public class Message {
public void send(String msg){
System.out.println("【消息发送】"+msg);
}
}
2、为了更加方便的观察出ClassLoader特点,下 面将这个类手工进行编译(不打包),将生成的Message.class 文件保存在D盘。
3、利用字节流进行二进制数据的加载,并且将加载到的内容转到ClassLoader之中,就是自定义类加载器。
自定义类加载器:
class ClassLoader extends java.lang.ClassLoader{
private static final String CLASS_FILE = "D:" + File.separator + "Message.class";
public Class<?> loadData (String classname) throws Exception{
byte [] data = this.loadClassData();
if (data!=null){
return super.defineClass(classname,data,0,data.length);
}
return null;
}
private byte[] loadClassData() throws Exception{//加载数据
InputStream input = new FileInputStream(CLASS_FILE);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
input.transferTo(bos);//加载文件到内存流
byte [] data = bos.toByteArray();
input.close();
bos.close();
return data;
}
}
public class JavaAPIDemo {
public static void main(String[] args) throws Exception {
ClassLoader classLoader = new ClassLoader();
Class<?> clazz = classLoader.loadData("com.xzzz.demo.Message");
Object object = clazz.getDeclaredConstructor().newInstance();
Method method = clazz.getDeclaredMethod("send",String.class);
method.invoke(object,"www.mldn.cn");
System.out.println("【类加载器】"+clazz.getClassLoader());
System.out.println("【类加载器】"+clazz.getClassLoader().getParent());
System.out.println("【类加载器】"+clazz.getClassLoader().getParent().getParent());
System.out.println("【类加载器】"+clazz.getClassLoader().getParent().getParent().getParent());
}
}
结果:
【消息发送】www.mldn.cn
【类加载器】com.xzzz.demo.ClassLoader@4c873330
【类加载器】jdk.internal.loader.ClassLoaders AppClassLoader@15db9742【类加载器】jdk.internal.loader.ClassLoadersPlatformClassLoader@41629346
【类加载器】null
这个最主要可以去操作远程的类,比如在加载网路服务器上的。
在现实的对发之中,99%的情况下不需要开发者自定义类加载器,因为一旦定义多了,整个项目容易产生混乱。