Java неясный результат - PullRequest
       10

Java неясный результат

3 голосов
/ 11 сентября 2011

Я определяю три класса (A, B, C):

public class A {
     int i = 5;
     public A() {
       foo();
     }
     public void foo() {
       System.out.println(i);
     }
}

class B extends A {
     int i = 6;
}

class C extends B {
        int i = 7;
     public void foo() {
        System.out.print(super.i);
     }
     public static void main(String[] args) {
        C c = new C();
     }
}

ожидаемый результат: 6 но программа возвращает: 0

может кто-нибудь объяснить результат? Ваша помощь будет оценена.

Ответы [ 3 ]

5 голосов
/ 11 сентября 2011

Класс C переопределяет A.foo(), и полиморфизм активен даже в конструкторе в Java.Поэтому, когда конструктор в A вызывает foo(), когда мы создаем экземпляр C, на самом деле вызывается C.foo().

C.foo() в свою очередь выводит B.i,таким образом, мы можем ожидать, что 6 будет распечатан - но инициализаторы переменных экземпляра выполняются только после конструкторов суперкласса, поэтому в точке выполнения B.i равно 0.

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

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

Перезапись кода во избежание использования инициализаторов переменных и теневого копирования делает это более понятным,при сохранении эквивалентного кода:

public class A {
  int ai;

  public A() {
    super();
    ai = 5;
    foo();
  }

  public void foo() {
    System.out.println(ai);
  }
}

class B extends A {
  int bi;

  public B() {
    super();
    bi = 6;
  }
}

class C extends B {
  int ci;

  public C() {
    super();
    ci = 7;
  }

  public void foo() {
    System.out.print(bi);
  }

  public static void main(String[] args) {
    C c = new C();
  }
}

Кстати, «скрывающая переменную» часть этого не вступит в игру, если вы сделаете все свои поля приватными, с чего я и начинаю.буду рекомендоватьЭто просто оставляет проблемы вызова виртуальных методов из конструктора, что, как правило, является плохой идеей из-за ожидания того, что объект сможет работать до того, как он получит полную инициализацию, и, возможно, неожиданного времени выполнения инициализатора переменной.

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

1 голос
/ 11 сентября 2011

Переменная никогда не инициализируется в классе A, поэтому она печатает переменную по умолчанию для prmiitive int, равного 0. Дело в том, что хотя super вызывается для конструкторов в дереве иерархии, конструктор не инициализирует i,это делается при инициализации, которая происходит после конструктора.

0 голосов
/ 11 сентября 2011

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

Этот пример возвращает «6»:

public class A {
     int i = 5;
     public A() {
       foo();
     }
     public void foo() {
       System.out.println(i);
     }
     public static void main(String[] args) {
        C c = new C();
        c.foo ();
     }
}

class B extends A {
     int i = 6;
}

class C extends B {
     int i = 7;
     public void foo() {
        System.out.print(super.i);
     }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...