2012-08-15 14 views
15

Mój kod to:Java wyjście Fragment kodu wyjaśnienie wymagane

class Foo { 
    public int a=3; 
    public void addFive() { 
    a+=5; 
    System.out.print("f "); 
    } 
} 

class Bar extends Foo { 
    public int a=8; 
    public void addFive() { 
    this.a += 5; 
    System.out.print("b "); 
    } 
} 

public class TestClass { 
    public static void main(String[]args) { 
    Foo f = new Bar(); 
    f.addFive(); 
    System.out.println(f.a); 
    } 
} 

wyjściowa:

b 3 

Proszę mi wyjaśnić, dlaczego jest wyjście na to pytanie "B 3" a nie „B 13 "skoro metoda została nadpisana?

+0

To jest pytanie dotyczące SCJP, prawda? –

+0

yep @PrakharMohan czy pojawiłeś się również w tym samym ??? Jeszcze nie ... :) –

Odpowiedz

14

Nie można nadpisać zmiennych w Java, stąd faktycznie mają dwa a zmienne - jeden w Foo i jeden w Bar. Z drugiej strony metoda addFive() jest polimorficzna, dlatego modyfikuje się Bar.a (Bar.addFive(), pomimo, że typ statyczny f jest Foo).

Ale na końcu uzyskujesz dostęp do f.a i to odniesienie jest rozwiązywane podczas kompilacji przy użyciu znanego typu f, który jest Foo. I dlatego Foo.a nigdy nie został dotknięty.

Przyszłe zmienne BTW w Javie powinny być nigdy nie być być publiczne.

+4

No chyba, że ​​masz 'GridBagConstraints' lub podobne obiekty. Ale dla początkujących, ** nigdy ** to odpowiednia rada :) –

18

F jest odniesienie typu Foo i zmienne nie są polimorficzne tak f.a odnosi się do zmiennej, która jest z Foo3

Jak to sprawdzić?

Aby to sprawdzić można usunąć a zmienną z Foo, da skompilować błąd czasu

Uwaga: Producent członek zmienne private i wykorzystania akcesorów do nich dostęp


też Patrz:

+10

Aby nieco rozszerzyć tę odpowiedź, jest to jeden z powodów, dla których często bezpieczniejsze jest używanie getterów zamiast bezpośredniego dostępu do zmiennych. –

+0

@DaveNewton, powiedz mi coś o getterach? nie słyszałem tego wcześniej. –

+2

Kwestii tej można było uniknąć w prosty sposób, jeśli używana jest wspólna praktyka programowania w Javie: zmienne muszą być prywatne (tylko klasa własna może z niego korzystać). Następnie, jak wspomniano powyżej, za pomocą metod pobierających i ustawiających w celu uzyskania dostępu/modyfikacji. – axcdnt

1

Ponieważ robisz f.a, otrzymasz wartość a z klasy Foo. jeśli wywołałeś metodę, aby uzyskać wartość, np. getA(), to uzyskałbyś wartość z klasy Bar.

11

Z takim pytaniem, egzamin SCJP ocenia Twoją wiedzę o tym, co jest znane pod nazwą , ukrywając numer. Egzaminator celowo komplikował rzeczy, aby spróbować uwierzyć, że zachowanie programu zależy tylko od polimorfizmu, którego nie ma.

Spróbujmy wyjaśnić sytuację, usuwając metodę addFive().

class Foo { 
    public int a = 3; 
} 

class Bar extends Foo { 
    public int a = 8; 
} 

public class TestClass { 
    public static void main(String[]args) { 
    Foo f = new Bar(); 
    System.out.println(f.a); 
    } 
} 

Teraz sprawy są nieco mniej zagmatwane.Metoda main deklaruje zmienną typu Foo, której przypisano obiekt typu Bar w czasie wykonywania. Jest to możliwe, ponieważ Bar dziedziczy po Foo. Następnie program odwołuje się do publicznego pola a zmiennej typu Foo.

Błąd polega na stwierdzeniu, że ten sam rodzaj pojęcia, znany jako przesłanianie, dotyczy pól klasy. Ale nie ma takiej koncepcji pola: pole publicznego a klasy Bar nie nadrzędnym jest pole publicznego a klasy Foo ale robi to co nazywa ukrywanie. Jak sama nazwa wskazuje, oznacza to, że w zakresie klasy Bar, a będzie odnosić się do własnego pola, które nie ma nic wspólnego z . (JLS 8.4.8 - Inheritance, Overriding, and Hiding)

Tak więc, kiedy piszemy f.a, do którego odnosimy się: a? Przypomnijmy, że rozdzielczość pola a jest wykonywana w czasie kompilacji przy użyciu typu deklaracji obiektu f, który jest Foo. W konsekwencji program wypisze "3".

Teraz dodajmy metodę addFive() do klasy Foo i zastąpmy ją klasą Bar, tak jak w pytaniu do egzaminu. Tutaj stosuje się polimorfizm, dlatego wywołanie f.addFive() jest rozwiązywane przy użyciu nie czasu kompilacji, lecz typu środowiska wykonawczego obiektu f, który jest Bar, a zatem jest drukowany "b".

Ale jest jeszcze coś, co musimy zrozumieć: dlaczego pole a, które zostało zwiększone o 5 jednostek, nadal trzyma się wartości "3"? Tutaj ukrywanie jest odtwarzane. Ponieważ jest to metoda klasy Bar, która jest wywoływana, a ponieważ w klasie Bar, każde a odnosi się do publicznego pola , to jest to faktycznie pole Bar, które jest inkrementowane.

1) Teraz jedna kwestia zależna: w jaki sposób możemy uzyskać dostęp do Bar „s pole publicznego a od sposobu main? Możemy zrobić coś podobnego:

System.out.println(((Bar)f).a); 

który zmusić kompilator rozwiązać członkiem dziedzinie a od f jako Bar „s a dziedzinie.

To mogłoby wydrukować "b 13" w naszym przykładzie.

2) Kolejna kwestia: jak moglibyśmy obejść ukrywanie w addFive() metody klasy Bar do odnoszą się nie do a polu należy Bar „s, ale jej nadklasy dziedzinie eponimous?Wystarczy dodanie słowa kluczowego super przed odniesieniem pola załatwia sprawę:

public void addFive() { 
    super.a += 5; 
    System.out.print("b "); 
} 

Byłoby to print „b” 8 w naszym przykładzie.

Uwaga że początkowe oświadczenie

public void addFive() { 
    this.a += 5; 
    System.out.print("b "); 
} 

może być doprecyzowana do

bo gdy kompilator jest rozwiązywanie pole a, zacznie szukać w najbliższym zakresu osłaniającego od obrębie metoda addFive() i znajdź instancję klasy Bar, co eliminuje konieczność użycia explicitely this.

Ale, cóż, this było prawdopodobnie wskazówką dla zdających, aby rozwiązać to pytanie egzaminacyjne!

+0

ładne wyjaśnienie Alex – Pratswinz