2012-02-22 3 views
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); 
    } 
} 

Выход:член не инициализируется в дочернем классе

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

Кажется Init() в ребенка называется, но значение NAME не сохраняется! Зачем? Было бы нормально, если я переведу NAME в родительский. Тем не менее, более удобно оставлять у ребенка, так как он специфичен для собак.

Кроме того, я могу явно вызвать init() в конструкторе child, чтобы решить эту проблему. Это не так хорошо.

+0

Это не ответит на ваш вопрос, но это связано: http://stackoverflow.com/questions/94361/when-do-you-use-javas-override-annotation-and-why – unludo

ответ

6

Порядок, в котором это выполняется следующим образом:

  1. Dog конструктор вызывается.
  2. Он вызывает конструктор Animal.
  3. Конструктор Animal вызывает метод init. Поскольку он переопределяется в Dog, вызывается версия Dog.
  4. В Dog.init вы установили name в "dog".
  5. Dog.init способ возвращается.
  6. Теперь переменные-члены Dog инициализируются. Это устанавливает name в null.

Результат: name будет null.

Отличный пример того, почему вы не должны вызывать методы, которые можно переопределить из конструктора, потому что это приводит к неожиданностям, подобным этому.

Side Примечание: Никогда не делайте этого:

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

Просто сделайте это вместо:

name = "Dog"; 

Она никогда не бывает необходимо явно создать новый String объект из строкового литерала.

+0

Спасибо! Я когда-либо читал что-то о «новой String()», выглядит как эффективная Java. Спасибо, что напомнили. – pengguang001

1

Это связано с тем, что сначала вызывается конструктор Animal, где имя установлено как «собака», а затем вызывается конструктор Dog, где имя сбрасывается в null.

Чтобы сделать его более четким, ваш код аналогичен следующему:

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); 
    } 
} 

Это одна из причин, почему призывающих к переопределение метода в конструктор базового класса является очень плохой практикой.

+0

Спасибо. Я изумлен о «name = null» в конструкторе ребенка! Есть ли ссылка или документ об этом? – pengguang001

+0

На самом деле это довольно просто.Все инициализации, выполненные вне конструктора, фактически выполняются после вызова конструктора суперкласса и перед кодом фактического конструктора. –

Смежные вопросы