ClassLoader可以用任何东西替换数组

Cod*_*ope 9 java jvm classloader

当我运行以下Java代码时:

ClassLoader c = new ClassLoader() {
    @Override
    public Class<?> findClass(String name) {
        return Object.class;
    }
};

Class<?> cc = c.loadClass(Object[][].class.getName());

System.out.println(cc.getName());
Run Code Online (Sandbox Code Playgroud)

我得到java.lang.Object的显示终端,即使我代替Object[][].class.getName()通过[[Ljava.lang.Object在代码中.问题是我期待控制台显示[[Ljava.lang.Object.

实际上,在JVM规范中,我可以阅读以下内容:

数组类由Java虚拟机(第5.3.3节)直接创建,而不是由类加载器创建.但是,D的定义类加载器用于创建数组类C的过程中.

既然Object[][]是一个数组类,我假设findClass不会使用参数调用我[[Ljava.lang.Object的元素类型java.lang.Object.

此外,在"创建数组类"一节中,实际描述了递归算法:

如果组件类型是引用类型,则使用类加载器L递归地应用本节的算法(第5.3节)以便加载并由此创建C的组件类型.

所以我的问题是:

  • 为什么我得到这个输出?这是否意味着我必须在我的ClassLoader中手动包含这个递归算法,而不是让JVM为我做这个?如果这意味着什么,最好的方法是什么?
  • 我是否误解了第一个引文中的"创建"?它只是意味着我不能创建运行时数组类,但我仍然可以修补它的加载?

shm*_*sel 4

您询问的是 JVM 规范,但您的测试演示了一个独立类的行为java.lang.ClassLoader,该类“由 Java 虚拟机调用来解析类引用”。如果 JVM 正在加载数组类,它将完全绕过类加载器。这可以通过让 JVM 尝试使用自定义类加载器加载类来演示:

Class<?> clazz = Class.forName("[[Lcom.foo.Test;", true, new ClassLoader() {
    @Override
    public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
        System.out.println("Loading " + name);
        return super.loadClass(name, resolve);
    }
});
System.out.println(clazz);
Run Code Online (Sandbox Code Playgroud)

输出:

加载 com.foo.Test
类 [[Lcom.foo.Test;

正如您所看到的,组件类型最初是通过类加载器加载的,但数组类型是隐式加载的。