18

I have this following piece of code:

public abstract class UCMService{
    private String service;     

    protected DataMap dataMap = new DataMap(); 

    protected class DataMap extends HashMap<String,String> {

        private static final long serialVersionUID = 4014308857539190977L;

        public DataMap(){
            System.out.println("11111");
            put("IdcService",service);
        }
    }

    public UCMService(String service){
        System.out.println("2222");
        this.service = service;
    }
}

Now in console the System.out.println of DataMap constructor is executing before the constructor of UCMService.

I was wondering why it is happening.

2 Answers 2

38

This is because at compile time, the compiler moves every initialization you have done at the place of declaration to every constructor of your class. So the constructor of UCMService class is effectively compiled to:

public UCMService(String service){
    super();  // First compiler adds a super() to chain to super class constructor
    dataMap = new DataMap();   // Compiler moves the initialization here (right after `super()`)
    System.out.println("2222");
    this.service = service;
}

So, clearly DataMap() constructor is executed before the print statement of UCMService class. Similarly, if you have any more constructor in your UCMService class, the initialization will be moved to all of them.


Let's see the byte code of a simple class:

class Demo {
    private String str = "rohit";

    Demo() {
        System.out.println("Hello");
    }
}

compile this class, and execute the command - javap -c Demo. You will see the following byte code of constructor:

Demo();
    Code:
       0: aload_0
       1: invokespecial #1   // Method java/lang/Object."<init>":()V
       4: aload_0
       5: ldc           #2   // String rohit
       7: putfield      #3   // Field str:Ljava/lang/String;
      10: getstatic     #4   // Field java/lang/System.out:Ljava/io/PrintStream;
      13: ldc           #5   // String Hello
      15: invokevirtual #6   // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      18: return

You can see the putfield instruction at line 7, initializes field str to "rohit", which is before the print statement (instruction at line 15)

Sign up to request clarification or add additional context in comments.

1 Comment

Really wonderful explanation. BTW, I haven't try it, but I may want to use some tools to look at compiler generated codes every time I encountered initialization order problem.
13

Short answer:
Because the spec says so.

Long answer:
It would be very strange for the constructor to be unable to use inline-initialized fields.

You want to be able to write

SomeService myService = new SomeService();
public MyConstructor() {
    someService.doSomething();
}

4 Comments

Wait, so some people told me not to do work in the constructor. I took this to mean, only assign variables in the constructor. Does someService.doSomething() count as work?
If doSomething() actually does something, then yes - that's work.
@nos and if doSomething() doesn't do something, then it should be renamed to something more descriptive.
@InspiredOne: A constructor should do whatever is necessary to produce a valid object instance. While it is sometimes reasonable to have objects start in a mode where the only thing one can do with them is call an initialization method, it is often [not always] more helpful to design a type so that there are no "not-yet-useful" modes. That having been said, using field initializers to create things that require cleanup is dangerous because there's no way to clean them up if a constructor fails.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.