7

If a static variable is in RIWO (Read Indirectly Write Only) state. the static variable can not be accessed directly.

here is the code

class Test {
    static{
        System.out.println(x);
    }

    static int x = 10;

    public static void main(String[] args) {

    }
}

in this case illegal forward reference compile time error is coming.

but if you are using the class name to access the static variable, it can be accessed.

here is the code example

class Test {
    static{
        System.out.println(Test.x);
    }

    static int x = 10;

    public static void main(String[] args) {

    }
}

answer is : 0

how is this possible ? isn't this a direct access ?

  • See the comments below [this SO answer](https://stackoverflow.com/a/2420405/8295283) which references this [Java documentation](https://docs.oracle.com/javase/specs/jls/se8/html/jls-12.html#jls-12.4.2) (point 9). Did you try changing the order? – Arnold Schrijver Jul 05 '18 at 05:34
  • 2
    Possible duplicate of [Cannot reference a field before it is defined, but only if you don't qualify it](https://stackoverflow.com/questions/30959458/cannot-reference-a-field-before-it-is-defined-but-only-if-you-dont-qualify-it) – ernest_k Jul 05 '18 at 05:35
  • @ArnoldSchrijver yes I tried with changing the order. then it is working properly. In the static control flow, these are the steps performing when a class is loading 1). Identify the static members from parent to child top to bottom. 2). Executes static variables assignments and static blocks from parent to child top to bottom. 3). Executes the main method. so if we are using static variable before the static block and inside the static block if we are directly printing the value, the value is printed. no issue at all. – Masith Prasanga Jul 05 '18 at 11:19

2 Answers2

1

As per JLS 12.4.1. When Initialization Occurs and this answer:

a class's static initialization normally happens immediately before the first time one of the following events occur:

  • an instance of the class is created
  • a static method of the class is invoked
  • a static field of the class is assigned
  • a non-constant static field is used

In your case reading Test.x falls under point 4, a non-constant static field. Your code reads and output 0 which is the default int value, however you change it by marking the field final as

static {
  System.out.println(Test.x);
}

final static int x = 10;

The code will now output 10. You can see it by comparing output of javap -c Test.class for both non-final and final field cases, the order of bytecode will be reversed around:

6: invokevirtual #4                  // Method java/io/PrintStream.println:(I)V
9: bipush        10

To me it looks like the Java compiler's forward reference error is a workaround for gotchas in static initialization in JVM.

Community
  • 1
  • 1
Karol Dowbecki
  • 43,645
  • 9
  • 78
  • 111
0

This could be a java compiler bug.

In your first case, compiler successfully find out the bad usage.

execute either the class variable initializers and static initializers of the class, or the field initializers of the interface, in textual order, as though they were a single block.

Use the variable before declare it of course should be forbidden.

In your second case, unfortunately, compiler don't find the bad usage.

The following things happens step by step:

  • JVM load the Test class, now Test.class object is accessable
  • Someone trigger the Test class static's initlization
  • JVM run in textual order, the most front static block will be invoked first
  • JVM try to get the Test.x. Since it's not constant, jvm go to METASPACE to get the value. int is 0 by default. So you get 0 printed.
  • JVM run the next line x = 10, set the data into METASPACE

By the way, Karol's answer has a little mistake. Only add final is not sufficient, the other necessary condition is it's really constant. The following code will still print 0.

static {
  System.out.println(JustTest.x);
}

static final int x = Integer.valueOf(1);
Dean Xu
  • 4,438
  • 1
  • 17
  • 44