2013-12-13 2 views
6

Сегодня я закончил следующий код кода (который, как я признаю, странно, и с тех пор я реорганизовался). Когда я запустил свой модульный тест, я обнаружил, что инициализация поля не была установлена ​​к моменту запуска конструктора суперкласса. Я понял, что я не совсем понимаю порядок инициализации конструктора/поля, поэтому я отправляю в надежде, что кто-то объяснит мне порядок, в котором они происходят.Что такое инициализация поля объекта и порядок конструктора в Java

class Foo extends FooBase { 
    String foo = "foobar"; 

    @Override 
    public void setup() { 
     if (foo == null) { 
      throw new RuntimeException("foo is null"); 
     } 
     super.setup(); 
    } 
} 

class FooBase { 
    public FooBase() { 
     setup(); 
    } 

    public void setup() { 

    } 
} 

@Test 
public void testFoo() { 
    new Foo(); 
} 

Сокращенный backtrace от JUnit выглядит следующим образом, я предполагаю, что ожидал $ Foo. <init> установить foo.

$Foo.setup 
$FooBase.<init> 
$Foo.<init> 
.testFoo 

ответ

7

Да, в Java (в отличие от C#, например) инициализаторы полей называются после конструктором суперкласса. Это означает, что любые вызовы с переопределенным вызовом от конструктора будут называться до, когда выполняются инициализаторы полей.

Упорядочение является:

  • Initialize суперкласс (рекурсивно вызывать эти шаги)
  • Выполнить поле Инициализаторы
  • Execute конструктор тела (после любого конструктора цепочки, которые уже имели место на этапе 1)

В принципе, это плохая идея вызывать неконкретные методы в конструкторах. Если вы это сделаете, запишите его очень, чтобы кто-нибудь, переопределяющий метод, знал, что метод будет вызван до инициализатора поля (или тела конструктора).

Для получения более подробной информации см. JLS section 12.5.

3

Первая операция конструктора всегда является вызовом конструктора суперкласса. Не имея конструктор явно определенный в классе эквивалентно наличию

public Foo() { 
    super(); 
} 

конструктор базового класса, таким образом, называется перед любым полем подкласса был инициализирован. И ваш базовый класс делает что-то, чего следует избегать: вызвать переопределяемый метод.

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

0

Вот пример полиморфизма в псевдо-C#/Java:

class Animal 
{ 
    abstract string MakeNoise(); 
} 

class Cat : Animal { 
    string MakeNoise() { 
    return "Meow"; 
    } 
} 

class Dog : Animal { 
    string MakeNoise() { 
    return "Bark"; 
    } 
} 

Main() { 
    Animal animal = Zoo.GetAnimal(); 
    Console.WriteLine (animal.MakeNoise()); 
} 

Главная функция не знает, типа животного и зависит от поведения конкретной реализации в методе MakeNoise().

class A 
{ 
A(int number) 
    { 
    System.out.println("A's" + " "+ number); 
    } 
} 

class B 
{ 
A aObject = new A(1); 
B(int number) 
    { 
    System.out.println("B's" + " "+ number);   
    } 
A aObject2 = new A(2); 
} 

public class myFirstProject { 
public static void main(String[] args) { 
    B bObj = new B(5); 
    } 
} 

из: в 1 A в 2 Б 5

Мои правила: 1. Не инициализировать значения по умолчанию в объявлении (нуль, ложь, 0, 0,0 ...) , 2.Предпочитайте инициализацию в объявлении, если у вас нет параметра конструктора, который изменяет значение поля. 3. Если значение поля изменяется из-за параметра конструктора, поместите инициализацию в конструкторы. 4. Будьте последовательны в своей практике. (Самое важное правило)

public class Dice 
{ 
private int topFace = 1; 
private Random myRand = new Random(); 

public void Roll() 
    { 
    // ...... 
    } 
} 

или

public class Dice 
{ 
private int topFace; 
private Random myRand; 

public Dice() 
    { 
    topFace = 1; 
    myRand = new Random(); 
    } 

public void Roll() 
    { 
    // ..... 
    } 
} 
Смежные вопросы