初始化场变量

时间:2011-01-14 17:37:34

标签: java field default-value

public class Foo {
    private int var;

    public Foo() {
        var = 10;
    }
}

在此代码段中,首先会为var分配一个默认值,然后重新分配给10,还是直接分配给10而不分配默认值?

有点微不足道的问题,但我很好奇。

6 个答案:

答案 0 :(得分:5)

如果查看Foo.class的反编译字节代码,您会注意到以下内容:

  • 类'构造函数本身只分配值10(bipush和putfield)。类的构造函数不首先分配0然后分配10。
  • 无论何时访问,VM都会为该字段设置默认值0 - 无论从哪个代码开始。因此,此默认值不会显示在任何位置 - 至少不会出现在类的字节码或其他通过反射访问字段的类中。原始默认值将烘焙到VM中。
  • 显式设置默认值将产生不同的字节码,请参阅第二个示例。

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

然后调用构造函数。