2016-09-16 15 views
30

Rozważmy:W jaki sposób zmienna 'this' w Javie faktycznie jest ustawiona na bieżący obiekt?

class TestParent{ 
    public int i = 100; 
    public void printName(){ 
    System.err.println(this); //{[email protected]} according to the Debugger. 
    System.err.println(this.i); //this.i is 100. 
    } 
} 

class TestChild extends TestParent{ 
    public int i = 200; 
} 

public class ThisTest { 
    public static void main(String[] args) { 
    new TestChild().printName(); 
    } 
} 

Wiem, że podobne pytania były zadawane, ale nie mogłem dostać solidne zrozumienie „tego” zmiennej w Javie.

Pozwól mi spróbować wyjaśnić, jak rozumiem wynik powyższego obrazu.

  1. Ponieważ jest to obiekt, który jest new TestChild() wywołanie metody printName() zmienna w linii 6 this jest ustawiony na TestChild obiektu - {TestChild @ 428} według debugera.

  2. Ponieważ Java nie ma pola wirtualnego - nie jestem do końca pewien, co to oznacza, ale rozumiem koncepcyjnie, że jest to przeciwieństwo metod Java, które obsługują Polimorfizm - this.i jest ustawiona na 100 TestParent w czasie kompilacji.

  3. Więc nieważne co this jest this.i w metodzie TestParent zawsze będzie zmienna w klasie TestParenti.

Nie jestem pewien, czy moje zrozumienie jest poprawne, więc proszę mnie poprawić, jeśli się mylę.

A także, moim głównym pytaniem jest,

Jak jest this zmienny zestaw do bieżącego obiektu, który jest wywołanie metody? W jaki sposób jest on faktycznie wdrażany?

+0

Jeśli nie, spróbuj dodać 'toString()' metoda (http://www.javapractices.com/topic/TopicAction.do?Id=55) do 'TestParent 'i zobacz, co' System.out.println (this); plony.' – c0der

+0

IMO' this' reprezentuje aktualną klasę, więc jeśli cokolwiek wiążemy do 'this', oznacza to, że wiążemy się z bieżącą' class', a do tego potrzebujemy 'variable/instance/member/method ...' aktualnej klasy. – emotionlessbananas

+4

Kevin Park, twoje zrozumienie w punktach od 1. do 3. jest poprawne. @AsteriskNinja, "ten" odwołuje się do obiektu, a nie do klasy (w wielu sytuacjach rozróżnienie jest nieistotne, w innych jest kluczowe). –

Odpowiedz

38

W istocie nie ma różnicy między

this.foo() 

i

anyObject.foo() 

jak zarówno są "realizowane" w ten sam sposób. Należy pamiętać, że „w końcu”, „orientacja obiektu jest tylko abstrakcji, aw«rzeczywistości», co się dzieje, jest coś takiego:

foo(callingObject) 

Innymi słowy: gdy używasz jakieś odniesienie do obiektu do nazywają metoda ... w końcu nie ma wywołania na jakiś obiekt.Ale głęboko w asembler i kod maszynowy, coś takiego jak "połączenie na coś" nie istnieje.Niepodobnie jak coś się dzieje, nie istnieje coś takiego jak "wywołanie czegoś"

wywołanie funkcji, a pierwszym (niejawnym/niewidocznym na poziomie kodu źródłowego) parametrem jest ten obiekt:

BTW: rzeczywiście można napisać, że w Javie jak:

class Bar { 
    void foo(Bar this) { ... } 

a później użyć

new Bar().foo(); 

I za to.fieldA, na końcu: masz odniesienie do jakiejś lokalizacji w pamięci; oraz tabelę z informacją, który "offset" znajdziesz w polu A.

Edytuj - tylko dla rekordu. Jeśli jesteś zainteresowany więcej informacji na temat foo (Bar to) - możesz przejść do tego question; podając szczegóły w specyfikacji Java za tym!

+9

Dokładnie. Niektóre języki sprawiają, że mechanizm ten jest bardziej wyraźny (powiedzmy Python, gdzie * masz *, aby określić argument "self" w metodach) – Bakuriu

+8

CO! Nie wiedziałem, że możesz napisać parametr "this" (chociaż wiedziałem, że był używany wewnętrznie z klasami wewnętrznymi). Ten przykład nadal nie jest legalny, chociaż brakuje w nim typu zwracanego (i treści metody). – Pokechu22

+2

@ Pokechu22 Dzięki za poinformowanie mnie; naprawiłem mój przykład. I tak, bardzo niewiele osób wie, że można zapisać takie metody. – GhostCat

1

Cóż, gdy tworzony jest nowy obiekt, ten obiekt ma adres w pamięci, dzięki czemu można go sobie wyobrazić tak, jakby obiekt miał prywatnego użytkownika, który jest ustawiony na adres podczas tworzenia obiektu. Możesz również pomyśleć o tym tak: obj.method(param) to tylko cukier syntaktyczny dla method(obj, param); i this jest w rzeczywistości parametrem method.

18

To, co się dzieje, to dwa zupełnie różne pola, nazywane i; aby używać ich pełnych nazw, jeden to TestParent::i, a drugi to TestChild::i.

Ponieważ metoda printName jest zdefiniowana w TestParent, gdy odnosi się do i, można go tylko zobaczyć TestParent::i, który jest ustawiony na 100.

co następuje po ustawieniu i do 200 w TestChild, oba pola są nazywane i widoczne, ale ponieważ mają one tę samą nazwę, a następnie kończysz ustawienie TestChild::i, pozostawiając nietknięte TestParent::i.

+5

To jest prawdziwa odpowiedź na to pytanie. Nie wiem, dlaczego inne odpowiedzi mówią o argumentach metodycznych. – Nayuki

+0

@Nayuki Ponieważ ta odpowiedź byłaby niepełna bez stwierdzenia, że ​​to słowo kluczowe jest zwykłym argumentem – Taemyr

0

Aby bezpośrednio zaadresować to, co widzisz na wyjściu: wezwanie do wydrukowania "this.i" przekazuje jako argument "print()" wartość pola "i" w bieżącym zakresie, który jest zakresem klasy macierzystej. Natomiast wezwanie do wydrukowania "tego" jest tłumaczone pod maską na wywołanie, aby wydrukować "this.getClass(). GetName()" [mówiąc w przybliżeniu], a wywołanie "getClass()" pobiera rzeczywisty obiekt klasy , który jest dla klasy dzieci.

0

Dodając więcej informacji na temat @Tom Anderson odpowiedź, co wyjaśnia ładnie ukrywanie koncepcji.

Dodałem jeszcze jednego konstruktora w katalogu Dziecko (TestChild), który drukuje wartości i w rodzicu i potomku.

Jeśli chcesz uzyskać wartość i od dziecka (TestChild), zastąp tę metodę w TestChild.

class TestParent{ 
    public int i = 100; 
    public void printName(){ 
    System.err.println("TestParent:printName()"); 
    System.err.println(this); //{[email protected]_NUM} according to the Debugger. 
    System.err.println(this.i); //this.i is 100. 
    } 
} 

class TestChild extends TestParent{ 
    public int i = 200; 
    public TestChild(){ 
    System.out.println("TestChild.i and TestParent.i:"+this.i+":"+super.i); 
    } 
    public void printName(){ 
     //super.printName(); 
     System.err.println("TestChild:printName()"); 
     System.err.println(this); //{[email protected]_NUM} according to the Debugger. 
     System.err.println(this.i); //this.i is 200. 
    } 
} 

public class ThisTest { 
    public static void main(String[] args) { 
    TestParent parent = new TestChild(); 
    parent.printName(); 
    } 
} 

Przypadek 1: Jeśli komentarz super.printName() rozmowę z dzieckiem, wersja dzieckiem TestChild.printName() wypisuje wartość I w TestChild

wyjściowa:

TestChild.i and TestParent.i:200:100 
TestChild:printName() 
[email protected] 
200 

Przypadek 2: TestChild.printName() wywołuje super.printName() jako pierwszą linię w metodzie printName(). W tym przypadku wartości z rodzica i podrzędnego są wyświetlane w odpowiednich metodach.

wyjściowa:

TestChild.i and TestParent.i:200:100 
TestParent:printName() 
[email protected] 
100 
TestChild:printName() 
[email protected] 
200 
Powiązane problemy