2011-09-11 1 views
3

Я определяю три класса (A, B, C):Java неясными результат

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

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

ответ

5

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

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

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

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

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

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

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

+0

Не после того, как конструктор - но первая часть скомпилированного тела конструктора * после * вызов конструктора суперкласса. –

0

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

Этот пример возвращает «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); 
    } 
} 
+0

Код OP компилируется и работает отлично для меня и печатает 0. Ваш код печатает 06 - вызов 'foo()' из конструктора печатает 0. Это только вызов 'foo()' * после * конструктора который печатает 6. –

Смежные вопросы