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

Инициализация в полиморфизме переменных

Предположим, что у вас есть следующий код

class A {
    int i = 4;

    A() { 
        print();
    }

    void print () {
        System.out.println("A");
    }
}

class B extends A {
    int i = 2;              //"this line"

    public static void main(String[] args){
        A a = new B();
        a.print();
    }

    void print () {
        System.out.println(i);
    }
}

это напечатает 0 2

Теперь, если вы удалите строку с надписью "this line", код будет печатать 4 4

  • Я понимаю, что если бы не было int я = 2; line,

A a = new B(); вызовет класс A, инициализирует я как 4, конструктор вызова,
который дает управление методу print() в class B и, наконец, печатает 4.

a.print() вызовет метод print() в классе B, потому что методы будут привязываться во время выполнения, что также будет использовать значение, определенное в классе A, 4.

(Конечно, если в моих рассуждениях есть какая-то ошибка, дайте мне знать)

  • Однако я не понимаю, есть ли int я = 2.

почему, если вы вставляете код, первая часть (создающий объект) будет все внезапно печатать 0 вместо 4? Почему это не инициализирует переменную как я = 4, а вместо этого назначает значение по умолчанию?

4b9b3361

Ответ 1

Это комбинация нескольких действий в Java.

  • Переопределение метода
  • Тенерирование переменных экземпляра
  • порядок конструкторов

Я просто просмотрю, что произошло в вашем коде, и посмотрите, понимаете ли вы.

Ваш код концептуально выглядит следующим образом (пропустить main()):

class A {
    int i = 0; // default value

    A() { 
        A::i = 4;  // originally in initialization statement
        print();
    }

    void print () {
        System.out.println("A");
    }
}

class B extends A {
    int i = 0;              // Remember this shadows A::i

    public B() {
        super();
        B::i = 2;
    }

    void print () {
        System.out.println(i);
    }
}

Итак, когда в исходном main(), вы вызвали A a = new B();, он создает B, для которого это происходит:

  • A::i и B::i все находятся по умолчанию 0
  • super(), что означает, что конструктор называется
    • A::i установлено значение 4
    • print(). Из-за позднего связывания он привязан к B::print()
    • B::print() пытается распечатать B::i, который по-прежнему 0
  • B::i установлено значение 2

Затем, когда вы вызываете a.print() в main(), он ограничен B::print(), который печатает B::i (на данный момент это 2).

Следовательно, результат вы видите

Ответ 2

Все переменные экземпляра нового объекта, в том числе объявленные в суперклассах, инициализируются значениями по умолчанию - JLS 12.5

Следовательно, ваша переменная B::i будет инициализирована на 0. Конструктор в B будет выглядеть следующим образом:

B() {
    super();
    i = 2;
}

Поэтому, когда вы вызываете

A a = new B();

Конструктор в вызовет метод print в B, который напечатает i в классе B, который равен 0.

Ответ 3

В вашем случае класс B объявление "i" скрывает объявление "i" в A, а все ссылки на "i" в дочернем классе относятся к B.i, а не к A.i.

Итак, то, что вы видите в A.i, является значением по умолчанию для любого атрибута int в java, который равен нулю.

Переменные экземпляра Java нельзя переопределять в подклассе.

Вы хотите попробовать это для уточнения.

class B extends A {
    int i = 2;              //"this line"

    public static void main(String[] args){
        B b = new B();
        A a = b;
        System.out.println("a.i is " + a.i);
    System.out.println("b.i is " + b.i);
    }

    void print () {
        System.out.println(i);
    }
}

Ouput:

a.i is 4
b.i is 2