为什么 Class.forName 出现在这个 class 的字节码中?

Why does Class.forName appear in the bytecode for this class?

我一直在对 Java 字节码进行一些个人研究,我遇到了一些奇怪的事情。如果我反编译 this class,我会在常量池中找到对 Class.forName() 的引用。但是,源代码中没有引用此方法。

我假设这段代码的某些内容导致 javac 发出一些动态加载 class 的代码,但我不确定为什么会发生这种情况。这让我觉得效率低下,但主要是我只是好奇为什么会这样。

javap反汇编代码后,发现源码中有一个方法是不存在的:

static java.lang.Class class$(java.lang.String);
  Code:
     0: aload_0
     1: invokestatic  #1                  // Method java/lang/Class.forName:(Ljava/lang/String;)Ljava/lang/Class;
     4: areturn
     5: astore_1
     6: new           #3                  // class java/lang/NoClassDefFoundError
     9: dup
    10: invokespecial #4                  // Method java/lang/NoClassDefFoundError."<init>":()V
    13: aload_1
    14: invokevirtual #5                  // Method java/lang/NoClassDefFoundError.initCause:(Ljava/lang/Throwable;)Ljava/lang/Throwable;
    17: athrow
  Exception table:
     from    to  target type
         0     4     5   Class java/lang/ClassNotFoundException

看起来这是在为 < JDK1.5 版本编译的字节码中生成的,只要在代码 [1] 中引用了 class 文字。基本上,这个:

if (getClass() == Level.class) {}

变成这样:

if (getClass() == class$("org.apache.log4j.Level")) {}

class$() 看起来像这样:

static Class class$(java.lang.String className) {
    try {
       return Class.forName(className);
    } catch (ClassNotFoundException e) {
       throw new NoClassDefFoundError();
    }
}

显然在 JDK1.5 中,ldc_w 指令被赋予了加载 class 常量的能力,并且不再需要 class$() 方法。