文章目录
一、类加载器介绍
1)引导类加载器(BootstrapClassLoader)
也叫启动类或根类加载器,它用来加载 Java 的核心类,是用原生代码来实现的,并不继承自 java.lang.ClassLoader(负责加载$JAVA_HOME中jre/lib/rt.jar里所有的class,由C++实现,不是ClassLoader子类)。由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以不允许直接通过引用进行操作。
2)扩展类加载器(ExtensionsClassLoader)
它负责加载JRE的扩展目录,lib/ext或者由java.ext.dirs系统属性指定的目录中的JAR包的类。由Java语言实现,父类加载器为null。
3)系统类加载器(SystemClassLoader)
被称为系统(也称为应用)类加载器,它负责在JVM启动时加载来自Java命令的-classpath选项、java.class.path系统属性,或者CLASSPATH换将变量所指定的JAR包和类路径。程序可以通过ClassLoader的静态方法getSystemClassLoader()来获取系统类加载器。如果没有特别指定,则用户自定义的类加载器都以此类加载器作为父加载器。由Java语言实现,父类加载器为ExtClassLoader。
二、自定义加载器
所有的加载器除了引导类加载器
,其他都是直接或间接的继承了ClassLoader
,调用的方法是使用它的loadClass
方法进行调用,加载要加载的class
文件产生Class
对象使用。这里自定义的加载器要重新findClass
方法并通过defineClass
方法返回创建的Class
对象。
1、创建思路
1)获取class
的路径
2)读取class
的字节码
3)调用defineClass
方法
/**
* 参数一是类的全限定名称
* 参数二是字节码
* 参数三是读取字节码的开始偏移量
* 参数四是读取字节码的结束偏移量
* 这里表示读取全部字节码
*/
Class<?> defineClass(name, classData, 0, classData.length);
4)调用class
文件
2、创建自定义加载器
package com.main.jvmtexst.classloadertest.test2;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
/**
* 自定义加载器
*/
public class CustomizeClassLoader extends ClassLoader {
//目录地址
private String path;
//类全限定名称地址
private String classPath;
//默认使用系统加载器加载
public CustomizeClassLoader(String path) {
this.path = path;
}
//选择自己的父类加载器,如果使用引导类加载器则使用null代替
public CustomizeClassLoader(ClassLoader parent, String path) {
super(parent);
this.path = path;
}
private String getClassPath() {
//处理全限定名,转换成地址形式
List<String> nameList = Arrays.asList(classPath.split("\\."));
StringBuilder nameBuilder = new StringBuilder();
nameList.forEach(item -> nameBuilder.append(item).append(File.separator));
classPath = nameBuilder.toString();
classPath = classPath.substring(0, classPath.length() - 1);
//返回全地址
return path + File.separator + classPath + ".class";
}
/**
* 重写findClass方法,通过landClass访问
*
* @param name 类的全限定名称
* @return 返回Class对象
* @throws ClassNotFoundException
*/
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
this.classPath = name;
byte[] classData = null;
//读取字节码文件流
try (FileInputStream fileInputStream = new FileInputStream(this.getClassPath());
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();) {
int ch = 0;
while ((ch = fileInputStream.read()) != -1) {
byteArrayOutputStream.write(ch);
}
classData = byteArrayOutputStream.toByteArray();
} catch (IOException e) {
throw new ClassNotFoundException(name);
}
/**
* 参数一是类的全限定名称
* 参数二是字节码
* 参数三是读取字节码的开始偏移量
* 参数四是读取字节码的结束偏移量
* 这里表示读取全部字节码
*/
return defineClass(name, classData, 0, classData.length);
}
}
3、创建class测试文件
package com;
public class Test {
public static int v1 = 1;
public Test() {
System.out.println(Test.class.getName() + "调用成功:" + this.hashCode());
System.out.println(Test.class.getName() + "加载器为:" + this.getClass().getClassLoader());
}
}
在根目录运行命令行
javac -d .\class -encoding UTF-8 com\Test.java
此时根目录下的有文件com/Test.class
4、创建测试类
package com.main.jvmtexst.classloadertest.test2;
import java.lang.reflect.Field;
public class CustomizeClassLoaderTest {
public static void main(String[] args) throws Exception {
//设置读取目录
String path = "D:\\selfProjects\\Test\\JAVABASIC\\class\\";
//创建自定义加载器
//使用默认加载器
ClassLoader classLoader1 = new CustomizeClassLoader(path);
//使用引导类加载器
ClassLoader classLoader2 = new CustomizeClassLoader(null, path);
//自定义加载器
ClassLoader classLoader3 = new CustomizeClassLoader(classLoader1, path);
System.out.println("classLoader1的加载器 : " + classLoader1.getParent());
System.out.println("classLoader2的加载器 : " + classLoader2.getParent());
System.out.println("classLoader3的加载器 : " + classLoader3.getParent());
//测试加载指定目录下的类文件
//加载并获取指定类的Class对象
Class loader = classLoader1.loadClass("com.Test");
//通过反射创建实例对象
Object test1 = loader.newInstance();
//获取实例对象的属性
Field v1 = loader.getField("v1");
System.out.println("test1的hashCode : " + test1.hashCode());
System.out.println("获取静态对象v1 : " + v1 + " = " + v1.getInt(test1));
}
}
测试结果:
classLoader1的加载器 : sun.misc.Launcher$AppClassLoader@18b4aac2
classLoader2的加载器 : null
classLoader3的加载器 : com.main.jvmtexst.classloadertest.test2.CustomizeClassLoader@2ef3eef9
com.Test调用成功:1201484275
com.Test加载器为:com.main.jvmtexst.classloadertest.test2.CustomizeClassLoader@2ef3eef9
test1的hashCode : 1201484275
获取静态对象v1 : public static int com.Test.v1 = 1
这样我们就完成了自定义加载类的创建和使用了。