Referenzvariablen in Java
Aliaseffekte
In der Methode "main" der Klasse "Aliasing" wird eine Referenz auf einen Textspeicher einer Variablen "s" und gleichzeitig auch einer anderen Variablen "t" zugewiesen. Da beide Referenzen dasselbe Objekt referenzieren, läßt dieses sich dann in dem Gültigkeitsbereich dieser beiden Bezeichner mit jedem der beiden Bezeichner ansprechen. So wird das Objekt dann unter Verwendung des Bezeichners "t" verändert, aber unter Verwendung des Bezeichners "s" ausgelesen.
Main.java
public final class Main
{ public static void main( final java.lang.String[] args )
{ final java.lang.StringBuilder s = new java.lang.StringBuilder( "-" );
final java.lang.StringBuilder t = s;
t.setCharAt( 0, 'a' ); java.lang.System.out.print ( s );
t.setCharAt( 0, 'b' ); java.lang.System.out.println( s );
java.lang.System.out.println( s == t ); }}transcript
ab
true
Das Verwirrende daran kann es sein, daß man bei oberflächlicher Betrachtung vermeint, daß die Variable »s« zwischen den beiden Ausgaben nicht verändert wird und daher die beiden Ausgaben gleich sein müßten. Dieses Beispiel illustriert Anweisungen mit einer versteckten Wirkung, die beim Programmieren manchmal zu beobachten sind: Eine Veränderung eines Zustandes erfolgt durch eine Maßnahme, der man dies zunächst nicht ansieht. Hier hat die Anweisung »t.setCharAt( 0, 'b' );« die versteckte Wirkung der „Veränderung von s“. Die Wirkung ist versteckt, da der Name »s« in der Anweisung selber nicht vorkommt. Solche versteckten Wirkungen lassen sich nicht immer ganz vermeiden, aber sie können Ursachen für fehlerhafte Interpretationen von Programmen sein und damit zu Fehlern führen.
(Unter einem Zustand verstehen wir den zeitlich veränderlichen Teil einer Variablen oder eines Objektes. Eine Wirkung ist die Veränderung eines Zustandes.)
Solche Aliassituationen in einem Block wird man selten bewußt herstellen, weil sie die Situation nur unnötig verkomplizieren. Aliassituationen können aber unbeabsichtigt auftreten, etwa wenn ein Methode zwei Referenzen verarbeiten soll, und beispielsweise von dem einen referenzierten Objekt in das andere kopieren soll. Es könnte dabei aber auch einmal vorkommen, daß beide Referenzen dasselbe Objekt referenzieren. Die Methode sollte sich dann auch in diesem Fall so wie beabsichtigt (wie gewünscht) verhalten. Der Programmierer muß also stets auch an die Möglichkeit denken, daß zwei auf verschiedenen Wegen erhaltene Referenzen einander gleich sein könnten.
Die beiden Abbildungen zum Aliaseffekt machen die Situation noch einmal deutlich.
Situation nach "t.setCharAt( 0, 'a' )"
Quelltextmodell : Laufzeitmodell
:
: .---.
t =======================>| # -----------------------.
Bezeichner Bindung : '---' Referenz |
: Variable |
: __V__
: .-' '-.
: .' '.
: / \
: .---. ; ;
s =======================>| # ------------->| "a" |
Bezeichner Bindung : '---' Referenz ; ;
: Variable \ /
: '. .'
: '-._____.-'
: ObjektSituation nach "t.setCharAt( 0, 'b' )"
Quelltextmodell : Laufzeitmodell
:
: .---.
t =======================>| # -----------------------.
Bezeichner Bindung : '---' Referenz |
: Variable |
: __V__
: .-' '-.
: .' '.
: / \
: .---. ; ;
s =======================>| # ------------->| "b" |
Bezeichner Bindung : '---' Referenz ; ;
: Variable \ /
: '. .'
: '-._____.-'
: ObjektVariable eines elementaren Typs
Quelltextmodell : Laufzeitmodell
:
:
:
: .---.
i ==============================>| 2 |
Bezeichner Bindung : '---'
: Variable
: mit Wert
:
:
:Variable eines Referenztyps
Quelltextmodell : Laufzeitmodell _____
: .-' '-.
: .' '.
: / \
: .---. ; ;
s =======================>| # ------------->| |
Bezeichner Bindung : '---' Referenz ; ;
: Variable \ /
: '. .'
: '-._____.-'
: Objekt
:
Referenzeffekt: Aliaseffekte
Die folgende Beispiele zeigen das oben am Beispiel von »StringBuilder« gezeigte noch einmal am Beispiel der Klasse »java.awt.geom.Point2D.Double«.
Main.java
public class Main
{ public static void main( final java.lang.String[] args )
{ final java.awt.geom.Point2D.Double o0
= new java.awt.geom.Point2D.Double();final java.awt.geom.Point2D.Double o1
= new java.awt.geom.Point2D.Double();o0.x = 1; /* Diese Nachricht interpretieren wir als ein Kommando */
o0.y = 2;
o1.x = 3;
o1.y = 4;java.lang.System.out.println( o0.x ); /* Diese Nachricht interpretieren wir als eine Frage */
java.lang.System.out.println( o0.y );
java.lang.System.out.println( o1.x );
java.lang.System.out.println( o1.y );java.lang.System.out.println( o0 == o1 ); }}
transcript
1.0
2.0
3.0
4.0
false- Abbildung
Das erste Objekt
o0 (Variable) .---------------.
.--------------. | x: 1 |
| Referenz ---------------------------->| y: 2 |
'--------------' '---------------'
Das zweite Objekt
o1 (Variable) .---------------.
.--------------. | x: 3 |
| Referenz ---------------------------->| y: 4 |
'--------------' '---------------'Main.java
public class Main
{ public static void main( final java.lang.String[] args )
{ final java.awt.geom.Point2D.Double o0
= new java.awt.geom.Point2D.Double();final java.awt.geom.Point2D.Double o1
= o0;o0.x = 1;
o0.y = 2;
o1.x = 3;
o1.y = 4;java.lang.System.out.println( o0.x );
java.lang.System.out.println( o0.y );
java.lang.System.out.println( o1.x );
java.lang.System.out.println( o1.y );
java.lang.System.out.println( o0 == o1 ); }}transcript
3.0
4.0
3.0
4.0
true- Abbildung
o0 (Variable)
.--------------.
| Referenz ------------. Das Objekt
'--------------' | .-------------.
| | |
'---------------->| x: 3 |
.---------------->| y: 4 |
o1 (Variable) | | |
.--------------. | '-------------'
| Referenz ------------'
'--------------'
Man kann sagen, daß o1 ein Alias für o0 ist.
Obwohl wir eine Referenz auf eine Objekt in Variablen abspeichern können, welche jeweils auch einen Namen haben können, hat das Objekt selber keinen eigenen Namen, Objekte sind anonym.
Objekte haben im allgemeinen keine Namen.
- ? Ausgabe vorhersagen
- Welche Ausgabe erzeugt das folgende Programm?
Main.java
public final class Main
{public static void main( final java.lang.String[] args )
{ final java.awt.geom.Point2D.Double a = new java.awt.geom.Point2D.Double();
final java.awt.geom.Point2D.Double b = a;
a.x = 1.0;
a.y = 4.0;
b.x = 2.0;
java.lang.System.out.println( a.x );
java.lang.System.out.println( b.y ); }}
Änderungen von Objekten
In der Methode "main" der Klasse "Main" wird durch zweifache Ausgabe des lokalen Bezeichners "s" Text »ab« ausgegeben. Die Variable »s« enthält die ganze Zeit lang dieselbe Referenz, verweist also auf dasselbe Objekt. Zwischen den beiden Ausgabeoperationen wird hier nicht der Inhalt der Variablen »s«, sondern der Zustand des Objektes verändert, der von diesem Inhalte referenziert wird.
Main.java
public class Main
{ public static void main( final java.lang.String[] args )
{ final java.lang.StringBuilder s = new java.lang.StringBuilder( "-" );
s.setCharAt( 0, 'a' ); java.lang.System.out.print ( s );
s.setCharAt( 0, 'b' ); java.lang.System.out.println( s ); }}System.out
ab
Die Abbildungen stellen die Situation bei den beiden Ausgabeoperationen bildlich dar. Man erkennt, daß zwar der Zustand des von der Referenz in der Variablen referenzierten Objekts verändert wird, aber sich die Variable selber nicht verändert. Sie zeigt immer auf dasselbe Objekt. Daher konnte die Variable in diesem Programm sogar mit dem Variablenmodifizierer »final« deklariert werden. (Man vergleiche diese Situation mit der Situation, die in dem vorigen Abschnitt „Änderungen von Variablen “ beschrieben wurde.)
Situation nach "s.setCharAt( 0, 'a' )"
Quelltextmodell : Laufzeitmodell _____
: .-' '-.
: .' '.
: / \
: .---. ; ;
s =======================>| # ------------->| "a" |
Bezeichner Bindung : '---' Referenz ; ;
: Variable \ /
: '. .'
: '-._____.-'
: Objekt
:Situation nach "s.setCharAt( 0, 'b' )"
Quelltextmodell : Laufzeitmodell _____
: .-' '-.
: .' '.
: / \
: .---. ; ;
s =======================>| # ------------->| "b" |
Bezeichner Bindung : '---' Referenz ; ;
: Variable \ /
: '. .'
: '-._____.-'
: Objekt
:
Referenzvariablen und Referenzwerte (Referenzen)
Referenzvariablen und Referenzwerte werden oft nicht deutlich unterschieden, indem beide einfach nur als „Referenzen“ bezeichnet werden. Eine „Referenz“ ist aber eigentlich ein Referenzwert und keine Referenzvariable.
Ein Referenzwert —oder, kurz, eine Referenz —ist eine Angabe, die es ermöglicht, ein bestimmtes Objekt zu erreichen (oder es ist die Nullreferenz).
Eine Referenzvariable ist eine Speicher, der zu einem Zeitpunkt genau einen Referenzwert enthalten kann, aber im Laufe der Zeit kann sich der enthaltene Referenzwert auch verändern.
Den Unterschied zwischen Referenzvariablen und Referenzwerten kann man jetzt verstehen, wenn man daran denkt, daß im Abschnitt „Änderungen von Variablen “ dieselbe Referenzvariable zwei verschiedene Referenzwerte enthielt (zu zwei verschiedenen Zeitpunkten). Umgekehrt findet man in dem letzten Abschnitt „Aliaseffekte “ die Situation dargestellt, daß zwei verschiedene Referenzvariablen denselben Referenzwert enthalten. (Zum Verständnis dieses Unterschiedes betrachte man noch einmal die Schaubilder dieser beiden Abschnitte.)
Werte ⁺
Wir verstehen unter einem Wert eine Zahl, eine Zeichenfolge, einen Wahrheitswert oder eine Referenz (statt bisher: ein Objekt).