public class A {
}
public class B {
public static void b() {
System.out.println(A.class);
}
}
如何在B.class的字节码中编译类文字A.class
?它是一个现场参考?我无法在Oracle / Sun的字节码文档中找到这一点。
无论是什么原因,反编译器都可以轻松地重建它:
java -jar decompiler.jar B.class
选择JAVA_TOOL_OPTIONS:' -Dfile.encoding = UTF8'
// //由Procyon v0.5.30反编译 //
public class B
{
public static void b() {
System.out.println(A.class); <<<
}
}
答案 0 :(得分:7)
在Java 5之前,像A.class
这样的类文字只是用于调用Class.forName("A")
的语法糖,将ClassNotFoundException
翻译为NoClassDefFoundError
,具体取决于编译器,将结果缓存在包含类的合成static
字段中,即B
。
原因是在Java 1.1中引入了类文字作为语言功能,但字节代码没有更改为对它有特殊支持。
从Java 5开始,类文字被视为实常数,使用单ldc
或ldc_w
指令加载到操作数堆栈,就像使用String
文字一样。不同之处在于常量池项的类型,它指的是String
常量String_info
和Class
常量Class_info
。
作为旁注,从Java 7开始,Java字节码甚至允许加载类型MethodType
或MethodHandle
的常量,这些常量没有实际的Java语言等效。
请参阅ldc
:
索引是一个无符号字节,必须是当前类(第2.6节)的运行时常量池的有效索引。 index 处的运行时常量池条目必须是类型为
int
或float
的运行时常量,或者是对字符串文字的引用,或者是符号引用到类,方法类型或方法句柄(第5.1节)。如果运行时常量池条目是类型
int
或float
的运行时常量,则该运行时常量的数值值将被推送到操作数堆栈分别为int
或float
。否则,如果运行时常量池条目是表示字符串文字(第5.1节)的类
reference
的实例的String
,那么该实例的reference
, value ,被压入操作数堆栈。否则,如果运行时常量池条目是对类的符号引用(第5.1节),则命名类将被解析(第5.4.3.1节),
reference
被解析为Class
表示该类的对象 value 被推送到操作数堆栈。否则,运行时常量池条目必须是对方法类型或方法句柄(第5.1节)的符号引用。解析了方法类型或方法句柄(第5.4.3.5节),并将
reference
或java.lang.invoke.MethodType
的结果实例java.lang.invoke.MethodHandle
推送到操作数堆栈上。
由于您提到了反编译器,大多数反编译器甚至能够识别更复杂的Java 5之前的代码模式并将它们反编译为类文字。当然,简单的ldc
指令对于反编译来说是微不足道的。