Rheinwerk Computing < openbook > Rheinwerk Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

Inhaltsverzeichnis
Vorwort
1 Java ist auch eine Sprache
2 Imperative Sprachkonzepte
3 Klassen und Objekte
4 Der Umgang mit Zeichenketten
5 Eigene Klassen schreiben
6 Exceptions
7 Äußere.innere Klassen
8 Besondere Klassen der Java SE
9 Generics<T>
10 Architektur, Design und angewandte Objektorientierung
11 Die Klassenbibliothek
12 Einführung in die nebenläufige Programmierung
13 Einführung in Datenstrukturen und Algorithmen
14 Einführung in grafische Oberflächen
15 Einführung in Dateien und Datenströme
16 Einführung in die <XML>-Verarbeitung mit Java
17 Einführung ins Datenbankmanagement mit JDBC
18 Bits und Bytes und Mathematisches
19 Die Werkzeuge des JDK
A Die Klassenbibliothek
Stichwort

Download:
- Aufgaben, ca. 1,1 MB
- Programme, ca. 12,8 MB

Buch bestellen
Ihre Meinung?

Spacer
Java ist auch eine Insel von Christian Ullenboom
Das umfassende Handbuch
Buch: Java ist auch eine Insel

Java ist auch eine Insel
Galileo Computing
1308 S., 10., aktualisierte Auflage, geb., mit DVD
ca. 49,90 Euro, ISBN 978-3-8362-1802-3
Pfeil 3 Klassen und Objekte
Pfeil 3.1 Objektorientierte Programmierung (OOP)
Pfeil 3.1.1 Warum überhaupt OOP?
Pfeil 3.1.2 Denk ich an Java, denk ich an Wiederverwendbarkeit
Pfeil 3.2 Eigenschaften einer Klasse
Pfeil 3.2.1 Die Klasse Point
Pfeil 3.3 Die UML (Unified Modeling Language) *
Pfeil 3.3.1 Hintergrund und Geschichte der UML
Pfeil 3.3.2 Wichtige Diagrammtypen der UML
Pfeil 3.3.3 UML-Werkzeuge
Pfeil 3.4 Neue Objekte erzeugen
Pfeil 3.4.1 Ein Exemplar einer Klasse mit dem new-Operator anlegen
Pfeil 3.4.2 Garbage-Collector (GC) – Es ist dann mal weg
Pfeil 3.4.3 Deklarieren von Referenzvariablen
Pfeil 3.4.4 Zugriff auf Objektattribute und -methoden mit dem ».«
Pfeil 3.4.5 Überblick über Point-Methoden
Pfeil 3.4.6 Konstruktoren nutzen
Pfeil 3.5 ZZZZZnake
Pfeil 3.6 Kompilationseinheiten, Imports und Pakete schnüren
Pfeil 3.6.1 Volle Qualifizierung und import-Deklaration
Pfeil 3.6.2 Mit import p1.p2.* alle Typen eines Pakets erreichen
Pfeil 3.6.3 Hierarchische Strukturen über Pakete
Pfeil 3.6.4 Die package-Deklaration
Pfeil 3.6.5 Unbenanntes Paket (default package)
Pfeil 3.6.6 Klassen mit gleichen Namen in unterschiedlichen Paketen *
Pfeil 3.6.7 Compilationseinheit (Compilation Unit)
Pfeil 3.6.8 Statischer Import *
Pfeil 3.6.9 Eine Verzeichnisstruktur für eigene Projekte *
Pfeil 3.7 Mit Referenzen arbeiten, Identität und Gleichheit
Pfeil 3.7.1 Die null-Referenz
Pfeil 3.7.2 null-Referenzen testen
Pfeil 3.7.3 Zuweisungen bei Referenzen
Pfeil 3.7.4 Methoden mit nicht-primitiven Parametern
Pfeil 3.7.5 Identität von Objekten
Pfeil 3.7.6 Gleichheit und die Methode equals()
Pfeil 3.8 Arrays
Pfeil 3.8.1 Grundbestandteile
Pfeil 3.8.2 Deklaration von Arrays
Pfeil 3.8.3 Arrays mit Inhalt
Pfeil 3.8.4 Die Länge eines Arrays über das Attribut length auslesen
Pfeil 3.8.5 Zugriff auf die Elemente über den Index
Pfeil 3.8.6 Array-Objekte mit new erzeugen
Pfeil 3.8.7 Typische Feldfehler
Pfeil 3.8.8 Feld-Objekte als Parametertyp
Pfeil 3.8.9 Vorinitialisierte Arrays
Pfeil 3.8.10 Die erweiterte for-Schleife
Pfeil 3.8.11 Arrays mit nicht-primitiven Elementen
Pfeil 3.8.12 Mehrdimensionale Arrays *
Pfeil 3.8.13 Nichtrechteckige Arrays *
Pfeil 3.8.14 Die Wahrheit über die Array-Initialisierung *
Pfeil 3.8.15 Mehrere Rückgabewerte *
Pfeil 3.8.16 Methode mit variabler Argumentanzahl (Vararg)
Pfeil 3.8.17 Klonen kann sich lohnen – Arrays vermehren *
Pfeil 3.8.18 Feldinhalte kopieren *
Pfeil 3.8.19 Die Klasse Arrays zum Vergleichen, Füllen, Suchen, Sortieren nutzen
Pfeil 3.8.20 Eine lange Schlange
Pfeil 3.9 Der Einstiegspunkt für das Laufzeitsystem: main()
Pfeil 3.9.1 Korrekte Deklaration der Startmethode
Pfeil 3.9.2 Kommandozeilenargumente verarbeiten
Pfeil 3.9.3 Der Rückgabetyp von main() und System.exit() *
Pfeil 3.10 Annotationen und Generics
Pfeil 3.10.1 Generics
Pfeil 3.10.2 Annotationen
Pfeil 3.10.3 Eigene Metadaten setzen
Pfeil 3.10.4 Annotationstypen @Override, @Deprecated, @SuppressWarnings
Pfeil 3.11 Zum Weiterlesen

Rheinwerk Computing - Zum Seitenanfang

3.7 Mit Referenzen arbeiten, Identität und GleichheitZur nächsten Überschrift

In Java gibt es mit null eine sehr spezielle Referenz, die Auslöser vieler Probleme ist. Doch ohne sie geht es nicht, und warum das so ist, wird der folgende Abschnitt zeigen. Anschließend wollen wir sehen, wie Objektvergleiche funktionieren und was der Unterschied zwischen Identität und Gleichheit ist.


Rheinwerk Computing - Zum Seitenanfang

3.7.1 Die null-ReferenzZur nächsten ÜberschriftZur vorigen Überschrift

In Java gibt es drei spezielle Referenzen: null, this und super. (Wir verschieben this und super auf Kapitel 5, »Eigene Klassen schreiben«.) Das spezielle Literal null lässt sich zur Initialisierung von Referenzvariablen verwenden. Die null-Referenz ist typenlos, kann also jeder Referenzvariablen zugewiesen und jeder Methode übergeben werden, die ein Objekt erwartet.[100](null verhält sich also so, als ob es ein Untertyp jedes anderen Typs wäre.)

Beispiel

Deklaration und Initialisierung zweier Objektvariablen mit null:

Point  p = null;
String s = null;
System.out.println( p ); // null
Die Konsolenausgabe über die letzte Zeile liefert kurz »null«.

Da es nur ein null gibt, ist zum Beispiel (Point) null == (String) null. Der Wert ist ausschließlich für Referenzen vorgesehen und kann in keinen primitiven Typ wie die Ganzzahl 0 umgewandelt werden.[101](Hier unterscheiden sich C(++) und Java.)

Mit null lässt sich eine ganze Menge machen. Der Haupteinsatzzweck sieht vor, damit uninitialisierte Referenzvariablen zu kennzeichnen, also auszudrücken, dass eine Referenzvariable auf kein Objekt verweist. In Listen oder Bäumen kennzeichnet null zum Beispiel das Fehlen eines gültigen Nachfolgers; null ist dann ein gültiger Indikator und kein Fehlerfall.

Auf null geht nix, nur die NullPointerException

Da sich hinter null kein Objekt verbirgt, ist es auch nicht möglich, eine Methode aufzurufen oder von der null ein Attribut zu erfragen. Der Compiler kennt zwar den Typ jedes Objekts, aber erst die Laufzeitumgebung (JVM) weiß, was referenziert wird. Wird versucht, über die null-Referenz auf eine Eigenschaft eines Objekts zuzugreifen, löst eine JVM eine NullPointerException[102](Der Name zeigt das Überbleibsel von Zeigern. Zwar haben wir es in Java nicht mit Zeigern zu tun, sondern mit Referenzen, doch heißt es NullPointerException und nicht NullReferenceException. Das erinnert daran, dass eine Referenz ein Objekt identifiziert und eine Referenz auf ein Objekt ein Pointer ist. Das .NET Framework ist hier konsequenter und nennt die Ausnahme NullReferenceException.) aus:

Listing 3.12: NullPointer.java

/*  1 */public class NullPointer
/* 2 */{
/* 3 */ public static void main( String[] args )
/* 4 */ {
/* 5 */ java.awt.Point p = null;
/* 6 */ String s = null;
/* 7 */
/* 8 */ p.setLocation( 1, 2 );
/* 9 */ s.length();
/* 10 */ }
/* 11 */}

Wir beobachten eine NullPointerException, denn das Programm bricht bei p.setLocation() mit folgender Ausgabe ab:

java.lang.NullPointerException
at NullPointer.main(NullPointer.java:8)
Exception in thread "main"

Die Laufzeitumgebung teilt uns in der Fehlermeldung mit, dass sich der Fehler, die NullPointerException, in Zeile 8 befindet.


Rheinwerk Computing - Zum Seitenanfang

3.7.2 null-Referenzen testenZur nächsten ÜberschriftZur vorigen Überschrift

Mit dem Vergleichsoperator == oder dem Test auf Ungleichheit mit != lässt sich leicht herausfinden, ob eine Referenzvariable wirklich ein Objekt referenziert oder nicht:

if ( object == null )
// Variable referenziert nichts, ist aber gültig mit null initialisiert
else
// Variable referenziert ein Objekt

null-Test und Kurzschluss-Operatoren

Wir wollen an dieser Stelle noch einmal auf die üblichen logischen Kurzschluss-Operatoren und den logischen, nicht kurzschließenden Operator zu sprechen kommen. Erstere werten Operanden nur so lange von links nach rechts aus, bis das Ergebnis der Operation feststeht. Auf den ersten Blick scheint es nicht viel auszumachen, ob alle Teilausdrücke ausgewertet werden oder nicht, in einigen Ausdrücken ist dies aber wichtig, wie das folgende Beispiel für die Variable s vom Typ String zeigt:

Listing 3.13: NullCheck.java

public class NullCheck
{
public static void main( String[] args )
{
String s = javax.swing.JOptionPane.showInputDialog( "Eingabe" );
if ( s != null && ! s.isEmpty() )
System.out.println( "Eingabe: " + s );
else
System.out.println( "Abbruch oder keine Eingabe" );
}
}

Die Rückgabe von showInputDialog() ist null, wenn der Benutzer den Dialog abbricht. Das soll unser Programm berücksichtigen. Daher testet die if-Bedingung, ob s überhaupt auf ein Objekt verweist, und prüft gleichzeitig, ob die Länge größer 0 ist. Dann folgt eine Ausgabe.

Diese Schreibweise tritt häufig auf, und der Und-Operator zur Verknüpfung muss ein Kurzschluss-Operator sein, da es in diesem Fall ausdrücklich darauf ankommt, dass die Länge nur dann bestimmt wird, wenn die Variable s überhaupt auf ein String-Objekt verweist und nicht null ist. Andernfalls bekämen wir bei s.isEmpty() eine NullPointerException, wenn jeder Teilausdruck ausgewertet würde und s gleich null wäre.

»null« in anderen Programmiersprachen

Ist Java eine pure objektorientiere Programmiersprache? Nein, da Java einen Unterschied zwischen primitiven Typen und Referenztypen macht. Nehmen wir für einen Moment an, dass es primitive Typen nicht gibt. Wäre Java dann eine reine objektorientierte Programmiersprache, bei der jede Referenz ein pures Objekt referenziert? Die Antwort ist immer noch nein, da es mit null etwas gibt, mit dem Referenzvariablen initialisiert werden können, aber was kein Objekt repräsentiert und keine Methoden besitzt. Und das kann bei der Dereferenzierung eine NullPointerException geben. Andere Programmiersprachen haben andere Lösungsansätze, und null-Referenzierungen sind nicht möglich. In der Sprache Ruby zum Beispiel ist immer alles ein Objekt. Wo Java mit null ein »nicht belegt« ausdrückt, macht das Ruby mit nil. Der feine Unterschied ist, dass nil ein Exemplar der Klasse NilClass ist, genau genommen ein Singleton, was es im System nur einmal gibt. nil hat auch ein paar öffentliche Methoden wie to_s (wie Javas toString()), was dann einen leeren String liefert. Mit nil gibt es keine NullPointerException mehr, aber natürlich immer noch einen Fehler, wenn auf diesem Objekt vom Typ NilClass eine Methode aufgerufen wird, die es nicht gibt. In Objective-C, der Standardsprache für iPhone-Programme, gibt es das Null-Objekt nil. Üblicherweise passiert nichts, wenn eine Nachricht an das nil-Objekt gesendet wird; die Nachricht wird einfach ignoriert.[103](Es gibt auch Compiler wie den GCC, der mit der Option -fno-nil-receivers dieses Verhalten abschaltet, um schnelleren Maschinencode zu erzeugen. Denn letztendlich muss in Maschinencode immer ein Test stehen, der auf 0 prüft.)


Rheinwerk Computing - Zum Seitenanfang

3.7.3 Zuweisungen bei ReferenzenZur nächsten ÜberschriftZur vorigen Überschrift

Eine Referenz erlaubt den Zugriff auf das referenzierte Objekt. Es kann durchaus mehrere Kopien dieser Referenz geben, die in Variablen mit unterschiedlichen Namen abgelegt sind – so wie eine Person von den Mitarbeitern als »Chefin« angesprochen wird, aber von ihrem Mann als »Schnuckiputzi«. Dies nennt sich auch Alias.

Beispiel

Ein Punkt-Objekt wollen wir unter einem alternativen Variablennamen ansprechen:

Point p = new Point();
Point q = p;
Ein Punkt-Objekt wird erzeugt und mit der Variablen p referenziert. Die zweite Zeile speichert nun dieselbe Referenz in der Variablen q. Danach verweisen p und q auf dasselbe Objekt.

Abbildung

Verweisen zwei Objektvariablen auf das gleiche Objekt, hat das natürlich zur Konsequenz, dass über zwei Wege Objektzustände ausgelesen und modifiziert werden können. Heißt die gleiche Person in der Firma »Chefin« und zu Hause »Schnuckiputzi«, wird der Mann sich freuen, wenn die Frau in der Firma keinen Stress hat.

Wir können das Beispiel auch gut bei Punkt-Objekten nachverfolgen. Zeigen p und q auf dasselbe Punkt-Objekt, können Änderungen über p auch über die Variable q beobachtet werden:

Listing 3.14: ItsTheSame.java

import java.awt.Point;

public class ItsTheSame
{
public static void main( String[] args )
{
Point p = new Point();
Point q = p;
p.x = 10;
System.out.println( q.x ); // 10
q.y = 5;
System.out.println( p.y ); // 5
}
}

Rheinwerk Computing - Zum Seitenanfang

3.7.4 Methoden mit Referenztypen als ParameternZur nächsten ÜberschriftZur vorigen Überschrift

Dass sich das gleiche Objekt unter zwei Namen (über zwei verschiedene Variablen) ansprechen lässt, können wir gut bei Methoden beobachten. Eine Methode, die über den Parameter eine Objektreferenz erhält, kann auf das übergebene Objekt zugreifen. Das bedeutet, die Methode kann dieses Objekt mit den angebotenen Methoden ändern oder auf die Attribute zugreifen.

Im folgenden Beispiel deklarieren wir zwei Methoden. Die erste Methode, initializeToken(Point), soll einen Punkt mit Zufallskoordinaten initialisieren. Übergeben werden wir dann zwei Point-Objekte: einmal für einen Spieler und einmal für eine Schlange. Die zweite Methode, printScreen(Point, Point), gibt das Spielfeld auf dem Bildschirm aus, und gibt dann, wenn die Koordinate einen Spieler trifft, ein »&« aus und bei der Schlange ein »S«.

Listing 3.15: DrawPlayerAndSnake.java

import java.awt.Point;

public class DrawPlayerAndSnake
{
static void initializeToken( Point p )
{
int randomX = (int)(Math.random() * 40); // 0 <= x < 40
int randomY = (int)(Math.random() * 10); // 0 <= y < 10
p.setLocation( randomX, randomY );
}

static void printScreen( Point playerPosition,
Point snakePosition )
{
for ( int y = 0; y < 10; y++ )
{
for ( int x = 0; x < 40; x++ )
{
if ( playerPosition.distanceSq( x, y ) == 0 )
System.out.print( '&' );
else if ( snakePosition.distanceSq( x, y ) == 0 )
System.out.print( 'S' );
else System.out.print( '.' );
}
System.out.println();
}
}

public static void main( String[] args )
{
Point playerPosition = new Point();
Point snakePosition = new Point();
System.out.println( playerPosition );
System.out.println( snakePosition );
initializeToken( playerPosition );
initializeToken( snakePosition );
System.out.println( playerPosition );
System.out.println( snakePosition );
printScreen( playerPosition, snakePosition );
}
}

Die Ausgabe kann so aussehen:

java.awt.Point[x=0,y=0]
java.awt.Point[x=0,y=0]
java.awt.Point[x=38,y=1]
java.awt.Point[x=19,y=8]
........................................
......................................&.
........................................
........................................
........................................
........................................
........................................
........................................
...................S....................
........................................

In dem Moment, in dem main() die statische Methode initializeToken() aufruft, gibt es sozusagen zwei Namen für das Point-Objekt: playerPosition und p. Allerdings ist das nur innerhalb der virtuellen Maschine so, denn initializeToken() kennt das Objekt nur unter unter p, aber kennt die Variable playerPosition nicht. Bei main() ist es umgekehrt: Nur der Variablenname playerPosition ist in main() bekannt, er hat aber vom Namen p keine Ahnung.

Hinweis

Der Name einer Parametervariablen darf durchaus mit dem Namen der Argument-Variablen übereinstimmen, was die Semantik nicht verändert. Die Namensräume sind völlig getrennt, und Missverständnisse gibt es nicht, da beide – die aufrufende Methode und die aufgerufene Methode – komplett getrennte lokale Variablen haben.

Wertübergabe und Referenzübergabe per »Call by Value«

Primitive Variablen werden immer per Wert kopiert (engl. Call by Value). Das Gleiche gilt für Referenzen. Daher hat auch die folgende statische Methode keine Nebenwirkungen:

Listing 3.16: JavaIsAlwaysCallByValue.java

import java.awt.Point;

public class JavaIsAlwaysCallByValue
{
static void clear( Point p )
{
p = new Point();
}

public static void main( String[] args )
{
Point p = new Point( 10, 20 );
clear( p );
System.out.println( p ); // java.awt.Point[x=10,y=20]
}
}

Nach der Zuweisung in der clear()-Methode referenziert die Parameter-Variable p ein anderes Punkt-Objekt, und der der Methode übergebene Verweis geht damit verloren. Diese Änderung wird nach außen hin natürlich nicht sichtbar, denn das Methoden-p ist ja nur ein temporärer alternativer Name für das main-p; eine Neuzuweisung an das Methoden-p ändert nicht das main-p, was bedeutet, dass der Aufrufer von clear() kein neues Objekt unter sich hat.

»Call by Reference« gibt es in Java nicht – ein Blick auf C und C++

In C++ gibt es eine weitere Argumentübergabe, die sich Call by Reference nennt. Würde eine Methode wie clear() mit Referenzsemantik deklariert, würde die Variable p ein Synonym darstellen, also einen anderen Namen für eine Variable – in unserem Fall q. Damit würde die Zuweisung im Rumpf den Zeiger auf ein neues Objekt legen. Die swap()-Funktion ist ein gutes Beispiel für die Nützlichkeit von Call by Reference:

void swap( int& a, int& b ) { int tmp = a; a = b; b = tmp; }

Zeiger und Referenzen sind in C++ etwas anderes, was Spracheinsteiger leicht irritiert. Denn in C++ und auch in C hätte eine vergleichbare swap()-Funktion auch mit Zeigern implementiert werden können:

void swap( int *a, int *b ) { int tmp = *a; *a = *b; *b = tmp; }
Die Implementierung gibt in C(++) einen Verweis auf das Argument.

Final deklarierte Referenz-Parameter und das fehlende const

Wir haben gesehen, dass finale Variablen dem Programmierer vorgeben, dass er Variablen nicht beschreiben darf. Das heißt, Zuweisungen sind tabu. Dabei ist es egal, ob die Parametervariable vom primitiven Typ oder vom Referenztyp ist. Bei einer Methodendeklaration der folgenden Art wäre also eine Zuweisung an i und auch an s verboten:

public void foo( final int i, final String s )

Ist die Parametervariable ein Referenztyp (und nicht final), so würden wir mit einer Zuweisung den Verweis auf das ursprüngliche Objekt verlieren, und das wäre wenig sinnvoll.

public void foo( String s )
{
s = "Keine Feier ohne Geier.";
}

Halten wir fest: Ist ein Parameter mit final deklariert, sind keine Zuweisungen möglich. final verbietet aber keine Änderungen an Objekten – und so könnte final im Sinne der Übersetzung »endgültig« verstanden werden. Mit der Referenz des Objekts können wir sehr wohl den Zustand verändern, so wie wir es auch im letzten Beispielprogramm taten.

final erfüllt demnach nicht die Aufgabe, schreibende Objektzugriffe zu verhindern. Eine Methode mit übergebenen Referenzen kann also Objektveränderungen vornehmen, wenn es etwa setXXX()-Methoden oder Variablen gibt, auf die zugegriffen werden kann. Die Dokumentation muss also immer ausdrücklich beschreiben, wann die Methode den Zustand eines Objekts modifiziert.

In C++ gibt es für Parameter den Zusatz const, an dem der Compiler erkennen kann, dass Objektzustände nicht verändert werden sollen. Ein Programm nennt sich const-korrekt, wenn es niemals ein konstantes Objekt verändert. Dieses const ist in C++ eine Erweiterung des Objekttyps, die es in Java nicht gibt. Zwar haben die Java-Entwickler das Schlüsselwort const reserviert, doch genutzt wird es bisher nicht.

Finale Parameter in der Vererbung *

In der Vererbung von Methoden spielen finale Parametervariablen keine Rolle. Wir können es als zusätzliche Information für die jeweilige Methode betrachten. Eine Unterklasse kann demnach beliebig das final hinzufügen oder auch wegnehmen. Alte Bibliotheken lassen sich so leicht weiterverwenden.


Rheinwerk Computing - Zum Seitenanfang

3.7.5 Identität von ObjektenZur nächsten ÜberschriftZur vorigen Überschrift

Der Vergleichsoperator == ist für alle Datentypen so definiert (natürlich auch !=), dass er die vollständige Übereinstimmung zweier Werte testet. Bei primitiven Datentypen ist das einfach einzusehen und bei Referenztypen im Prinzip genauso. Der Operator == testet bei Referenzen, ob diese übereinstimmen, also auf das gleiche Objekt verweisen. Demnach sagt der Test etwas über die Identität der referenzierten Objekte aus, aber nichts darüber, ob zwei verschiedene Objekte möglicherweise den gleichen Inhalt haben. Der Inhalt der Objekte spielt bei == keine Rolle.

Beispiel

Zwei Objekte mit drei unterschiedlichen Punktvariablen p, q, r und die Bedeutung von ==:

Point p = new Point( 10, 10 );
Point q = p;
Point r = new Point( 10, 10 );
System.out.println( p == q ); // true, da p und q dasselbe Objekt referenzieren
System.out.println( p == r ); // false, da p und r zwei verschiedene Punkt-
// Objekte referenzieren, die zufällig dieselben
// Koordinaten haben

Da p und q auf dasselbe Objekt verweisen, ergibt der Vergleich true. p und r referenzieren unterschiedliche Objekte, die aber zufälligerweise den gleichen Inhalt haben. Doch woher soll der Compiler wissen, wann zwei Punkt-Objekte inhaltlich gleich sind? Weil sich ein Punkt durch die Attribute x und y auszeichnet? Die Laufzeitumgebung könnte voreilig die Belegung jeder Objektvariablen vergleichen, doch das entspricht nicht immer einem korrekten Vergleich, so wie wir ihn uns wünschen. Ein Punkt-Objekt könnte etwa zusätzlich die Anzahl der Zugriffe zählen, die jedoch für einen Vergleich, der auf der Lage zweier Punkte basiert, nicht berücksichtigt werden darf.


Rheinwerk Computing - Zum Seitenanfang

3.7.6 Gleichheit und die Methode equals()Zur nächsten ÜberschriftZur vorigen Überschrift

Die allgemein gültige Lösung besteht darin, die Klasse festlegen zu lassen, wann Objekte gleich sind. Dazu kann jede Klasse eine Methode equals() implementieren, die Exemplare dieser Klasse mit beliebigen anderen Objekten vergleichen kann. Die Klassen entscheiden immer nach Anwendungsfall, welche Attribute sie für einen Gleichheitstest heranziehen, und equals() liefert true, wenn die gewünschten Zustände (Objektvariablen) übereinstimmen.

Beispiel

Zwei inhaltlich gleiche Punkt-Objekte, verglichen mit == und equals():

Point p = new Point( 10, 10 );
Point q = new Point( 10, 10 );
System.out.println( p == q ); // false
System.out.println( p.equals(q) ); // true. Da symmetrisch auch q.equals(p)
Nur equals() testet in diesem Fall die inhaltliche Gleichheit.

Bei den unterschiedlichen Bedeutungen müssen wir demnach die Begriffe Identität und Gleichheit von Objekten sorgfältig unterscheiden. Daher noch einmal eine Zusammenfassung:

Tabelle 3.4: Identität und Gleichheit von Objekten

Getestet mit Implementierung
Identität der Referenzen == nichts zu tun
Gleichheit der Zustände equals() abhängig von der Klasse

equals()-Implementierung von Point *

Die Klasse Point deklariert equals(), wie die API-Dokumentation zeigt. Werfen wir einen Blick auf die Implementierung, um eine Vorstellung von der Arbeitsweise zu bekommen:

Listing 3.17: java/awt/Point.java, Ausschnitt

public class Point ...
{
public int x;
public int y;

public boolean equals( Object obj )
{
if ( obj instanceof Point ) {
Point pt = (Point) obj;
return (x == pt.x) && (y == pt.y); // (*)
}
return super.equals( obj );
}
...
}

Obwohl bei diesem Beispiel für uns einiges neu ist, erkennen wir den Vergleich in der Zeile (*). Hier vergleicht das Point-Objekt seine eigenen Attribute mit den Attributen des Objekts, das als Argument an equals() übergeben wurde.

Es gibt immer ein equals(): die Oberklasse Object und ihr equals() *

Glücklicherweise müssen wir als Programmierer nicht lange darüber nachdenken, ob eine Klasse eine equals()-Methode anbieten soll oder nicht. Jede Klasse besitzt sie, da die universelle Oberklasse Object sie vererbt. Wir greifen hier auf Kapitel 5, »Eigene Klassen schreiben«, vor; der Abschnitt kann aber übersprungen werden. Wenn eine Klasse also keine eigene equals()-Methode angibt, dann erbt sie eine Implementierung aus der Klasse Object. Die sieht wie folgt aus:

Listing 3.18: java/lang/Object.java, Ausschnitt

public class Object
{
public boolean equals( Object obj )
{
return ( this == obj );
}
...
}

Wir erkennen, dass hier die Gleichheit auf die Gleichheit der Referenzen abgebildet wird. Ein inhaltlicher Vergleich findet nicht statt. Das ist das einzige, was die vorgegebene Implementierung machen kann, denn sind die Referenzen identisch, sind die Objekte logischerweise auch gleich. Nur über Zustände »weiß« die Basisklasse Object nichts.



Ihr Kommentar

Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen.

>> Zum Feedback-Formular
<< zurück
  Zum Katalog
Zum Katalog: Java ist auch eine Insel





Java ist auch eine Insel
Jetzt bestellen


 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchempfehlungen
Zum Katalog: Java ist auch eine Insel






 Java ist auch
 eine Insel


Zum Katalog: Java SE Bibliotheken






 Java SE Bibliotheken


Zum Katalog: Professionell entwickeln mit Java EE 7






 Professionell
 entwickeln mit
 Java EE 7


Zum Katalog: Einstieg in Eclipse






 Einstieg in
 Eclipse


Zum Katalog: Einstieg in Java






 Einstieg in
 Java


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo




Copyright © Rheinwerk Verlag GmbH 2011
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Rheinwerk Computing]

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de