5
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'

  1. // // Decompiled by Procyon v0.5.30 //

    public class B
    {
        public static void b() {
            System.out.println(A.class); <<<
        }
    }
    
4
  • 2
    You marked it as an exact duplicate yet in the answers there is no mention of bytecode. My question concerns bytecode. Is the object representing the class a constant field, a variable field, a constant field ref, a variable field ref, or something else? If it's a ref to a field, what class has that field? Class.class doesn't, obviously, so it has to be the class A in my example. So what is it in A? A variable? A static constant? Commented Aug 22, 2017 at 18:53
  • Not sure about bytecode, but if it's any help - reference to the class object is held by the classloader that loaded that class, so you could call it a (part of) variable field of the classloader. Commented Aug 22, 2017 at 20:28
  • 1
    I can't post an answer, as this was marked as a duplicate (and I can't un-mark it, apparently). The .class operator compiles to a 'load constant' instruction (ldc or ldc_w), where the operand is an index into the constant pool. The constant pool entry is a Class_info structure (not Fieldref_info). That structure simply points to yet another entry: a Utf8 entry containing the type descriptor (type name in the form package/name/ClassName). You can verify this yourself by compiling a simple example and running it through javap -v. Commented Sep 4, 2017 at 11:32
  • Thank you, that's very useful! Not sure why stackoverflow is being so doctrinaire about this question, which definitely doesn't duplicate the alleged duplicate. Commented Sep 4, 2017 at 17:12

1 Answer 1

11

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 or float, 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 or float, the numeric value of that run-time constant is pushed onto the operand stack as an int or float, respectively.

Otherwise, if the run-time constant pool entry is a reference to an instance of class String representing a string literal (§5.1), then a reference 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 the Class 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 of java.lang.invoke.MethodType or java.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.

Sign up to request clarification or add additional context in comments.

1 Comment

I suppose it's time Procyon joined "most decompilers" in handling the old-style patterns :).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.