本次尝试在不突破双亲委派模型的基础上,自定义类加载器,以理解Java类加载的流程。
既然不突破双亲委派,那就只重写findClass方法:
package classloader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
public class MyClassLoader extends ClassLoader {
@Override
public Class<?> findClass(String name) {
FileInputStream in;
try {
in = new FileInputStream(
new File("/Users/cz/java/" + name.replace( '.', '/') + ".class"));
ByteArrayOutputStream out = new ByteArrayOutputStream();
int ch = 0;
while ((ch = in.read()) != -1) {
out.write(ch);
}
byte[] data = out.toByteArray();
return defineClass(name, data, 0, data.length);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static void main(String[] args) throws Exception {
MyClassLoader myClassLoader1 = new MyClassLoader();
Class<?> clazz1 = myClassLoader1.loadClass("CZ");
System.out.println(clazz1.getClassLoader());
MyClassLoader myClassLoader2 = new MyClassLoader();
Class<?> clazz2 = myClassLoader2.loadClass("CZ");
System.out.println(clazz2.getClassLoader());
// 验证isInstance方法是否会比较classloader
Object obj1 = clazz1.newInstance();
Object obj2 = clazz2.newInstance();
System.out.println(clazz1.isInstance(obj1));
System.out.println(clazz1.isInstance(obj2));
}
}
在不指定父类加载器的情况下,类ClassLoader默认的构造方法里将父类加载器设置为了系统类加载器(AppClassLoader)
protected ClassLoader() {
this(checkCreateClassLoader(), getSystemClassLoader());
}
所以我在findClass方法里面指定了路径,不在classpath里面,所以不会被AppClassLoader加载。
另外,我还验证了一下Class类的isInstance方法。同样的一份class文件,被两个类加载器加载后形成了两个类型。
下面是运行结果:
classloader.MyClassLoader@7852e922
classloader.MyClassLoader@70dea4e
true
false
下面再试试自定义类加载器去加载classpath里面的class文件
如果你的父类加载器是AppClassLoader的话,根据双亲委派模型,是轮不到你的。所以我这里必须指定父类加载器,而且优先级必须要高于AppClassLoader。
package classloader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
public class MyClassLoader2 extends ClassLoader {
public MyClassLoader2() {
}
public MyClassLoader2(ClassLoader parent) {
super(parent);
}
@Override
public Class<?> findClass(String name) {
FileInputStream in;
try {
in = new FileInputStream(
new File("/Users/cz/myapp/workspace/JVM/bin/" +
name.replace( '.', '/') + ".class"));
ByteArrayOutputStream out = new ByteArrayOutputStream();
int ch = 0;
while ((ch = in.read()) != -1) {
out.write(ch);
}
byte[] data = out.toByteArray();
return defineClass(name, data, 0, data.length);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static void main(String[] args) throws Exception {
// null代表启动类加载器
MyClassLoader2 classLoader1 = new MyClassLoader2(null);
Class<?> clazz1 = classLoader1.loadClass("classloader.MyClassLoader2");
System.out.println(clazz1.getClassLoader());
MyClassLoader2 classLoader2 = new MyClassLoader2();
Class<?> clazz2 = classLoader2.loadClass("classloader.MyClassLoader2");
System.out.println(clazz2.getClassLoader());
Object obj = clazz2.newInstance();
System.out.println(clazz1.isInstance(obj));
}
}
总共生成了2个类加载器,classLoader1的父类加载器是启动类加载器,classLoader2的父类加载器是系统类加载器(达不到我们的要求)。
运行结果:
classloader.MyClassLoader2@7852e922
sun.misc.Launcher$AppClassLoader@2a139a55
false