Когда инициализируется элемент?

#java #oop #inheritance

#java #ооп #наследование

Вопрос:

Рассмотрим следующие классы:

 public class A {
    String bar = "A.bar";

    A() { foo(); }

    public void foo() {
        System.out.println("A.foo(): bar = "  
        bar);
    }
}


public class B extends A {
    String bar = "B.bar";

    B() { foo(); }

    public void foo() {
        System.out.println("B.foo(): bar = "  
        bar);
    }
}

public class C {
    public static void main(String[]
    args) {
        A a = new B();
        System.out.println("a.bar = "
          a.bar);
        a.foo();
    }
}
  

Теперь напечатана первая строка: B.foo(): bar = null . Насколько я понимаю, инициализация элементов предшествует конструктору, но я вижу, что здесь это не так.

Почему на этом этапе B#bar не инициализируется?
Когда B#bar инициализируется?

Спасибо!

Обновить:
После прочтения ваших ответов, пожалуйста, взгляните на этот контрпример:

 public class Bazz {

    public int a = 42;

    public Bazz() {
        System.out.println(a);
    }

    public static void main(String[] args) {
        Bazz bazz = new Bazz();
    }
}
  

В данном случае a это не 0 так. Вместо этого конструктор Bazz() печатает 42 .
В чем разница?

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

1. Метод переопределен, но вы объявляете новую отдельную переменную.

Ответ №1:

Позвольте мне перечислить шаги:

  • A a = new B(); // Он видит, что B является дочерним элементом, поэтому вызывает конструктор A (B.bar еще не инициализирован)
  • Вызывается конструктор A foo . Поскольку он был переопределен B, вызывается B foo и отображает значение null.
  • Инициализируется после завершения работы конструктора A B.bar .
  • Затем вызывается конструктор B.

Примечание: даже если super() не вызывается в B, компилятор автоматически добавляет этот оператор.

РЕДАКТИРОВАТЬ: Как компилятор видит конструктор B:

super();

this.bar = «B.bar»;

foo();

ОБНОВЛЕНИЕ: ваш новый результат вытекает из того же объяснения, которое я дал ранее:

  • Bazz bazz = new Bazz(); // При вызове конструктора переменная a инициализируется значением 42

  • System.out.println(a); // выводит 42

Переменные экземпляра инициализируются до выполнения конструктора, но после выполнения конструктора super. Обратитесь к моей правке выше.

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

1. Теперь я понял. (1) конструктор супер (2) переменных (3). Спасибо!

2. @user3307716 О, ты мог бы завернуть Bazz bazz = new Bazz(); и System.out.println(a); в код.

Ответ №2:

Вы создаете через new B() , и в конструкторе B() он вызывает super() .

Таким образом, ваш код вызывает A a = new B(); , который попадает в System.out.println("B.foo(): bar = " bar); . На данный момент a сборка не завершена (не полностью инициализирована), поэтому B.bar есть null . bar будет инициализировано с помощью = "B.bar" , когда конструктор нажмет step-return at } .

TL; DR:

Почему на этом этапе B#bar не инициализируется?

Потому что a не полностью инициализирован в System.out.println("B.foo(): bar = " bar); .

Когда B#bar инициализируется?

} (Завершение конструктора).

Для записи, вот стандартный журнал:

B.foo(): bar = null // bar не был инициализирован. Мы находимся на: A() { foo(); }

B.foo(): bar = B.bar // был инициализирован bar. Мы находимся на: B() { foo(); }

a.bar = A.bar // Вы вызвали «println a.bar = a.bar «. В A a , bar равно.bar.

B.foo(): bar = B.bar // foo() полиморфен и, поскольку a является class B , B.foo() был вызван, отсюда и эта строка.


@edit: Bazz.a имеет тип int . int никогда не будет null , и компилятор автоматически принимает такие переменные для замены ваших операторов при передаче компилятору. Так System.out.println(a); становится System.out.println(42); .

Ответ №3:

При создании объекта из класса первым запускается статический инициализатор.

Он инициализирует статические атрибуты и статические методы,

Затем запускается инициализатор объекта,

Сначала запускается конструктор объекта B() ,

таким образом, нестатические атрибуты ( bar ) инициализируются позже. таким образом, атрибуты инициализируются после выполнения конструктора. Итак, bar — это null

Если вы объявили bar как статический,

 static String bar = "B.bar";
  

Вы можете сначала инициализировать переменную bar;

вывод будет;

B.foo(): bar = B.bar

B.foo(): bar = B.bar

a.bar = A.bar

B.foo(): bar = B.bar

Ответ №4:

Порядок такой:

  1. Инициализация A.bar
  2. Выполнение A конструктора
  3. Инициализация B.bar
  4. Выполнение B конструктора

Обратите внимание, что bar in A — это переменная, отличная от bar in B . Поскольку конструктор A вызывает переопределенный метод, этот метод может получить доступ к переменной экземпляра в B до ее инициализации.