Наследование: создание объекта

Скажем, у меня есть эта часть кода:

class Animal {
    int legs = 4;
    int head = 1;
}

public class Dog extends Animal {
    public static void main (String []args) {
        Dog dog = new Dog();
    }
}

Мне известно, что super() неявно помещается в первую строку конструктора no-args, поэтому я знаю, что будет выполнен конструктор Animal, и поэтому будет задана переменная экземпляра Animal.

С этой целью я хотел бы понять, если после того, как эти переменные будут инициализированы супер-конструктором (Animal), эта переменная экземпляра будет храниться там в объекте Animal или скопирована в подкласс (Dog),

В первом случае объект Animal будет неявно создаваться с помощью super();, и всякий раз, когда экземпляр Dog должен будет получить доступ к одной из этих переменных, будет сделан доступ к переменным, хранящимся в экземпляре Animal (созданный на фоне). Или второй случай, если объект Animal будет временно создан, вся переменная экземпляра (в Animal) скопирована в экземпляр Dog, а затем удалит созданный экземпляр Animal.

Я лично считаю, что, например, объект Dog будет напрямую связан с объектом Animal, который напрямую связан с объектом.

Это так?

Ответ 1

Есть только один объект, который сразу (прямо с самого начала) имеет экземпляр Dog. Он имеет все поля Dog (а не то, что у вас здесь есть) и все поля Animal.

Первоначально все переменные будут установлены в значения по умолчанию (0, нуль и т.д.). Затем, когда вы попадаете в тело конструктора каждого класса (после вызова конструктора суперкласса), исполняются инициализаторы переменных экземпляра, тогда выполняется тело конструктора для этого класса.

Не требуется копирования, так как существует только один объект. Например, если вы должны написать конструктор Animal следующим образом:

public Animal() {
    System.out.println(this.getClass());
}

... он распечатает Dog, потому что объект уже является Dog, хотя его конструктор Dog еще не выполнен.

Ответ 2

Ваш Dog extends Animal, а переменные head, legs не являются частными, поэтому вы получите доступ к ним из экземпляра Dog.

На практике происходит следующее:

  • Вы создаете экземпляр Dog, который также является Animal
  • все свойства объекта создаются и инициализируются (включая head, а также после свойств Animal)
  • Вызывается неявный конструктор Dog (он вызывает super())
  • Неявный конструктор Animal называется

И результатом является объект, который является Dog, но неявным образом также и Animal.


Как выглядит объект Dog (давайте забудем о классе Object для примера). Предположим, что Dog имеет свойство public String name;.

Вот карта памяти экземпляра Dog:

Addr  Type     Name      Defined in:
------------------------
| 0 | int    | legs    | Animal
| 1 | int    | head    | Animal
| 2 | String | name    | Dog
------------------------
  • Если у вас есть Animal, вы получаете доступ к head как переменную в адресе 1,
  • Если у вас есть Dog, вы получаете доступ к name в качестве переменной в адресе 2,
  • Если у вас есть Dog, вы получаете доступ к head как переменную по адресу 1

Код, который работает в теле класса Animal, может видеть только адреса 0-1. Код, который работает в классе Dog, может обращаться к адресам 0-2. Если свойство будет private для суперкласса, то этот адрес будет запрещен для подкласса.

Эта карта памяти позволяет легко сбрасывать объекты: используется одно и то же отображение памяти, только обработка (код и видимость) различна.

Я надеюсь, что это немного разъяснит.

Ответ 3

после того, как эти переменные были инициализированы суперконструктором (Animal), эта переменная экземпляра будет храниться там в Animal объект или скопирован в подкласс (Собака).

Во-первых, переменные экземпляра не переопределяются в sub-class. они только видны. Они не будут скопированы в экземпляр Dog. Вы можете получить к ним доступ из класса Dog с экземпляром Dog, если они отмечены как общедоступными, защищенными, так и без модификаторов.

Ответ 4

Нет объекта Animal Object, будет создан только объект Dog. Объект Dog имеет переменные ноги, голова унаследована от объекта Animal. Они будут действовать так, как если бы это были члены объекта Dog. Конструктор класса животных будет вызван, поскольку вы расширяете класс Animal в Dog. И поскольку Animal неявно расширяет Object, конструктор класса Object будет вызван из Animal.

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