2

So I was just programming a bit, when I got some very unwanted behavior and found out something very weird about Java. Let's look at the (condensed) code:

public class Main {

    static{
        test = "test2";           // this is fine!?
        System.out.println(test); //compilation error!
    }

    static String test = "test1"; // initialization line

    public static void main(String[] args) {
        System.out.println(test);
    }
}

This program gives you a compilation time error saying that you can't reference "test" before it is defined. However I just did it exactly one line before. Now guess what is the output, when you comment out the error line...

The output is: "test1", so the initialization overwrites the assignment happening before the declaration!

Also if you don't initialize the variable, "test2" is the output. However, if you explicitly assign "null" as value during initialization, the output will be "null".

Questions:

  1. Why can I access a variable to write, but not to read, before it is declared?
  2. Why is the initialization done after the first assignment (this contradicts the term "initialization")?
  3. I always thought that explicitly initializing with "null" is the same as omitting the initialization. This is obviously wrong with this example. Was I just thinking the wrong thing or does this contradict the spec?
  4. Is this really the wanted Java behavior? Is it a bug?
Henry
  • 2,953
  • 2
  • 21
  • 34
Wolf
  • 892
  • 2
  • 8
  • 22
  • I have to admit I am a bit surprise. Well the `System.out.println` seems logic since `test` is not declared yet. But the strange thing is that you can initialise it like this (if you remove `test1`, you see that the value `test2` is assign in it) – AxelH Nov 30 '16 at 13:59
  • Can you show us the code that you modified and worked? Otherwise check this http://stackoverflow.com/questions/2007666/in-what-order-do-static-initializer-blocks-in-java-run – Omoro Nov 30 '16 at 13:59
  • these kind of question appears every month... – AdamSkywalker Nov 30 '16 at 14:00
  • @Omoro I can't show the original code, but i simply fixed it by putting the declaration before the static{} block. This way the code is executed from top to bottom (or at least it behaves as if) – Wolf Nov 30 '16 at 14:02
  • String test1 = test in static scope also does not work. It is not about the println function. I think it is about operator= – cokceken Nov 30 '16 at 14:03
  • If you put `static String test = "test1";`as the first statement or above the `static initializer` block, the string `test2` will be printed two times which is exactly what it is suppose to do because you reassign the `test` reference to the literal `test2` in the static initializer. So it prints `test2` once in the `static initializer` block and the second in the `main` method. – Omoro Nov 30 '16 at 14:06

1 Answers1

3

This is the intended behavior, as defined in the JLS. To answer your questions:

  1. Why can I access a variable to write, but not to read, before it is declared?

This is answered by section 8.3.2.3

The declaration of a member needs to appear textually before it is used only if the member is an instance (respectively static) field of a class or interface C and all of the following conditions hold:

  • The usage occurs in an instance (respectively static) variable initializer of C or in an instance (respectively static) initializer of C.

  • The usage is not on the left hand side of an assignment.

  • The usage is via a simple name.

  • C is the innermost class or interface enclosing the usage.

It is a compile-time error if any of the four requirements above are not met.

So, since the assignment is on the left hand side of an assignment, it is explicitly not one of the cases where the reference has to be after the declaration.

  1. Why is the initialization done after the first assignment (this contradicts the term "initialization")?

This is answered by section 12.4.2.

Next, 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.

All initializations happen as though they were inside a static initialization block, in the order the appear relative to every other initialization or static block. The field starts with it's default value of null, is used in the static block, and is then initialized.

  1. I always thought that explicitly initializing with "null" is the same as omitting the initialization. This is obviously wrong with this example... was I just thinking the wrong thing or does this contradict the spec?

It's not quite the same, for the reasons you're seeing here. If you modify the value in a static block before the initialization happens, it will get set back to null during the static initialization phase.

  1. Is this really the wanted Java behavior? Is it a bug?

Yes, this is the wanted behavior. As the JLS sections linked above show, this isn't just an unintended thing. The rules about it are explicitly stated to allow this.

Community
  • 1
  • 1
resueman
  • 10,572
  • 10
  • 31
  • 45
  • Thank you very much. This looks like a great answer! I will study it more closely before I accept it later. – Wolf Nov 30 '16 at 14:26