0

我仍在尝试学习 java 类文件格式,并且我知道常量变量值存储在 ConstantValue 属性下,但是我不明白非常量字段值存储在类文件中的位置。如

public Robot robot = new Robot();

我查看了 oracles 类文件规范,但找不到任何这样的属性。

4

2 回答 2

2

存储初始值的唯一属性是ConstantValue. 但是,这仅用于静态变量。在 Java 编译代码中,它仅用于静态最终变量,尽管它可以用于手写字节码中的任何静态变量。当然,该值必须是恒定的。

对于您发布的示例,您拥有 Java 中所谓的initializer。这些在类文件中没有等效项。相反,Java 编译器将在每个超类构造函数调用返回后粘贴此代码。请注意,这意味着如果您可以在此之前查看它们,例如通过在超类构造函数中调用虚拟方法,它们还没有被初始化。

这是一个例子。

public class initializers {

    static final float f = 4;
    static final int i = int.class.hashCode();
    static int i2 = 4;

    public final Object x = null;
    public Object x2 = null;


    public initializers() {}
    public initializers(int x) {this();}
    public initializers(float x) {}
}

编译和反汇编类结果

.version 49 0
.class super public initializers
.super java/lang/Object

.field static final f F = 4.0F
.field static final i I
.field static i2 I
.field public final x Ljava/lang/Object;
.field public x2 Ljava/lang/Object;

.method public <init> : ()V
    .limit stack 2
    .limit locals 1
    aload_0
    invokespecial java/lang/Object <init> ()V
    aload_0
    aconst_null
    putfield initializers x Ljava/lang/Object;
    aload_0
    aconst_null
    putfield initializers x2 Ljava/lang/Object;
    return
.end method

.method public <init> : (I)V
    .limit stack 1
    .limit locals 2
    aload_0
    invokespecial initializers <init> ()V
    return
.end method

.method public <init> : (F)V
    .limit stack 2
    .limit locals 2
    aload_0
    invokespecial java/lang/Object <init> ()V
    aload_0
    aconst_null
    putfield initializers x Ljava/lang/Object;
    aload_0
    aconst_null
    putfield initializers x2 Ljava/lang/Object;
    return
.end method

.method static <clinit> : ()V
    .limit stack 1
    .limit locals 0
    getstatic java/lang/Integer TYPE Ljava/lang/Class;
    invokevirtual java/lang/Object hashCode ()I
    putstatic initializers i I
    iconst_4
    putstatic initializers i2 I
    return
.end method

如您所见,f是唯一一个使用ConstantValue属性的。i不是因为它不是一个常量表达式,而i2不是因为它不是最终的。初始化代码ii2放置在静态初始化程序(“类构造函数”)中。初始化代码xx2粘贴在两个超类构造函数调用之后,但不是在第二个构造函数中,它只调用同一个类中的构造函数。

于 2013-06-02T15:34:39.337 回答
0

您粘贴的代码中没有常量。只有一个变量(将在运行时在堆栈上分配)。

如果您对具有非常量字段的类的字节码是什么样子感兴趣,可以试试这篇文章


该变量robot在运行时初始化。说,robot在课堂上Foo

class Foo {
    public Robot robot = new Robot();
    Foo() {
    }
}

现在 Java 编译器将转换new Foo()为执行以下步骤的指令序列:

  • Foo在堆上为新对象分配空间。这包括 field 的空间 robot
  • 跳转到类的初始化指令Foo
    • Foo 初始化程序为新实例分配空间Robot
    • 调用父级的构造函数Foo
    • 调用初始化器Robot
  • ...

那么字节码中的内容是

  • 分配和初始化指令Foo
  • 分配和初始化指令Robot

结论是类文件的字节码中没有字段机器人的空间。有一些分配指令在运行时执行并为堆上的字段创建空间。


于 2013-06-02T14:43:45.343 回答