public class A {
}
public class B {
public static void b() {
System.out.println(A.class);
}
}
How is the class literal A.class
compiled in B.class's bytecode? Is it a field reference? I can't find a mention of that in the bytecode documentation from Oracle/Sun.
Decompilers have no trouble reconstructing it, whatever it is:
java -jar decompiler.jar B.class
Picked up JAVA_TOOL_OPTIONS: '-Dfile.encoding=UTF8'
// // Decompiled by Procyon v0.5.30 //
public class B
{
public static void b() {
System.out.println(A.class); <<<
}
}
Before Java 5, a class literal like A.class
was just syntactic sugar for calling Class.forName("A")
under the hood, translating a ClassNotFoundException
to a NoClassDefFoundError
and, depending on the compiler, cache the result in a synthetic static
field of the containing class, i.e. B
.
The reason is that class literals were introduced in Java 1.1 as a language feature, but the byte code was not changed to have special support for it.
Since Java 5, class literals are treated as real constants, being loaded to the operand stack using a single ldc
or ldc_w
instruction, just like with String
literals. The difference lies in the type of the constant pool item, it refers to, String_info
for String
constants and Class_info
for Class
constants.
As a side note, since Java 7, the Java bytecode even allows to load constants of type MethodType
or MethodHandle
which has no actual Java language equivalent.
See ldc
:
The index is an unsigned byte that must be a valid index into the run-time constant pool of the current class (§2.6). The run-time constant pool entry at index either must be a run-time constant of type
int
orfloat
, or a reference to a string literal, or a symbolic reference to a class, method type, or method handle (§5.1).If the run-time constant pool entry is a run-time constant of type
int
orfloat
, the numeric value of that run-time constant is pushed onto the operand stack as anint
orfloat
, respectively.Otherwise, if the run-time constant pool entry is a
reference
to an instance of classString
representing a string literal (§5.1), then areference
to that instance, value, is pushed onto the operand stack.Otherwise, if the run-time constant pool entry is a symbolic reference to a class (§5.1), then the named class is resolved (§5.4.3.1) and a
reference
to theClass
object representing that class, value, is pushed onto the operand stack.Otherwise, the run-time constant pool entry must be a symbolic reference to a method type or a method handle (§5.1). The method type or method handle is resolved (§5.4.3.5) and a
reference
to the resulting instance ofjava.lang.invoke.MethodType
orjava.lang.invoke.MethodHandle
, value, is pushed onto the operand stack.
Since you mentioned decompilers, most decompilers are even capable of recognizing the more complex pre-Java 5 code patterns and decompile them to a class literal. Of course, the simple ldc
instruction is trivial to decompile.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With