Подтвердить что ты не робот

Порядок вызовов конструктора в многоуровневом наследовании в java

//: c07:Sandwich.java
// Order of constructor calls.
// package c07;
// import com.bruceeckel.simpletest.*;

import java.util.*;

class Meal {
  Meal() { System.out.println("Meal()"); }
}

class Bread {
  Bread() { System.out.println("Bread()"); }
}

class Cheese {
  Cheese() { System.out.println("Cheese()"); }
}

class Lettuce {
  Lettuce() { System.out.println("Lettuce()"); }
}

class Lunch extends Meal {
  Lunch() { System.out.println("Lunch()"); }
}

class PortableLunch extends Lunch {
  PortableLunch() { System.out.println("PortableLunch()");}
}

public class Sandwich extends PortableLunch {
//  private static Test monitor = new Test();
  private Bread b = new Bread();
  private Cheese c = new Cheese();
  private Lettuce l = new Lettuce();
  public Sandwich() {
    System.out.println("Sandwich()");
  }
  public static void main(String[] args) {
    new Sandwich();
   /*
   monitor.expect(new String[] {
      "Meal()",
      "Lunch()",
      "PortableLunch()",
      "Bread()",
      "Cheese()",
      "Lettuce()",
      "Sandwich()"
    });
    // */
  }
} ///:~

Выход этого кода

Meal()
Lunch()
PortableLunch()
Bread()
Cheese()
Lettuce()
Sandwich()  

Так как поля в классе создаются в том порядке, в котором они объявлены, почему бы не

Bread()
Cheese()
Lettuce()

входят в верхнюю часть списка выше?

Кроме того, что он пытается сделать в этом коде?

   monitor.expect(new String[] {
      "Meal()",
      "Lunch()",
      "PortableLunch()",
      "Bread()",
      "Cheese()",
      "Lettuce()",
      "Sandwich()"
    });  

Сначала я подумал, что это анонимный класс, но он не похож на него. Является ли это инициализацией массива String? Почему у него нет имени для переменной String? Скажите, пожалуйста, имя используемой здесь конструкции программирования.

4b9b3361

Ответ 1

Конструктор:

public Sandwich() {
    System.out.println("Sandwich()");
}

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

public Sandwich() {
    super();   // Compiler adds it if it is not explicitly added by programmer
    // All the instance variable initialization is moved here by the compiler.
    b = new Bread();
    c = new Cheese();
    l = new Lettuce();

    System.out.println("Sandwich()");
}

Итак, первый оператор в конструкторе - это цепочка конструктора суперкласса. На самом деле, первый оператор в любом конструкторе цепочки к конструктору суперкласса, если на то пошло. Поэтому сначала вызывается конструктор суперкласса PortableLunch, который снова объединяет вызов его суперклассического класса, из-за super(), добавленного компилятором (помните?).

Эта цепочка вызова конструктора выполняется до класса в верхней части иерархии наследования, тем самым вызывая конструктор класса Object в конце.

Теперь, после выполнения каждого конструктора суперкласса, и все поля суперкласса были инициализированы, непосредственный конструктор подкласса запускает выполнение после вызова super(). И затем, наконец, он возвращается к конструктору Sandwitch(), который теперь инициализирует ваши поля 3.

Итак, в основном ваши поля инициализируются наконец, и, следовательно, они печатаются в конце, как раз перед тем, как печатается Sandwitch().

Обратитесь к JLS - §12.5 - Создание экземпляра нового класса для подробного объяснения процесса создания экземпляра.


Что касается вашей второй части вопроса:

monitor.expect(new String[] {
      "Meal()",
      "Lunch()",
      "PortableLunch()",
      "Bread()",
      "Cheese()",
      "Lettuce()",
      "Sandwich()"
    });  

Этот код создает неназванный массив и инициализирует его несколькими строковыми литералами в одно и то же время. Он похож на способ создания именованного массива:

String[] arr = new String[] { "rohit", "jain" };

Ответ 2

Объекты в вашем примере используют наследование, которое вызывает цепочку конструкторов. При использовании наследования класс, наследующий от другого (subtype), должен вызывать конструктор класса, который он расширяет (super type). Когда существует иерархия типов, то есть несколько классов расширяются друг от друга в цепочке, вызовы суперструктора распространяются на первый класс в цепочке, который не наследуется от другого класса (игнорируя Object).

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

Как только суперструкторы типов в иерархии типов классов были вызваны, поля подтипа объявлены, так как они также могут понадобиться конструктору подтипа. После того, как объявлены поля подтипа, выполняется конструктор подтипа.

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

Meal() (Top of Class Hierarchy, not including Object)
Lunch() (Extends Meal)
PortableLunch() (Extends Lunch)
Bread() (Field of lunch declared before constructor call)
Cheese() (Field of lunch declared before constructor call)
Lettuce() (Field of lunch declared before constructor call)
Sandwich() (Extends Portable Lunch)

Вот действительно хороший обзор создания объекта в Java.

Ответ 3

new String[] {
      "Meal()",
      "Lunch()",
      "PortableLunch()",
      "Bread()",
      "Cheese()",
      "Lettuce()",
      "Sandwich()"
    }

\ выше указано объявление анонимного массива. В этом случае вам не нужно указывать размер.

Все конструкторы суперкласса будут вызываться сначала перед инициализацией ваших переменных экземпляра. т.е.

порядок --- >

  Object(), 
  all your superclass constructors,
  instance variables of this class in that order

Ответ 4

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

Bread()
Cheese()
Lettuce()

после

Meal()
Lunch()
PortableLunch()

о инициализации String [], как вы думаете, не является анонимным классом, является просто созданием объекта String, который необязательно должен быть назначен переменной.