自定义类加载器

自定义类加载器只需要继承java.lang.ClassLoader类,该类有两个核心方法,一个是loadClass(String, boolean),实现了双亲委派机制,还有一个方法是findClass,默认实现是空方法,所以我们自定义类加载器主要是重写findClass方法。

public class MyClassLoaderTest {
static class MyClassLoader extends ClassLoader{
//指定类加载器加载哪个路径下的类
private String classPath;
public MyClassLoader(String classPath) {
this.classPath = classPath;
}
//name为类的全限定名 (com.mafu.User) 把指定的路径下的.class文件读入到字节数组中
private byte[] loadByte(String name) throws Exception {
name = name.replace('.', '/');
FileInputStream fileInputStream = new FileInputStream(classPath + "/" + name + ".class");
int len = fileInputStream.available();
byte[] data = new byte[len];
fileInputStream.read(data);
fileInputStream.close();
return data;
}
//把.class文件二进制数组转为了一个Class对象
@Override
protected Class<?> findClass(String name){
try {
byte[] data = loadByte(name);
return defineClass(name,data,0,data.length);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}
//测试 main方法
public static void main(String[] args) throws Exception {
MyClassLoader classLoader=new MyClassLoader("D:/test");
Class clazz=classLoader.loadClass("com.mafu.jvm.User");
System.out.println(clazz.getClassLoader().getClass().getName());
//打印结果为:com.mafu.jvm.MyClassLoaderTest$MyClassLoader
}
}

打破双亲委派机制

//重写父类的loadClass方法,其实就把委托父类加载的逻辑代码去掉,用自己写的类加载器加载
protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException{
synchronized (getClassLoadingLock(name)) {
Class<?> c = findLoadedClass(name);
/** 去掉以下被注释的代码就不会向上委托给父类加载器去加载了
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
*/
if (c == null) {
//检查到当前类未被加载过就进来执行以下代码
long t0 = System.nanoTime();
long t1 = System.nanoTime();
if(name.startsWith("com.mafu.jvm")){
//如果是自己的包(指定的包下的类)就直接加载打破双亲委派
c = findClass(name);
}else {
//否则其他的就走双亲委派加载机制
c = this.getParent().loadClass(name);
}
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
// }
if (resolve) {
resolveClass(c);
}
return c;
}
}

注意:同一个JVM内,两个相同包名和类名的类对象可以共存,因为他们的类加载器可以不一样,所以看两个类对象是否是同一个,除了看类的包名和类名是否都相同之外,还需要他们的类加载器也是同一个才能认为他们是同一个。

posted @   程序马2023  阅读(176)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示