java中的默认实例值初始化



我正在尝试检查生成的字节码中实例变量的默认值(即此处为0(。我可以看到<init>()被调用,如果我在构造函数中打印myvar实例变量,那么我会看到getfield被调用用于myvar,但这个默认值首先设置在哪里?

请回答以下问题:

  1. myvar中的默认值何时设置?(编译或对象创建时间之后(
  2. 谁(编译器或jvm(正在初始化实例变量(或者说设置默认值(
public class FieldInit {
int myvar;
public static void main(String[] args) {
new FieldInit(); // and what would happen if I comment out this
}
}

我正在尝试使用javap对字节码进行反汇编,但看不到<clinit>()方法,我想这可能会发生。请让我知道是否可以看到<clinit>()方法,如果可以,如何?

在JVM中,对象实例化被拆分为两个字节码指令:

  1. new分配一个新的未初始化对象
  2. invokespecial调用一个初始化对象的构造函数

new字节码的JVM规范说:

该类的新实例的内存是从垃圾收集堆,以及新对象的实例变量被初始化为其默认初始值

JVM在执行new指令时将所有实例字段设置为零。因此,在调用构造函数时,所有字段都已设置为默认值。你不会发现这个";"归零";在字节码中——这是JVM在对象分配期间隐式完成的。

  1. 你的问题无法回答,因为它比你想象的更复杂
  2. java中的

字段被编码在字段数据结构中,该数据结构包括用于初始值的空间但是,此数据结构中唯一可能的初始值是数字和字符串

让我们看看它在行动!(请注意,L只是一个java语法来告诉java:这个数字是long,而不是int。5是常数,5L也是(。

class Test {
public static final long mark = 5L;
}
javac Test.java
javap -v c Test
.....
public static final long mark;
descriptor: J
flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
ConstantValue: long 5l

嘿,看,它在那里,常数值5L。

但如果它不是恒定的呢?

啊,这是个问题。你不能在这里编码。

所以,相反,这是语法糖时间!

您可以在类的静态初始化期间运行的任何类中编写一个特殊方法。您还可以编写一个特殊的方法,该方法在创建新实例时运行。这一点几乎与构造函数完全相同,只有奇异的差异。它看起来像这样:

public class Test {
static {
System.out.println("What voodoo magic is this?");
}
public static void main(String[] args) {
System.out.println("In main");
}
}

让我们看看它在行动!

javac Test.java
java Test
What voodoo magic is this?
In main
javap -c -v Test
...
static {};
descriptor: ()V
flags: (0x0008) ACC_STATIC
Code:
stack=2, locals=0, args_size=0
0: getstatic     #7                  // Field >java/lang/System.out:Ljava/io/PrintStream;
3: ldc           #21                 // String Voodoo...
5: invokevirtual #15                 // Method >java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 3: 0
line 4: 8
}

正如你所看到的,那个奇怪的static{}东西被编译成了一个看起来完全像方法、字节码等等的东西,但方法的名称很奇怪,它只是static{}

线索来了

但是,如果我们让事情变得更加复杂,会发生什么呢。让我们将此字段初始化为当前时间!

class Test {
public static final long mark = System.currentTimeMillis();
}

这只是语法糖。这解释了它是如何工作的,因为正如我告诉您的,在类文件级别,不能用非常量初始化字段。因此,它编译为相同的东西:

class Test {
public static final long mark;
static {
mark = System.currentTimeMillis();
}
}

您可以javap对此进行确认。

其中一个被称为"编译时常数"。另一个不是。这表现在不同的方面。例如,您可以传递CTC作为注释参数。尝试一下:尝试使用static final long MARK = 5L;(你可以(,然后使用static final long MARK = System.currentTimeMillis();——这是不允许的。

那么,初始值在哪里?如果它是一个常数值,javap -c -v会显示它。如果它不是,它就卡在静态块中。

相关内容

  • 没有找到相关文章