public class Foo {
private int var;
public Foo() {
var = 10;
}
}
在此代码段中,首先会为var
分配一个默认值,然后重新分配给10,还是直接分配给10而不分配默认值?
有点微不足道的问题,但我很好奇。
答案 0 :(得分:5)
如果查看Foo.class
的反编译字节代码,您会注意到以下内容:
public class Foo {
private int var;
public Foo();
0 aload_0 [this]
1 invokespecial java.lang.Object() [10]
4 aload_0 [this]
5 bipush 10
7 putfield Foo.var : int [12]
10 return
如果您写下以下内容:
public class Foo {
private int var = 0;
public Foo() {
var = 20;
}
}
字节码将是:
0 aload_0 [this]
1 invokespecial java.lang.Object() [10]
4 aload_0 [this]
5 iconst_0
6 putfield Foo.var : int [12]
9 aload_0 [this]
10 bipush 20
12 putfield Foo.var : int [12]
15 return
下一个示例显示访问变量仍然不会导致任何值的赋值:
public class Foo {
private int var;
public Foo() {
System.out.println(var);
var=10;
}
}
此代码将打印0
,因为操作码8上的getField Foo.var
会将“0”推到操作数堆栈上:
public Foo();
0 aload_0 [this]
1 invokespecial java.lang.Object() [10]
4 getstatic java.lang.System.out : java.io.PrintStream [12]
7 aload_0 [this]
8 getfield Foo.var : int [18]
11 invokevirtual java.io.PrintStream.println(int) : void [20]
14 aload_0 [this]
15 bipush 10
17 putfield Foo.var : int [18]
20 return
答案 1 :(得分:3)
根据 spec :(第4.2.15节)
首先0。
然后10。
如果你在构造函数中首先阅读它,你会得到0。
public class Foo {
private int var;
public Foo() {
System.out.println(var); //0
var = 10;
}
}
答案 2 :(得分:3)
在调用构造函数之前,将始终为未初始化的字段分配默认值,因为运行时将在调用构造函数之前将对象的内存分配归零。它必须这样做,因为它不知道构造函数可能提前做什么,并且因为派生类可能存在于其他jar /类路径中并获取值(如果它受保护)或调用到之前使用该字段的方法由构造函数初始化。
这是独立于编译器执行的,因此这不是编译器可以优化的东西,编译器甚至无法控制它。 < / p>
答案 3 :(得分:1)
首先会给出默认值。特别是,如果Foo是从Bar派生的,并且Bar的构造函数可以某种方式得到var的值(例如通过在Bar中声明并在Foo中重写的虚方法),那么即使变量是可见的,该默认值也是可见的。最后。例如:
class Parent {
public Parent() {
showVariables();
}
public void showVariables() {
}
}
class Child extends Parent {
private final int x;
public Child() {
x = 10;
}
@Override
public void showVariables() {
System.out.println("x = " + x); // Prints x = 0
}
}
public class Test {
public static void main(String[] args) {
new Child();
}
}
请注意,即使在声明点初始化字段时,仍然会发生:
public class Foo {
private int var = 10;
public Foo() {
// Implicit call to super constructor - this occurs *before*
// var is assigned the value 10
}
}
在这方面,Java与C#不同。在C#中,var
将在调用基础构造函数之前被赋值为10。
答案 4 :(得分:0)
分配默认值意味着在分配默认值之前它有一个值。对象在创建时具有默认值,但未对其进行分配。
如果查看字节代码,唯一的代码是分配新值。
答案 5 :(得分:0)
语言规范说:
否则,新对象中的所有实例变量(包括在超类中声明的变量)都将初始化为其默认值(§4.12.5)。
http://java.sun.com/docs/books/jls/third_edition/html/execution.html#44410
然后调用构造函数。