1
//src/com/test/animal/Animal.java
package com.test.animal;

public class Animal
{
    Animal()
    {
        init();
    }

    public void init()
    {
        System.out.println("parent init()");
    }
}

//src/com/test/animal/Dog.java 
package com.test.animal;

public class Dog extends Animal
{
    String name = null;

    Dog()
    {
        super();
    }

    public void init()
    {
        System.out.println("child init()");
        super.init();
        name = new String("dog");
        System.out.println("name: "+name);
    }

    public static void main(String[] args)
    {
        Dog d = new Dog();
        System.out.println("name: "+d.name);
    }
}

The output is:

child init()
parent init()
name: dog
name: null

It seems the init() in child is called, but NAME value not saved! Why? It would be OK if I move NAME to parent. However, it's more resonable to retain in child, since it's Dog-specific.

Also, I can explicitly call init() in child's constructor to solve this issue. It's not that good.

pengguang001
  • 4,045
  • 6
  • 27
  • 31
  • It does not answer your question, but it's related: http://stackoverflow.com/questions/94361/when-do-you-use-javas-override-annotation-and-why – unludo Feb 22 '12 at 08:24

2 Answers2

6

The order in which this executes is as follows:

  1. The Dog constructor is called.
  2. It calls the Animal constructor.
  3. The Animal constructor calls the init method. Because it is overridden in Dog, the Dog version is called.
  4. In Dog.init you set name to "dog".
  5. The Dog.init method returns.
  6. Now, the member variables of Dog are initialized. This sets name to null.

Result: name will be null.

An excellent example of why you should not call methods that can be overridden from a constructor - because it leads to surprises like this.

Side note: Never do this:

// Unnecessarily creating a new String object
name = new String("dog");

Just do this instead:

name = "Dog";

It is never necessary to explicitly create a new String object from a string literal.

Jesper
  • 202,709
  • 46
  • 318
  • 350
1

This is because the constructor of Animal is first called, where name is set to "dog", and then the constructor of Dog is called, where name is reset to null.

To make it clearer, your code is similar to the following:

public class Dog extends Animal
{
    String name;

    Dog()
    {
        super();
        name = null;
    }

    public void init()
    {
        System.out.println("child init()");
        super.init();
        name = new String("dog");
        System.out.println("name: "+name);
    }

    public static void main(String[] args)
    {
        Dog d = new Dog();
        System.out.println("name: "+d.name);
    }
}

This is one of the reasons why calling an overridable method in a base class constructor is very bad practice.

JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • Thanks. I'm astonished about the 'name=null' in child's constructor! Is there any link or doc about this? – pengguang001 Feb 22 '12 at 08:39
  • It's pretty simple actually. All the initializations done outside of the constructor are in fact done after the call to the superclass constructor, and before the code of the actual constructor. – JB Nizet Feb 22 '12 at 08:51