Daniel Schneller works as a software developer, database administrator, and general IT professional for an independent software vendor in the retail sector. In 2002, he started focusing on enterprise-level Java development and has since gained extensive knowledge and experience implementing large scale systems based on Java EE and relational databases, especially MySQL since version 4.0. He recently published the MySQL Admin Cookbook with Packt Publishing Ltd. Currently, he is mostly involved with the ongoing development of framework-level functionality, ranging from JavaEE to JavaME and OSGi platforms. He blogs about Java, MySQL, Windows, Linux, and other insanities at http://www.danielschneller.com Daniel is a DZone MVB and is not an employee of DZone and has posted 7 posts at DZone. You can read more from them at their website. View Full User Profile

Java Object Initialization Order - Know your JLS!

07.19.2010
| 14661 views |
  • submit to reddit

Recently I came across an interesting problem whose solution eluded me at first glance. Consider these three classes:

package com.ds.test;

public class Upper {
String upperString;

public Upper() {
Initializer.initialize(this);
}
}
package com.ds.test;

public class Lower extends Upper {

String lowerString = null;

public Lower() {
super();
System.out.println("Upper: " + upperString);
System.out.println("Lower: " + lowerString);
}

public static void main(final String[] args) {
new Lower();
}
}

package com.ds.test;
public class Initializer {
static void initialize(final Upper anUpper) {
if (anUpper instanceof Lower) {
Lower lower = (Lower) anUpper;
lower.lowerString = "lowerInited";
}
anUpper.upperString = "upperInited";
}
}

What output is to be expected from running the Lower class? In this very reduced example it is much easier to get a view of the whole situation - in reality where this occurred there was a lot more code to distract one's attention...

Anyway, this is what the output looks like:

Upper:  upperInited
Lower: null;

While the little example uses Strings, the real code of Initializer had a delegate object registered with the equivalent of the Lower class - at least that was the intention. For some reason however did this not work when running the application. Instead, the default path was taken - the one for the delegate object being not set (null).

Now, change the code of Lower slightly:

package com.ds.test;

public class Lower extends Upper {

String lowerString;

public Lower() {
super();
System.out.println("Upper: " + upperString);
System.out.println("Lower: " + lowerString);
}

public static void main(final String[] args) {
new Lower();
}
}

The output is now:

Upper:  upperInited
Lower: lowerInited

Notice the difference in the code?

Yes, the lowerString field is no longer explicitly set to null. Why would this make a difference? Isn't the default value for reference type fields (such as String here) null anyway? Of course, it is. However it turns out that this tiny little change - which apparently would not change the code's behavior in any way - makes this thing fly or not fly.

So what is going on? It becomes clear when looking at the initialization order:

  1. main() calls the Lower constructor.
  2. An instance of Lower is prepared. That means, all fields are created and populated with default values, i. e. null for reference types, false for booleans and so on. At this time, any inline assignments to the fields have not taken place!
  3. The super-constructor is called. This is mandated by the language spec. So, before anything else happens, Upper's constructor is called.
  4. The Upper constructor runs and hands a reference to the freshly created instance to the Initializer.initialize() method.
  5. The Initializer attaches new Strings to both fields. It does so by using a somewhat dirty instanceof check - not a particularly good design pattern, but possible, nevertheless. Once that has happened, both the upperString lowerString references are no longer null.
  6. The Initializer.initialize() call finishes, as does the Upper constructor.
  7. Now it becomes interesting: Construction of the Lower instance continues. Assuming there is no explicit =null assignment in the lowerString field declaration, the Lower constructor resumes execution and prints out the two Strings that are attached to the fields.
    However, if there is an explicit assignment to null, execution has a slightly different flow: Just when the super constructor is done, any variable initializers are executed (see section 12.5 of the Java Language Spec), before the rest of the constructor is run. In this case the String reference that was previously assigned to lowerString is not overwritten with null again! Only then does the rest of the constructor continue execution, now printing lowerString: null.

Apart from being a nice example for why it is handy to be aware of some of the minutiae of object creation (or knowing where to look in the JLS, printed or online) this shows why it is a bad idea to write the Initializer like this. It should not be aware of Upper's subclasses at all! Instead, if for some reason initialization of certain fields cannot be done in the Lower class itself, it will just require its own variant of some sort of initialization helper. In that case, it would really make no difference if you used String lowerString; or String lowerString = null; - just as it should be.

 

From http://www.danielschneller.com/2010/07/java-object-initialization-order-know.html

Published at DZone with permission of Daniel Schneller, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags:

Comments

Arun Kumar replied on Mon, 2010/07/19 - 7:14am

Nice article . The topic should have been ideally Java Variable initialization , you could ve clearly explained the JLS by following code  

public class Lower extends Upper{
String lowerString ="after assignment";

public Lower() {
super();
lowerString ="after initialization";
System.out.println("Upper: " + upperString);
System.out.println("Lower: " + lowerString);
}

Correct me if am wrong  :) 

Prasanna Jain replied on Mon, 2010/07/19 - 9:57am

Good Example !!! Please keep up the good work

Guillaume Jeudy replied on Mon, 2010/07/19 - 10:22am

This example might not reflect this, but this can be a case where doing too much work in constructors is bad.

 I prefer to have my factory call initialize() method after the object is constructed. This will keep you away of all the pitfalls related to object construction in Java.

David Karr replied on Mon, 2010/07/19 - 12:04pm

I would assume in point 7 that "Just when the super" should be "Just after the super"and "lowerString is not overwritten" should be "lowerString is now overwritten".

Rosdi Kasim replied on Mon, 2010/07/19 - 10:10pm

Point number 7, I think you meant:

 "...the String reference that was previously assigned to lowerString is _NOW_ overwritten with null again! "

Stefan Langer replied on Tue, 2010/07/20 - 3:45am

Referring to "this" and passing it along while the object is under construction is always tricky especially when using static methods. As mentioned in the article don't do this. Instead defer the initialisation after object construction.

Arun Murali replied on Tue, 2010/07/20 - 4:03am

Nice. I wonder where static blocks come in.

Arun Kumar replied on Tue, 2010/07/20 - 4:12am in response to: Arun Murali

static blocks doesnt get applied  to object instances.

 

If u are setting some value in static block , then it will be getting overidden during your instance creation.

Allard Buijze replied on Tue, 2010/07/20 - 5:02am

Thanks for the post. It is -in general- a very bad practice to have a "this" instance escape the constructor. The problem you describe here is one of the reasons. Once concurrency starts to play a role, all guarantees provided be the JMV disappear (see Java Concurrency in Practice, by Brian Goetz). For similar reasons as the above, the constructor should never call any overridable (i.e. non-private and non-final) methods either.

Daniel Schneller replied on Tue, 2010/07/20 - 12:40pm in response to: David Karr

You are, of course, correct. I tend to make this mistake time and again :) Corrected on my blog.

Suresh Murthy replied on Wed, 2010/07/21 - 2:59am

Hi Daniel, Great post. I like it when some dissects a problem and gets to its root to find out the cause of the issue. I remember reading a wonderful article on artima developers by Bill Venners on Object Initialization.

Here's the link: http://www.artima.com/designtechniques/desinit.html

By far this has been one of the best articles I found on object initialization in Java.

Another related link: http://www.artima.com/designtechniques/initialization.html

Thomas Kern replied on Thu, 2012/09/06 - 10:55am

Initially thought it was just rereading the uninitialized space declared for lowerString, but I see now that you are right.

Interesting...

http://www.java-tips.org 

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.