!! 有的时候博客内容会有变动,首发博客是最新的,其他博客地址可能会未同步,认准
https://blog.zysicyj.top
Java 类加载器就是将 Java 字节码文件转换成 Java 类的一种机制。Java 虚拟机会在需要使用某个类时通过类加载器将该类加载进内存并转换成对应的 Java 类。
Java 类加载器主要有三类:Bootstrap ClassLoader、Extension ClassLoader 和 Application ClassLoader,其中 Bootstrap ClassLoader 是由 C++ 语言实现的,其他两个是由 Java 语言实现的。
Java 类加载器的作用在于加载 Java 类,但是有时候默认的类加载器无法满足我们的需求。例如:
自定义类加载器打破的是 Java 的双亲委派机制。Java 类加载器的加载顺序是从下往上走的,即:先由自定义类加载器加载类,如果找不到,再由父加载器加载,如果父加载器也找不到,再由祖先加载器加载,一直到 Bootstrap ClassLoader。这个机制可以保证同一个应用程序中的同一个类只被加载一次。
根据上述机制,我们实现自定义类加载器需要实现以下两个方法:
findClass()
方法: 如果默认加载器找不到类,则调用此方法来查找并加载类。 defineClass()
方法:将字节码转换成 Java 类并返回。 具体实现方式可见以下代码:
public class MyClassLoader extends ClassLoader {
private String classPath;
public MyClassLoader(String classPath, ClassLoader parent) {
super(parent);
this.classPath=classPath;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] classData = getClassData(name);
if (classData == null){
throw new ClassNotFoundException();
}
else{
return defineClass(name,classData,0,classData.length);
}
}
private byte[] getClassData(String className){
String path = classNameToPath(className);
try{
InputStream inputStream = new FileInputStream(path);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int num = 0;
while((num=inputStream.read(buffer))!=-1){
baos.write(buffer,0,num);
}
inputStream.close();
return baos.toByteArray();
}
catch(Exception e) {
return null;
}
}
private String classNameToPath(String className){
return classPath + className.replace(".", "/") + ".class";
}
}
该代码模拟了一个自定义类加载器。在这个例子中,我们在 findClass()
方法中使用 getClassData()
方法来加载类,并在 defineClass()
方法中将字节码转换成 Java 类并返回。
上述示例是一个简单的自定义类加载器示例,其中还有一些细节和注意事项需要引起我们的注意,具体会在后面的章节中提到。
在项目实际中,我们可能需要使用多个自定义类加载器来加载不同的类,这需要我们自己来实现 ClassLoader。下面是一个示例:
public class Test {
public static void main(String[] args) {
String classPath = "F:/work/TestDemo/bin/";
MyClassLoader classLoader1 = new MyClassLoader(classPath, ClassLoader.getSystemClassLoader().getParent());
MyClassLoader classLoader2 = new MyClassLoader(classPath, ClassLoader.getSystemClassLoader().getParent());
try {
Class<?> class1 = classLoader1.loadClass("demo.bean.User");
Class<?> class2 = classLoader2.loadClass("demo.bean.User");
System.out.println(class1 == class2); // false,因为类加载器不一样
Object obj1= class1.newInstance();
Object obj2= class2.newInstance();
Method method=class1.getMethod("setUname", String.class);
method.invoke(obj1, "张三");
method.invoke(obj2,"李四");
Method method1=class1.getMethod("getUname");
Method method2=class2.getMethod("getUname");
System.out.println(method1.invoke(obj1).equals(method2.invoke(obj2)));
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
在这个示例中,我们使用了两个自定义类加载器(classLoader1 和 classLoader2),用它们分别加载了同样的一个类(demo.bean.User)。由于是使用不同的类加载器类加载同样的一个类,所以两个 class 对象是不相等的。我们在 setUname()
方法中设置了 obj1 中的 uName 为“张三”,在 obj2 中设置为“李四”,并在 getUname()
方法中返回了 uName 属性的值,在最终的输出中我们可以看到它们的返回值是不相等的。
loadClass()
方法,所以我们需要注意自定义类加载器的作用范围,避免影响到系统的正确使用。
Java 类加载器是将 Java 字节码文件转换成 Java 类的机制。Java 类加载器分为三种:Bootstrap ClassLoader、Extension ClassLoader 和 Application ClassLoader。
自定义类加载器可以解决一些默认类加载器无法解决的问题,如实现类隔离、解决类的相互依赖关系等。自定义类加载器打破 Java 的双亲委派机制,需要重写 findClass()
方法和 defineClass()
方法。
自定义类加载器有一些缺点,如安全问题、代码复杂度高等,使用时需要注意一些细节和注意事项。
本文由 mdnice 多平台发布