JAVA 类加载器 的问题?

虚拟机规范规定了类加载器的父委托机制,但是由于运行时包(命名空间)的问题,存在隔离,书中提到,但凡是经过的类加载都会保存一份class文件,但是我运行自定义类加载器,多次加载java.lang.String类,为什么第二次没有在本地没有缓存,是忘记需要重写那个方法吗?

图片描述

相关代码

public class MyClassLoader extends ClassLoader {

  private static Path classDir = Paths.get("/Users/echo/");

  public MyClassLoader() {
    super();
  }

  @Override
  protected Class<?> findClass(String name) throws ClassNotFoundException {
    byte[] classBytes = readClassBytes(name);
    if (null == classBytes || classBytes.length == 0) {
      throw new ClassNotFoundException("can not load the class " + name + ".");
    }
    return defineClass(name, classBytes, 0, classBytes.length);
  }

  private byte[] readClassBytes(String name) throws ClassNotFoundException {
    String classPath = name.replace(".", File.separator);
    Path classFullPath = classDir.resolve(Paths.get(classPath + ".class"));
    if (!classFullPath.toFile().exists()) {
      throw new ClassNotFoundException("the class " + name + " not found.");
    }
    try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
      Files.copy(classFullPath, bos);
      return bos.toByteArray();
    } catch (IOException e) {
      throw new ClassNotFoundException("load the class " + name + " occur error.", e);
    }
  }

  @Override
  protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
    synchronized (getClassLoadingLock(name)) {
      Class<?> c = findLoadedClass(name);
      if (c != null) {
        System.out.println("在自定义加载器缓存中找到.");
      }
      if (c == null) {
        long t0 = System.nanoTime();
        try {
          if (getParent() != null) {
            c = getParent().loadClass(name);
            if (c != null) {
              System.out.println("在父类加载器中找到.");
            }
          } else {
            c = getSystemClassLoader().loadClass(name);
            if (c != null) {
              System.out.println("直接在根加载器中找到.");
            }
          }
        } catch (ClassNotFoundException e) {

        }
        if (c == null) {
          long t1 = System.nanoTime();
          c = findClass(name);
          if (c != null) {
            System.out.println("在自定义加载器中找到.");
          }
        }
      }
      if (resolve) {
        resolveClass(c);
      }
      return c;
    }
  }
}
    MyClassLoader classLoader = new MyClassLoader();
    classLoader.loadClass("java.lang.String");
    try {
      TimeUnit.SECONDS.sleep(5);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    classLoader.loadClass("java.lang.String");
阅读 2.7k
2 个回答

看你的代码你是自己读取并加载了类的,这种情况需要你自己缓存已加载的类。Java提供的几个标准类加载器都对经过其加载的类做了缓存,所以如果你把类加载委托给这些类加载器的话是会自动实现缓存的。

一般来说,类只会加载一次,不需要反复加载的,所以没有缓存一说。而你这里手动再加载了一次,于是会有两次相同的输出。“凡是经过的类加载都会保存一份class文件”这句话我觉得有问题,应该是加载过的类都会产生一个Class对象(而不是文件),放在堆中维护,而这个Class对象也不充当缓存的角色,再加载就会被覆盖。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题