简体   繁体   中英

Difference between initialization of final class variable

I just tried out to set a variable, showing me that the super constructor has been finished without wasting my code. So I remembered how class variables are initialized; after the super constructor but before the class constructor. But there is something strange if you look at this example:

public class Init {

    public Init() {
        System.out.println("Init instance of " + this.getClass().getSimpleName());
        System.out.println("syso Init:");
        this.syso(false);
    }

    protected void syso(boolean childCall) {
        System.out.println("should not be printed because the method is overwitten");
    }

    public static class Child extends Init {

        private final boolean finalTrueA = true;
        private final boolean finalTrueB;
        private final boolean finalTrueC;

        private boolean nonFinalTrueA = true;
        private boolean nonFinalTrueB;
        private boolean nonFinalTrueC;

        {
            this.finalTrueB = true;
            this.nonFinalTrueB = true;
        }

        public Child() {
            super();
            this.finalTrueC = true;
            this.nonFinalTrueC = true;

            System.out.println("\n\nsyso Child:");
            this.syso(true);
        }

        @Override
        protected void syso(boolean childCall) {
            System.out.println("finalTrueA " + this.finalTrueA + " should be " + childCall);
            System.out.println("finalTrueB " + this.finalTrueB + " should be " + childCall);
            System.out.println("finalTrueC " + this.finalTrueC + " should be " + childCall);

            System.out.println();

            System.out.println("nonFinalTrueA " + this.nonFinalTrueA + " should be " + childCall);
            System.out.println("nonFinalTrueB " + this.nonFinalTrueB + " should be " + childCall);
            System.out.println("nonFinalTrueC " + this.nonFinalTrueC + " should be " + childCall);
        }
    }

    public static void main(String[] args) {
        @SuppressWarnings("unused")
        Child child = new Child();
    }

}

which has the following output:

Init instance of Child
syso Init:
finalTrueA true should be false // <-- why??????????
finalTrueB false should be false
finalTrueC false should be false

nonFinalTrueA false should be false
nonFinalTrueB false should be false
nonFinalTrueC false should be false


syso Child:
finalTrueA true should be true
finalTrueB true should be true
finalTrueC true should be true

nonFinalTrueA true should be true
nonFinalTrueB true should be true
nonFinalTrueC true should be true

This effect can be seen on the eclipse output as well as on running the class with javac. But the eclipse debugger shows the correct value of finalTrueA within the super constructor call of the syso method.

So my question: Is this effect correct, that the different initialization results in different behaviour of the static fields and this is a bug of the eclipse debugger, or is this behaviour incorrect and it is a bug of java???

Every final variable which is immediately assigned with a compile-time constant and has a primitive or String type is a compile-time constant as well and its value will be inlined at compile-time when used in an expression context. This does apply to all variables, not only static . Consider the following (valid) code:

public static void main(String[] args)
{
  final int zero=0, one=zero+1, two=one+one, three=two+one;
  switch(args.length)
  {
    case zero: System.out.println("no args"); break;
    case one: System.out.println("one arg"); break;
    case two: System.out.println("two args"); break;
    case three: System.out.println("three args");
  }
}

Here, the local variables zero , one , two , and three are compile-time constants and hence even allowed as switch case labels…

In you case the value of this.finalTrueA inlined at compile-time and hence will be true even when invoked from the super-constructor.

All final variables should be assigned during compile time. Either via static initializer or in the constructor or in the class itself.

See extract from jls

It is a compile-time error if a blank final (§4.12.4) class variable is not definitely
assigned (§16.8) by a static initializer (§8.7) of the class in which it is declared.

A blank final instance variable must be definitely assigned (§16.9) at the end of every
constructor (§8.8) of the class in which it is declared; otherwise a compile-time error occurs

Based on the this, the variable finalTrueA is set to true at the time of compilation itself and thats why you see true .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM