自定义类加载器
自定义类加载器只需要继承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内,两个相同包名和类名的类对象可以共存,因为他们的类加载器可以不一样,所以看两个类对象是否是同一个,除了看类的包名和类名是否都相同之外,还需要他们的类加载器也是同一个才能认为他们是同一个。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!