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

Что происходит, когда подклассы не определяют конструктор в Java?

У меня есть несколько случаев, о которых мне интересно. Во-первых, если у вас нет конструктора:

class NoCons { int x; }

Когда я делаю new NoCons(), вызывается конструктор по умолчанию. Что он делает точно? Установил ли он x значение 0, или это происходит в другом месте?

Что делать, если у меня есть такая ситуация:

class NoCons2 extends NoCons { int y; }

Что происходит, когда я звоню new NoCons2()? Создается конструктор по умолчанию NoCons по умолчанию, а затем конструктор NoCons2? Каждый из них устанавливает соответствующие поля x и y в 0?

Как насчет этой версии:

class Cons2 extends NoCons { int y; public Cons2() {} }

Теперь у меня есть конструктор, но он не вызывает конструктор суперкласса. Как x когда-либо инициализируется? Что, если бы у меня была такая ситуация:

class Cons { int x; public Cons() {} }
class NoCons2 extends Cons { int y;  }

Будет ли вызываться конструктор Cons?

Я мог бы просто попробовать все эти примеры, но я не могу сказать, когда запускается конструктор по умолчанию. Какой общий способ подумать об этом, чтобы я знал, что произойдет в будущих ситуациях?

4b9b3361

Ответ 1

Когда класс Java не имеет явно определенного конструктора, добавляется публичный конструктор по умолчанию no-args, поэтому:

class Cons { int x; } 

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

class Cons { int x; public Cons() {} }

Конструктор подкласса, который явно не определяет, какой из его родительских конструкторов он вызывает, автоматически вызовет конструктор по умолчанию в родительском классе до, он сделает что-нибудь еще. Итак, предполагая:

class A { public A() { System.out.println("A"); } }

то это:

class B extends A { public B() { System.out.println("B"); } }

в точности эквивалентно:

class B extends A { public B() { super(); System.out.println("B"); } }

и вывод в обоих случаях будет:

A
B

Итак, когда вы это сделаете:

new NoCons2();

Порядок:

  • Вызывается конструктор по умолчанию NoCons, хотя это технически первая часть (2); и затем
  • Вызывается конструктор по умолчанию NoCons2.

Ответ 2

Вы хотите обратиться к разделу "Спецификация языка Java" 12.5 "Создание экземпляров нового класса" , чтобы получить официальные правила создания объектов. Соответствующий раздел:

Прежде чем в результате возвращается ссылка на вновь созданный объект, указанный конструктор обрабатывается для инициализации нового объекта с помощью следующей процедуры:

  • Назначьте аргументы для конструктора новым созданным переменным параметра для этого вызова конструктора.
  • Если этот конструктор начинается с явного вызова конструктора другого конструктора в том же классе (с помощью этого), то затем оценивайте аргументы и обрабатывайте вызов конструктора рекурсивно, используя эти пять шагов. Если вызов конструктора завершается внезапно, то эта процедура завершается внезапно по той же причине; в противном случае, перейдите к шагу 5.
  • Этот конструктор не начинается с явного вызова конструктора другого конструктора в том же классе (используя это). Если этот конструктор относится к классу, отличному от Object, то этот конструктор начнет с явного или неявного вызова конструктора суперкласса (используя super). Оцените аргументы и обработайте вызов конструктора суперкласса рекурсивно, используя эти пять шагов. Если вызов конструктора завершается внезапно, то эта процедура завершается внезапно по той же причине. В противном случае перейдите к шагу 4.
  • Выполните инициализаторы экземпляра и инициализаторы переменных экземпляра для этого класса, присваивая значения инициализаторов переменной экземпляра соответствующим переменным экземпляра в порядке слева направо, в котором они отображаются в текстовом виде в исходном коде для класса. Если выполнение любого из этих инициализаторов приводит к исключению, то никакие новые инициализаторы не обрабатываются, и эта процедура завершается внезапно с тем же исключением. В противном случае перейдите к шагу 5. (В некоторых ранних реализациях компилятор неправильно пропустил код для инициализации поля, если выражение инициализатора поля было константным выражением, значение которого было равно значению инициализации по умолчанию для его типа.)
  • Выполните оставшуюся часть тела этого конструктора. Если это выполнение завершается внезапно, то эта процедура завершается внезапно по той же причине. В противном случае эта процедура завершится нормально.

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

new NoCons2();
  • Сначала вызывается супер конструктор (вызов для super() вставлен для вас, потому что вы явно не вызываете вызов).
  • Инициализируются переменные экземпляра для создаваемого класса.
  • Остальная часть тела конструктора выполняется (ничего в вашем случае).

В вашем первом примере x будет установлен во время построения NoCons, а y будет задано при построении NoCons2.

Таким образом, точная последовательность событий в этом примере будет выглядеть примерно так:

  • Конструктор NoCons2.
  • Звонок на super(), goto 3
  • Создан конструктор NoCons.
  • Вызов super(), который является неявным вызовом Object().
  • Что бы ни случилось в конструкторе объекта.
  • x устанавливается в 0.
  • завершите тело конструктора NoCons, верните управление обратно в конструктор NoCons2.
  • y установлено в 0.
  • завершить тело конструктора NoCons2
  • Конструкция объекта NoCons2 завершена.

Ответ 3

cletus ответил на самый большой вопрос. Ответ другой - это то, что переменные-члены в Java инициализируются равными 0, null или false (в зависимости от типа).

Ответ 4

Вот что происходит, когда вызывается "новое":

  • выделена память (достаточно для хранения всех членов данных класса и всех родительских классов и некоторой служебной информации).
  • выделенная память установлена ​​на ноль (что означает 0, 0.0, false, null в зависимости от типа)
  • вызывается конструктор для класса, который после вызова "new".
  • больше событий (после следующей части)

Если вы не предоставляете конструктор, компилятор делает следующее:

  • создает конструктор без аргументов
  • созданный конструктор имеет тот же доступ, что и класс (так что public или package) Вызывается
  • super().

Итак, когда конструктор класса после "нового" называется самым первым, что он делает, это вызов "super()", который вызывает родительский конструктор. Это происходит до java.lang.Object.

Прежде чем запустить тело конструктора, VM выполнит следующее:

  • присваиваются переменные экземпляра, которым присваиваются значения
  • тогда выполняется инициализационный блок экземпляра, если он присутствует.

В следующем коде показано все это:

public class Main
{
    private Main()
    {
    }

    public static void main(final String[] args)
    {
        final Foo fooA;
        final Foo fooB;

        fooA = new Foo(7);
        System.out.println("---------------------");
        fooB = new Foo(42);
    }
}

class Bar
{
    protected int valueA = getValue("valueA", 1);
    protected int valueB;

    static
    {
        System.out.println("static block for Bar happens only one time");
    }

    {
        System.out.println("instance block for Bar happens one time for each new Bar");
        System.out.println("valueA = " + valueA);
        System.out.println("valueB = " + valueB);
    }

    Bar()
    {
        super();  // compiler adds this - you would not type it in
        System.out.println("running Bar()");
        System.out.println("valueA = " + valueA);
        System.out.println("valueB = " + valueB);
        valueB = getValue("valueB", 2);
    }

    protected static int getValue(final String name, final int val)
    {
        System.out.println("setting " + name + " to " + val);
        return (val);
    }
}

class Foo
    extends Bar
{
    protected int valueC = getValue("valueC", 1);
    protected int valueD;

    static
    {
        System.out.println("static block for Foo happens only one time");
    }

    {
        System.out.println("instance block for Foo happens one time for each new Foo");
        System.out.println("valueC = " + valueC);
        System.out.println("valueD = " + valueD);
    }

    Foo(final int val)
    {
        super();  // compiler adds this - you would not type it in
        System.out.println("running Foo(int)");
        System.out.println("valueC = " + valueC);
        System.out.println("valueD = " + valueD);
        valueD = getValue("valueD", val);
    }
}