Frage zum "Referenzen-konzept" in Java

Zrebna

Bekanntes Mitglied
Hi!

ich weiß, dass es in Java keine so "vor den Kullissen" Zeiger gibt, wie in C/C++, und dass die Referenzvariablen auch nicht wirklich
auf Speicheradressen von, im Speicher existierenden Objekten, zeigen, sondern auf deren Object-IDs -> effektiv ist das Endergebnis aber das selbe und es Objektvariablen zeigen quasi auf das objekt und können dessen Inhalte nutzen.

Dazu habe ich ein Bild gefunden, was ich hier hochlade und meiner Meinung nach ist es aber an den rot-markierten Stellen falsch?
Effektiv ist ja die Reeferenzvariable das, was in C ein Doppelpointer ist, d.h. ein Zeiger auf einen Zeiger bzw. eine Referenz auf eine Referenz - ist das korrekt?
Denn hier zeigt ja 'wagen2' auch direkt auf das Objekt, was eben nicht richtig ist?
Ist mein Korrekturvorschlag bzw. meine Überlegung bzgl. dem Bild richtig oder falsch?

Kann Jemand für Aufklärung sorgen? ^^

Lg
Zrebna
 

Anhänge

  • Referenzen_Java.PNG
    Referenzen_Java.PNG
    177,3 KB · Aufrufe: 54
Effektiv ist ja die Reeferenzvariable das, was in C ein Doppelpointer ist, d.h. ein Zeiger auf einen Zeiger bzw. eine Referenz auf eine Referenz - ist das korrekt?
Nein. Es gibt hier (was die Java Language Specification angeht) keine Indirektionsstufen. Und tatsächlich gibt es diese Indirektion in keiner JVM Implementierung, die ich kenne. Dort sind sogenannte "Ordinary Object Pointer" (OOP) wirklich die virtuellen Speicheradressen der Objekte (plus ein paar Optimierungen, wie Adress-Ausrichtung an 8-Bytegrenzen, so dass man Adressen mit weniger Bits repräsentieren kann - Stichwort "Compressed OOPs"). Nur, kommt man (ausser mit der Hilfe von Arrays und sun.misc.Unsafe Magie) nicht an diese Adresse heran. Jedesmal eine Indirektion bei jeder Dereferenzierung von Objekten durchführen zu müssen, wäre auch viel zu teuer und wird deshalb auch nicht gemacht.
Das einzige, was man hier sieht, was wie eine Adresse "aussieht", ist der Identity-Hashcode des Objektes. Und dieser ist tatsächlich zumindest in HotSpot auch der Ordinary Object Pointer (auf 32-bit abgeschnitten), nur eben wird der Wert dieser Adresse zum Zwecke der "Konstantheit" des hashCode() im Objekt selbst nochmal gespeichert. Wenn das Objekt durch GC also relocated wird, dann verändert sich der HashCode nicht, wenn er vorher schon einmal durch hashCode() abgefragt wurde. Naja, das nur dazu.
Denn hier zeigt ja 'wagen2' auch direkt auf das Objekt, was eben nicht richtig ist?
Doch, ist richtig.
Ist mein Korrekturvorschlag bzw. meine Überlegung bzgl. dem Bild richtig oder falsch?
Ist falsch.
Kann Jemand für Aufklärung sorgen? ^^
Ja. 🙂
Also: Wie du schon richtig gesagt hast, gibt es in Java keine reinen "Zeiger", die man wie in C/C++ als Ganzzahlen interpretieren könnte und sogar Zeigerarithmetik auf ihnen betreiben kann. Das ist in Java nicht möglich. Objekte haben nur eine Identität und keine sichtbaren "Speicheradressen", mit denen man dann (wie gesagt) Zeigerarithmetik betreiben könnte.
Und, wenn du eine Variable `wagen2` auf eine andere Variable `wagen1` zuweist, dann zeigt nicht etwa `wagen2` auf die "Variable" `wagen1`, sondern `wagen2` zeigt auf dasselbe Objekt wie `wagen1` zum Zeitpunkt der Zuweisung. Das heisst also, der Wert der Variablen `wagen2` ist eine Referenz auf dasselbe Objekt wie bei `wagen1`. Implementierungstechnisch enthalten dann in HotSpot beide Variablen die virtuelle Speicheradresse von dem einen Objekt.
 
Das Bild ist richtig.
Was passiert denn, wenn man den Code ergänzt um die Zeile
Java:
wagen1 = new Kraftwagen("0-4");
welchen Inhalt hat danach wagen2?
Dann zeigt wagen1 nicht mehr auf das Kraftwagen("0-1")-Objekt, sondern auf das
neu erzeugte Kraftwagen("0-4")-Objekt?
Und hat dann foglich als Referenz-ID/Kennung nicht mehr 1008, sondern die Kennung die das neue Objekt hat?

@httpdigest:
Danke für deinen ausführlichen Post - ich lese ihn mir morgen in Ruhe durch, da ich für Heute weg muss, aber noch kurz eben dem Vorposter antworten wollte...

Lg,
Zrebna
 
Dann zeigt wagen1 nicht mehr auf das Kraftwagen("0-1")-Objekt
Meine Frage war aber, welchen Inhalt danach wagen2 (nicht wagen1) hat. Nach deinen rot gemalten Pfeilen würde sich dies ja auch ändern.
Probiere doch
Java:
class Kraftwagen {
    String text;
    public Kraftwagen(String text) {
        this.text = text;
    }
    public String toString() {
        return text;
    }
}

public class Test {
    public static void main(String[] args) {
        Kraftwagen wagen1 = new Kraftwagen("0-1");
        Kraftwagen wagen2 = wagen1;
        System.out.println(wagen1);
        System.out.println(wagen2);
        wagen1 = new Kraftwagen("0-4");
        System.out.println(wagen1);
        System.out.println(wagen2);
    }
}
 
Nein. Es gibt hier (was die Java Language Specification angeht) keine Indirektionsstufen. Und tatsächlich gibt es diese Indirektion in keiner JVM Implementierung, die ich kenne. Dort sind sogenannte "Ordinary Object Pointer" (OOP) wirklich die virtuellen Speicheradressen der Objekte (plus ein paar Optimierungen, wie Adress-Ausrichtung an 8-Bytegrenzen, so dass man Adressen mit weniger Bits repräsentieren kann - Stichwort "Compressed OOPs"). Nur, kommt man (ausser mit der Hilfe von Arrays und sun.misc.Unsafe Magie) nicht an diese Adresse heran. Jedesmal eine Indirektion bei jeder Dereferenzierung von Objekten durchführen zu müssen, wäre auch viel zu teuer und wird deshalb auch nicht gemacht.
Das einzige, was man hier sieht, was wie eine Adresse "aussieht", ist der Identity-Hashcode des Objektes. Und dieser ist tatsächlich zumindest in HotSpot auch der Ordinary Object Pointer (auf 32-bit abgeschnitten), nur eben wird der Wert dieser Adresse zum Zwecke der "Konstantheit" des hashCode() im Objekt selbst nochmal gespeichert. Wenn das Objekt durch GC also relocated wird, dann verändert sich der HashCode nicht, wenn er vorher schon einmal durch hashCode() abgefragt wurde. Naja, das nur dazu.

Doch, ist richtig.

Ist falsch.

Ja. 🙂
Also: Wie du schon richtig gesagt hast, gibt es in Java keine reinen "Zeiger", die man wie in C/C++ als Ganzzahlen interpretieren könnte und sogar Zeigerarithmetik auf ihnen betreiben kann. Das ist in Java nicht möglich. Objekte haben nur eine Identität und keine sichtbaren "Speicheradressen", mit denen man dann (wie gesagt) Zeigerarithmetik betreiben könnte.
Und, wenn du eine Variable `wagen2` auf eine andere Variable `wagen1` zuweist, dann zeigt nicht etwa `wagen2` auf die "Variable" `wagen1`, sondern `wagen2` zeigt auf dasselbe Objekt wie `wagen1` zum Zeitpunkt der Zuweisung. Das heisst also, der Wert der Variablen `wagen2` ist eine Referenz auf dasselbe Objekt wie bei `wagen1`. Implementierungstechnisch enthalten dann in HotSpot beide Variablen die virtuelle Speicheradresse von dem einen Objekt.
Ah, ok - denke ich verstehe es nun - vielen Dank!
Das heißt, dass quasi eine Referenzvariable, die auf eine andere Referenzvariable zugewiesen wird, zeigt einfach auf das selbe Objekt,
auf was die zugewiesene Referenzvariable gegenwärtig zeigt...
Ok, also dnan ist klar, dass es keine Art Doppelzeiger geben kann.
 
Meine Frage war aber, welchen Inhalt danach wagen2 (nicht wagen1) hat. Nach deinen rot gemalten Pfeilen würde sich dies ja auch ändern.
Probiere doch
Java:
class Kraftwagen {
    String text;
    public Kraftwagen(String text) {
        this.text = text;
    }
    public String toString() {
        return text;
    }
}

public class Test {
    public static void main(String[] args) {
        Kraftwagen wagen1 = new Kraftwagen("0-1");
        Kraftwagen wagen2 = wagen1;
        System.out.println(wagen1);
        System.out.println(wagen2);
        wagen1 = new Kraftwagen("0-4");
        System.out.println(wagen1);
        System.out.println(wagen2);
    }
}
Mit dem oben zitiertem Post von httpdigest, würde ich eben nun sagen, dass wagen2 als Wert immer noch 1008 hat, also immer noch auf das "0-1"-KFZ-objekt zeigt, auf was vorher wage1 gezeigt hat und wagen1 aber nun auf das neu erzeugte Objekt

Test mit deinem Quellcode hat es auch bestätigt - Danke hierfür!🙂

Lg
Zrebna
 

Zurück
Oben