Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

The ClassLoader can replace the array by anything

When I run the following Java code:

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());

I get java.lang.Object in the display terminal, even if I replace Object[][].class.getName() by [[Ljava.lang.Object in the code. The problem is that I was expecting the console to show [[Ljava.lang.Object.

In effect, in the JVM specification, I can read the following:

An array class is created directly by the Java Virtual Machine (§5.3.3), not by a class loader. However, the defining class loader of D is used in the process of creating array class C.

Since Object[][] is an array class, I assumed that my findClass wouldn't be called with the argument [[Ljava.lang.Object but with its element type java.lang.Object.

Further, in the section "Creating Array Classes", the recursive algorithm is actually described:

If the component type is a reference type, the algorithm of this section (§5.3) is applied recursively using class loader L in order to load and thereby create the component type of C.

So my questions are:

  • Why am I getting this output? Does it mean that I have to manually include this recursive algorithm inside my ClassLoader, rather than letting the JVM doing it for me? If this is what it means, what is the best way to do it?
  • Am I misinterpreting the "created" in the first quotation? Does it only mean that I can't create the runtime array class, but that I still can patch its loading?
like image 382
Codoscope Avatar asked Dec 13 '16 02:12

Codoscope


1 Answers

You're asking about the JVM specification, but your test demonstrates the behavior of java.lang.ClassLoader, an independent class which is "invoked by the Java virtual machine to resolve class references". If the JVM is loading an array class, it will bypass the class loader entirely. This can be demonstrated by letting the JVM try to load the class with a custom class loader:

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);

Output:

Loading com.foo.Test
class [[Lcom.foo.Test;

As you can see, the component type is initially loaded via the class loader, but the array types are loaded implicitly.

like image 77
shmosel Avatar answered Oct 12 '22 09:10

shmosel