benf.org : other : cfr : How are Field Initialisers compiled? |
Other than in the case of a static primitive (String counts as a primitive here!) initialiser, there's no difference between initialising a field in every constructor, vs at point of declaration
Links from the 'class file format' doc:
Q: What's the difference between
// case 0 public class MemberTest { private final Map<String, String> m = new HashMap<String, String>(); public MemberTest2() { } }and
// case 1 public class MemberTest { private final Map<String, String> m; public MemberTest2() { this.m = new HashMap<String, String>(); } }
A: ... absolutely nothing. (or certainly not with the compiler I have now ;) ) - these two compile to the same bytecode - which is that of case 1.
If you decompile these two with cfr_0_13 with the argument '--liftconstructorinit false' you will see the latter code in both case.
Any non-static, non-primitive field initialiser is copied into EVERY constructor, after the possible super(..) call.
The only (fairly obvious exception to this is chained constructors - in which case the chainer (the constructor which calls this(...) does NOT get a copy of the initialisation code.
// case 2 public class MemberTest { private int x = 3; public MemberTest2() { } }vs
// case 3 public class MemberTest { private int x; public MemberTest2() { this.x = 3; } }
Again, these two compile to the same bytecode.
// case 4 public class MemberTest { private final int x = 3; public MemberTest2() { } }vs
// case 5 public class MemberTest { private final int x; public MemberTest2() { this.x = 3; } }
These two produce different bytecode - (finally!) - in case 4, the compiler makes use of the ConstantValue attribute in a field_info structure - See 'Java Class file format - 4.7.2 The ConstantValue Attribute'
/* * Decompiled with CFR. */ public class MemberTest { private final int x = 3; // (comes from constant value attribute) public MemberTest() { this.x = 3; // (comes from bytecode) } }
Hang on! - this has defined a ConstantValue attribute, AND it has bytecode based initialisation! The relevant information is this para from the Class file spec:
"... if a field_info structure representing a non-static field has a ConstantValue attribute, then that attribute must silently be ignored. Every Java virtual machine implementation must recognize ConstantValue attributes."
... for some reason, the compiler has emitted a constantValue attribute, but has had to emit bytecode as well. The only place ConstantValue attributes can have meaning is in static initialisers.
// case 6 public class MemberTest { private static final int x = 3; }vs
// case 7 public class MemberTest { private static final int x; static { MemberTest.x = 3; } }
These do produce different initialisations - the former uses the ConstantValue attribute, the latter the bytecode.
Last updated 05/2013 |