Java: вызов конструктора суперкласса, который вызывает переопределенный метод, который устанавливает поле подкласса

У меня есть этот демонстрационный код:

class Test2 extends Test {
    public int number = 0;

    @Override
    public void set(){
        number = 1;
        info();
    }

    @Override
    public void info(){
        System.out.println(number);
    }
}

public class Test {
    public Test(){
        set();
    }

    public void set(){
    }

    public void info(){
    }

    public static void main(String[] args){
        Test2 object = new Test2();
        object.info();
    }
}

Код дает этот вывод:

1
0

Почему? Я ожидаю этот вывод:

1
1

На мой взгляд, основная функция вызывает конструктор класса Test2 для создания объекта. Конструктор автоматически вызывает конструктор суперкласса. Этот конструктор вызывает метод set(), который переопределен. Поэтому вызывается метод set() класса Test2. Этот метод устанавливает поле и вызывает метод info(), который записывает число. Затем основная функция снова вызывает метод info() созданного объекта.

Числовое поле установлено правильно, так как вывод первой строки равен «1». Но почему вторая строка содержит 0? Кажется, что поле вообще не было установлено. Можешь объяснить?

Что я должен сделать, чтобы получить ожидаемое поведение? Заранее спасибо!


person user1608790    schedule 23.08.2012    source источник
comment
связанные: stackoverflow.com/a/5692162/697449   -  person Paul Bellora    schedule 24.08.2012
comment
Похоже, что инициализаторы полей запускаются после конструктора в Java, поэтому значение сбрасывается в 0 инициализатором после того, как метод set устанавливает его в 1. Вам следует избегать вызова виртуальных методов в конструкторах, поскольку вы вызываете поведение в частично сконструированном объекте. .   -  person Lee    schedule 24.08.2012


Ответы (3)


class Test2 {
    public int number = 0;
}

эквивалентно

class Test2 {
    public int number;

    public Test2() {
        super();
        number = 0;
    }
}

Таким образом, при вызове суперконструктора поле number устанавливается в 1. После возврата из суперконструктора выполняется присвоение number 0.

Просто удалите назначение, и оно должно вести себя так, как вы ожидаете.

person xehpuk    schedule 23.08.2012
comment
Ну, у меня уже есть идея, как избежать проблемы - сначала создать экземпляр, а затем инициализировать. Но это очень полезно. - person user1608790; 24.08.2012

Если сказать, что Animal расширяет Dog, и вы вызываете Animal a = new Dog()

Последовательность шагов будет такой, как показано ниже

  1. Инициализируются статические поля животных

  2. Статический блок животного выполнен

  3. Статические поля собаки инициализированы ‹ Они будут повторно инициализированы, если животное каким-либо образом изменило их>

  4. Выполняется статический блок собаки

  5. Нестатические поля животных инициализированы. Они будут повторно инициализированы, если животное каким-либо образом изменило их>

  6. Конструктор животных выполнен

  7. Нестатическое поле собаки инициализировано ‹ Они будут повторно инициализированы, если животное изменит их>

  8. Конструктор собаки выполнен

person check    schedule 04.05.2014

Ваш код нарушает золотое правило Java - никогда не вызывайте в конструкторе метод, который может быть переопределен подклассом, т.е. такие методы должны быть закрытыми.

К тому времени, когда конструктор по умолчанию в Test2 завершается, он перезаписывает начальное значение 1, назначенное ему через инициализатор public int number = 0;.

person JamesB    schedule 23.08.2012