views:

329

answers:

5

Somebody tell me:

class MyClass {
  private static MyClass myClass = new MyClass();
  private static final Object obj = new Object();
  public MyClass() {
    System.out.println(obj); // will print null once
  }
}

I wonder, isn't this a bug? Why static objects are not initialized before the constructor runs?

--update

I'd just copied this example program without attention, I thought we were talking about 2 Object fields, now I saw that the first is a MyClass field.. :/

Accepted-answer mark goes to Pyrolistical by the dedication to explain.

Also, thanks Kevin Brock.

I thank everyone who supported, I thank the academy, the friends,.. XD

+9  A: 

Because statics are initialized in the order they are given in source code.

Check this out:

class MyClass {
  private static MyClass myClass = new MyClass();
  private static MyClass myClass2 = new MyClass();
  public MyClass() {
    System.out.println(myClass);
    System.out.println(myClass2);
  }
}

That will print:

myClassObject
null
myClassObject
myClassObject2

EDIT

Ok let's draw this out to be a bit more clear.

Initialize statics one by one in the order as declared in the source code.

Since the first static is initialized before the rest, during the initialization of the first static the rest are null or default values.

During the initiation of the second static the first static is correct but the rest are still null or default.

Is that clear?

Pyrolistical
..and because `myClass` **itself** is static.
BalusC
ok, static is initilized in order. And the statics are before the constructor, so why it is not initialized when the constructor runs?Really looks like a bug for me..
Tom Brito
@Tom, nope you got it wrong.statics are not before constructors. statics are init when constructors are called respectively. In my example when the first static is init with MyClass the constructor is called. When the constructor is running myClass is init (because its running itself), but myClass2 is not. When the second MyClass is init it calls the constructor again and this time myClass was already init and myClass2 is being init at that time
Pyrolistical
so its like statics are initialized in another thread while the constructor runs? Do you have a link to same text explaining this in details?
Tom Brito
Updated post, I hope that is more explanatory
Pyrolistical
I am also surprised you are the first one to ask this question so simply. This is ones of those gotchas
Pyrolistical
A: 

that is because static fields initialized in same order they defined.

antony
this does not answer why it is null at constructor time.
Tom Brito
+16  A: 

That's because Java executes the static section in order it is declared. In your case, the sequence is

  1. new MyClass
  2. new Object

When #1 is executed, obj is still not initialized, so it prints null. Try the following and you will see the difference:

class MyClass {
  private static final Object obj = new Object();
  private static MyClass myClass = new MyClass();
  public MyClass() {
    System.out.println(obj); // will print null once
  }
}

Generally speaking, it is better to avoid such a construct all together. If you are trying to create a singleton, that's how that code fragment should look like:

class MyClass {

  private static final MyClass myClass = new MyClass();

  private Object obj = new Object();

  private MyClass() {
    System.out.println(obj); // will print null once
  }
}
Slava Imeshev
+1 - good answer.
JonH
Correct about the <clinit> execution order. The Singleton isn't really a Singleton though, since the constructor is public.
Rob Heiser
Rob - thanks for pointing out - corrected.
Slava Imeshev
ok, static is initilized in order. And the statics are declared and asked for initialize before the constructor, so why it is not initialized when the constructor runs?Really looks like a bug for me..
Tom Brito
@Tom you have to understand by calling `new MyClass()` in the static you ARE calling the constructor
Pyrolistical
think we are having a communication fail here. I actually know that new MyClass() is a call to a constructor, this does not explain why the static fields are null when the constructor runs. Like the instance fields are initialized before the constructor, the static fields should be either.. but why they don't?
Tom Brito
Static members *are* constructed before the instance constructor runs. It's just that in your code, your static initializer *also* calls your constructor. It's a chicken-and-egg problem.
Dean Harding
+2  A: 

Here's also a good link about how constructors work in Java:

http://stackoverflow.com/questions/2419286/is-the-constructor-for-an-object-invoked-after-object-creation-on-heap-java/2419295#2419295

JonH
This should be posted as comment, as it is not a answer to the question.
Tom Brito
@Tom Brito - No it is an answer to the question, it explains how the constructor works and in what order it runs. Please read it.
JonH
+4  A: 

Let's try a different way to explain this...

This is the sequence the JVM goes through when you first reference the class MyClass.

  1. Load the byte-code into memory.
  2. Memory for the static storage is cleared (binary zero).
  3. Initialize the class:
    1. Execute each static initializer in the order that it appears, this includes static variables and static { ... } blocks.
    2. JVM then initializes your myClass static variable to a new instance of MyClass.
    3. When this happens, the JVM notices that MyClass is already loaded (byte-code) and in the process of being initialized, so it skips initialization.
    4. Allocate memory on heap for object.
    5. Execute constructor.
    6. Print out value of obj which is still null (since it is not part of the heap and constructor initialized variables).
    7. When constructor finishes, execute next static initializer which sets obj to a new instance of Object.
  4. Class initialization done. From this point, all constructor calls will behave as you presume/expect - that is obj would not be null but a reference to an Object instance.

Remember that Java specifies that a final variable is assigned a value once. It is not that it is guaranteed to be assigned a value when the code references it unless you ensure that the code references it after it is assigned.

This is not a bug. This is the defined way to handle usage of the class during its own initialization. If this were not so, then the JVM would go into an infinite loop. See step #3.3 (if the JVM does not skip initialization for a class that is in the process of initialization it would just keep initializing it - infinite loop).

Note as well, this all happens on the same thread that first references the class. Second, the JVM guarantees that initialization will complete before any other thread is allowed to use this class.

Kevin Brock
Nice answer Kevin, best one here
Newtopian