Initialising Field Variables
public class Foo {
private int var;
public F开发者_如何学Gooo() {
var = 10;
}
}
In this code snippet, would var
first be assigned a default value and then reassigned to 10, or would it be assigned to 10 directly without being assigned a default value?
Somewhat a trivial question, but I'm curious.
If you look at the decompiled byte code of Foo.class
, you will notice the following:
- The class' constructor itself only assigns the value 10 (bipush and putfield). The class's constructor does not first assign 0 and then 10.
- The VM will have a default value of 0 for the field whenever it is accessed - no matter from which code. So this default value will not show up anywhere - at least not in the bytecode of the class or other classes which access the field for example by reflection. Primitive default values are baked into the VM.
- Explicitly setting the default will produce different bytecode, see second example.
.
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
If you write the following:
public class Foo {
private int var = 0;
public Foo() {
var = 20;
}
}
the bytecode will be:
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
The next example shows that accessing the variable will still not lead to an assignment of any value:
public class Foo {
private int var;
public Foo() {
System.out.println(var);
var=10;
}
}
This code will print 0
because getField Foo.var
at opcode 8 will push "0" onto the operand stack:
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
Uninitialized fields will always be assigned the default value prior to calling the constructor, because the runtime will zero the memory allocation for the object prior to calling the constructor. It must do this because it does not know what the constructor might do ahead of time, and because derived classes may live in other jars/classpaths and fetch the value (if it's protected) or call into a method that uses the field before it is initialized by the constructor.
This is performed independently of the compiler, and therefore this is not something the compiler can optimize away, and the compiler doesn't even have control over this.
According to the spec: (Section 4.2.15)
First 0.
Then 10.
If you would read it first in the constructor, you would get 0.
public class Foo {
private int var;
public Foo() {
System.out.println(var); //0
var = 10;
}
}
It would be given the default value first. In particular, if Foo was derived from Bar, and the constructor of Bar could get at the value of var somehow (e.g. through a virtual method declared in Bar and overridden in Foo), that default value would be visible, even if the variable is final. For example:
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();
}
}
Note that this still happens even when the field is initialized at the point of declaration:
public class Foo {
private int var = 10;
public Foo() {
// Implicit call to super constructor - this occurs *before*
// var is assigned the value 10
}
}
In this respect Java differs from C#. In C#, var
would be assigned the value 10 before the call to the base constructor.
Assigning the default value implies it had a value before you assigned the default value. An object has the default value when it is created, it is not assigned it.
If you look at the byte code, the only code is to assign the new value.
The language specification says:
Otherwise, all the instance variables in the new object, including those declared in superclasses, are initialized to their default values (§4.12.5).
http://java.sun.com/docs/books/jls/third_edition/html/execution.html#44410
Then the constructor is called.
精彩评论