【问题标题】:How is a class literal compiled to Java bytecode?如何将类文字编译为 Java 字节码?
【发布时间】:2018-01-31 03:54:07
【问题描述】:
public class A {
}

public class B {
    public static void b() {
        System.out.println(A.class);
    }
}

类文字A.class是如何编译成B.class的字节码的?是字段参考吗?我在 Oracle/Sun 的字节码文档中找不到提及。

无论它是什么,反编译器都可以轻松重构它:

java -jar decompiler.jar B.class

拿起 JAVA_TOOL_OPTIONS: '-Dfile.encoding=UTF8'

  1. // // 由 Procyon v0.5.30 反编译 //

    public class B
    {
        public static void b() {
            System.out.println(A.class); <<<
        }
    }
    

【问题讨论】:

  • 您将其标记为完全重复但在答案中没有提及字节码。我的问题涉及字节码。表示类的对象是常量字段、变量字段、常量字段 ref、变量字段 ref 还是其他?如果它是一个字段的引用,那么该字段属于哪个类? Class.class 显然不是,所以在我的示例中它必须是 A 类。那么A中是什么?一个变量?静态常量?
  • 不确定字节码,但如果有帮助的话 - 对类对象的引用由加载该类的类加载器持有,因此您可以将其称为类加载器的(部分)变量字段。
  • 我无法发布答案,因为这被标记为重复(显然我无法取消标记)。 .class 运算符编译为“加载常量”指令(ldcldc_w),其中操作数是常量池的索引。常量池条目是一个Class_info 结构(不是Fieldref_info)。该结构仅指向另一个条目:包含类型描述符的Utf8 条目(package/name/ClassName 形式的类型名称)。您可以自己编译一个简单的示例并通过javap -v 运行它来验证这一点。
  • 谢谢,很有用!不知道为什么 stackoverflow 对这个问题如此教条,这绝对不会重复所谓的重复。

标签: java bytecode


【解决方案1】:

在 Java 5 之前,像 A.class 这样的类文字只是在底层调用 Class.forName("A") 的语法糖,将 ClassNotFoundException 转换为 NoClassDefFoundError,并且根据编译器的不同,将结果缓存在合成包含类的static字段,即B

原因是 Java 1.1 中引入了类文字作为一种语言特性,但字节码没有更改以对其进行特殊支持。

从 Java 5 开始,类文字被视为实常数,使用单个 ldcldc_w 指令加载到操作数堆栈,就像使用 String 文字一样。区别在于常量池项的类型,它指的是String_info代表String常量,Class_info代表Class常量。

附带说明,从 Java 7 开始,Java 字节码甚至允许加载类型为 MethodTypeMethodHandle 的常量,它们没有实际的 Java 语言等效项。

ldc:

index 是一个无符号字节,它必须是当前类的运行时常量池的有效索引(第 2.6 节)。 index 处的运行时常量池条目要么必须是 intfloat 类型的运行时常量,要么是对字符串文字的引用,要么是对类的符号引用,方法类型或方法句柄 (§5.1)。

如果运行时常量池条目是 intfloat 类型的运行时常量,则该运行时常量的数值 被压入操作数堆栈分别是 intfloat

否则,如果运行时常量池条目是 reference 到类 String 的实例,表示字符串文字(第 5.1 节),那么 reference 到该实例,值 em>,被压入操作数堆栈。

否则,如果运行时常量池条目是对类的符号引用(第 5.1 节),则命名类将被解析(第 5.4.3.1 节)并将reference 指向代表该类的Class 对象类 value 被压入操作数堆栈。

否则,运行时常量池条目必须是对方法类型或方法句柄的符号引用(第 5.1 节)。方法类型或方法句柄已解析(第 5.4.3.5 节),并将 referencejava.lang.invoke.MethodTypejava.lang.invoke.MethodHandle 的结果实例的值推送到操作数堆栈中。

由于您提到了反编译器,大多数反编译器甚至能够识别更复杂的 Java 5 之前的代码模式并将它们反编译为类文字。当然,简单的ldc指令反编译起来很简单。

【讨论】:

  • 我想是时候 Procyon 加入“大多数反编译器”来处理旧式模式了 :)。
猜你喜欢
  • 1970-01-01
  • 2014-01-20
  • 2014-06-13
  • 1970-01-01
  • 2018-05-13
  • 1970-01-01
  • 2011-04-20
  • 1970-01-01
  • 2013-10-01
相关资源
最近更新 更多