benf.org :  other :  cfr :  How are Field Initialisers compiled?

Initialisers

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:

Non-primitive field initialisers

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.

Primitive, but non-final, field initialisers

// 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.

Primitive final field initialisers.

// 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'

CFR decompilation of case 5

/*
 * 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