Почему выводится следующий результат? Являются ли поля класса не инициализированными перед конструктором?

#java #inheritance #constructor #initialization #overriding

Вопрос:

Я хочу понять, почему результат следующего кода:

 public class Base {
    private int member1 = 1;
    private int member2 = 2;

    public Base() {
        System.out.println("base ctor");
        print();
    }

    public void print() {
        System.out.println(member1   ""   member2);
    }
}

class Derived extends Base {
    private int member3 = 3;
    private int member4 = 4;

    public Derived() {
        System.out.println("derived ctor");
        print();
    }

    public void print() {
        System.out.println(member3   ""   member4);
    }
}

class Test{
    public static void main(String[] args) {
        Derived d = new Derived();
    }
}
 

является:

 base ctor
00
derived ctor
34
 
  • конструктор производного класса неявно вызывает его супер
  • конструктор. конструктор super (базовый класс) выводит «базовый ctor»

супер конструктор звонки print() которая печатает member 1 , member 2 я думал, что member1 и member2 уже инициализирован, потому что, как я думал, что я знаю, когда мы создаем объект порядке вещей статические поля и блоки идут в первую очередь (если его первой загрузки класса), то экземпляр вещи (например, поля и человек) идут по порядку, что означает, что предшествует в порядке код.

Здесь — поля появляются перед конструктором, что означает, что он должен запуститься и уже инициализировать член1 и член2 до того, как он достигнет печати.

Почему он печатается 0 0 ?

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

Спасибо.

Ответ №1:

Вы вызываете print метод Derived дважды — один раз во время Base конструктора и один раз во время Derived конструктора. Это потому Derived.print() , что переопределяет Base.print() .

Так что это ожидание:

super вызовы конструктора print() — который выводит member 1 , member 2

… неверно. Он вызывает print() реализацию Derived , которая печатает member3 и member4 . Они еще не были инициализированы.

Порядок исполнения является:

  • Инициализаторы полей базового класса
  • Тело конструктора базового класса
  • Инициализаторы полей производного класса
  • Тело конструктора производного класса

Если вы просто измените два print своих метода на print1 и print2 (и соответствующим образом измените вызывающий код), он напечатает:

 base ctor
12
derived ctor
34
 

… как вы и ожидали.

Комментарии:

1. Вопрос в том, почему, почему print() вызов в Base конструкторе фактически запускает print() то, что находится в Derived классе? Я знаю , что объект является a new Derived , но означает ли это, что даже вызовы из суперконструктора попадут в производную реализацию?

2. Потому что вы переопределили print метод в своем Derived классе (та же подпись метода, что и в вашем Base классе). Если вы переименуете методы, как предложил @ print1 JonSkeet, и print2 у вас не возникнет такой проблемы.

3. @NoobCoder: Да, используемая реализация метода не меняется в течение срока службы объекта. Это Derived объект с самого начала, поэтому любой вызов print вызовет Derived.print , даже в Base конструкторе .

4. @JonSkeet этого не знал, спасибо. Что если бы мы объявили объект как Base obj = new Derived и вызвали бы print() базовый конструктор? Мне так странно думать, что если мы вызовем print() конструктор класса X (который я считал «святым», потому что это на самом деле создает объект), то фактическое print() , которое будет выполнено, будет другим.

5. @NoobCoder: Вам было бы очень легко проверить это самостоятельно, но нет, тип переменной, которой вы назначаете, не имеет значения. Тип объекта, который отвечает за определение используемой реализации метода, остается неизменным на протяжении всего срока его службы.