Objekte und Referenzen
Lassen Sie uns mit Objekten und Referenzen weiterarbeiten. Nehmen wir an, wir können die Klasse verwenden, die eine Person repräsentiert, wie unten gezeigt. Die Klasse Person
hat Objektvariablen wie Name, Alter, Gewicht und Größe. Zusätzlich enthält sie Methoden zur Berechnung des Body-Mass-Index und andere Methoden.
public class Person {
private String name;
private int age;
private int weight;
private int height;
public Person(String name) {
this(name, 0, 0, 0);
}
public Person(String name, int age, int height, int weight) {
this.name = name;
this.age = age;
this.weight = weight;
this.height = height;
}
// weitere Konstruktoren und Methoden
public String getName() {
return this.name;
}
public int getAge() {
return this.age;
}
public int getHeight() {
return this.height;
}
public void growOlder() {
this.age = this.age + 1;
}
public void setHeight(int newHeight) {
this.height = newHeight;
}
public void setWeight(int newWeight) {
this.weight = newWeight;
}
public double bodyMassIndex() {
double heightPerHundred = this.height / 100.0;
return this.weight / (heightPerHundred * heightPerHundred);
}
@Override
public String toString() {
return this.name + ", age " + this.age + " years";
}
}
Was passiert genau, wenn ein neues Objekt erstellt wird?
Person joan = new Person("Joan Ball");
Ein Konstruktoraufruf mit dem Befehl new
bewirkt mehrere Dinge. Zuerst wird im Arbeitsspeicher des Computers Platz für die Speicherung der Objektvariablen reserviert. Dann werden Standard- oder Anfangswerte für die Objektvariablen gesetzt (z.B. erhält eine Variable vom Typ int
einen Anfangswert von 0). Schließlich wird der Quellcode im Konstruktor ausgeführt.
Ein Konstruktoraufruf gibt eine Referenz auf ein Objekt zurück. Eine Referenz ist eine Information über den Speicherort der Objektdaten.

Der Wert der Variablen wird also auf eine Referenz gesetzt, d.h. auf das Wissen über den Speicherort der zugehörigen Objektdaten. Das obige Bild zeigt auch, dass Strings — wie der Name unserer Beispielperson — ebenfalls Objekte sind.
Zuweisung einer Referenztyp-Variablen kopiert die Referenz
Fügen wir eine Person
-Typ-Variable namens ball
in das Programm ein und weisen joan
als Anfangswert zu. Was passiert dann?
Person joan = new Person("Joan Ball");
System.out.println(joan);
Person ball = joan;
Die Anweisung Person ball = joan;
erstellt eine neue Person-Variable ball
und kopiert den Wert der Variablen joan
als ihren Wert. Als Ergebnis verweist ball
auf dasselbe Objekt wie joan
.

Schauen wir uns das gleiche Beispiel etwas genauer an.
Person joan = new Person("Joan Ball");
System.out.println(joan);
Person ball = joan;
ball.growOlder();
ball.growOlder();
System.out.println(joan);
Joan Ball, age 0 years Joan Ball, age 2 years
Joan Ball — also das Personenobjekt, auf das die Referenz in der Variablen joan
zeigt — ist zu Beginn 0 Jahre alt. Danach wird der Wert der Variablen joan
der Variablen ball
zugewiesen (also kopiert). Das Personenobjekt ball
wird um zwei Jahre älter, und Joan Ball altert dementsprechend!
Der interne Zustand eines Objekts wird nicht kopiert, wenn der Wert einer Variablen zugewiesen wird. Es wird kein neues Objekt in der Anweisung Person ball = joan;
erstellt — der Wert der Variablen ball
wird auf eine Kopie von joans
Wert gesetzt, also auf eine Referenz auf ein Objekt.

Als nächstes wird das Beispiel so fortgesetzt, dass ein neues Objekt für die Variable joan
erstellt und eine Referenz darauf als Wert der Variablen zugewiesen wird. Die Variable ball
verweist weiterhin auf das zuvor erstellte Objekt.
Person joan = new Person("Joan Ball");
System.out.println(joan);
Person ball = joan;
ball.growOlder();
ball.growOlder();
System.out.println(joan);
joan = new Person("Joan B.");
System.out.println(joan);
Das folgende wird ausgegeben:
Joan Ball, age 0 years Joan Ball, age 2 years Joan B., age 0 years
Am Anfang enthält die Variable joan
eine Referenz auf ein Objekt, aber am Ende wurde eine Referenz auf ein anderes Objekt als ihr Wert kopiert. Hier ist eine Darstellung der Situation nach der letzten Codezeile.

null
-Wert einer Referenzvariablen
Erweitern wir das Beispiel weiter, indem wir den Wert der Referenzvariablen ball
auf null
setzen, also auf eine Referenz "auf nichts". Die null
-Referenz kann als Wert jeder Referenztyp-Variablen gesetzt werden.
Person joan = new Person("Joan Ball");
System.out.println(joan);
Person ball = joan;
ball.growOlder();
ball.growOlder();
System.out.println(joan);
joan = new Person("Joan B.");
System.out.println(joan);
ball = null;
Die Situation des Programms nach der letzten Zeile ist im folgenden Bild dargestellt.

Das Objekt, dessen Name Joan Ball ist, wird von niemandem mehr referenziert. Mit anderen Worten, das Objekt ist zu "Müll" geworden. In der Programmiersprache Java muss sich der Programmierende nicht um die Speichernutzung des Programms kümmern. Von Zeit zu Zeit bereinigt der automatische Müllsammler der Java-Sprache die Objekte, die zu Müll geworden sind. Wenn keine Müllsammlung stattfinden würde, würden die Müllobjekte bis zum Ende der Programmausführung einen Speicherplatz belegen.
Sehen wir uns an, was passiert, wenn wir versuchen, eine Variable auszugeben, die auf "nichts" referenziert, also null
.
Person joan = new Person("Joan Ball");
System.out.println(joan);
Person ball = joan;
ball.growOlder();
ball.growOlder();
System.out.println(joan);
joan = new Person("Joan B.");
System.out.println(joan);
ball = null;
System.out.println(ball);
Joan Ball, age 0 years Joan Ball, age 2 years Joan B., age 0 years null
Das Ausgeben einer null
-Referenz gibt "null" aus. Was passiert, wenn wir versuchen, eine Methode, z.B. growOlder
, auf einem Objekt aufzurufen, das auf nichts verweist:
Person joan = new Person("Joan Ball");
System.out.println(joan);
joan = null;
joan.growOlder();
Das Ergebnis:
Joan Ball, age 0 years Exception in thread "main" java.lang.NullPointerException at Main.main(Main.java:(row)) Java Result: 1
Schlimme Dinge passieren. Dies könnte das erste Mal sein, dass Sie den Text NullPointerException sehen. Während des Programmablaufs ist ein Fehler aufgetreten, der darauf hinweist, dass eine Methode auf einer Variablen aufgerufen wurde, die auf nichts verweist.
Wir versprechen Ihnen, dass dies nicht das letzte Mal ist, dass Sie diesen Fehler sehen. Wenn dies geschieht, ist der erste Schritt, nach Variablen zu suchen, deren Wert null
sein könnte. Glücklicherweise ist die Fehlermeldung hilfreich: Sie zeigt an, welche Zeile den Fehler verursacht hat. Probieren Sie es selbst aus!
Objekt als Methodenparameter
Wir haben gesehen, dass sowohl primitive als auch Referenzvariablen als Methodenparameter fungieren können. Da Objekte Referenzvariablen sind, kann jede Art von Objekt als Methodenparameter definiert werden. Schauen wir uns ein praktisches Beispiel an.
Fahrgeschäfte in Vergnügungsparks erlauben nur Personen, die größer als eine bestimmte Höhe sind. Die Grenze ist nicht für alle Attraktionen gleich. Erstellen wir eine Klasse, die ein Fahrgeschäft im Vergnügungspark repräsentiert. Beim Erstellen eines neuen Objekts erhält der Konstruktor als Parameter den Namen der Attraktion und die kleinste Höhe, die den Eintritt zur Attraktion erlaubt.
public class AmusementParkRide {
private String name;
private int lowestHeight;
public AmusementParkRide(String name, int lowestHeight) {
this.name = name;
this.lowestHeight = lowestHeight;
}
public String toString() {
return this.name + ", minimum height: " + this.lowestHeight;
}
}
Schreiben wir dann eine Methode, die verwendet werden kann, um zu prüfen, ob eine Person groß genug ist, um die Attraktion zu betreten. Die Methode gibt true
zurück, wenn die Person, die als Parameter übergeben wird, Zugang erhält, und false
ansonsten.
Im Folgenden wird angenommen, dass die Klasse Person
die Methode public int getHeight()
enthält, die die Größe der Person zurückgibt.
public class AmusementParkRide {
private String name;
private int lowestHeight;
public AmusementParkRide(String name, int lowestHeight) {
this.name = name;
this.lowestHeight = lowestHeight;
}
public boolean allowedToRide(Person person) {
if (person.getHeight() < this.lowestHeight) {
return false;
}
return true;
}
public String toString() {
return this.name + ", minimum height: " + this.lowestHeight;
}
}
Die Methode allowedToRide
eines AmusementParkRide
-Objekts erhält ein Person
-Objekt als Parameter. Wie zuvor wird der Wert der Variablen — in diesem Fall eine Referenz — für die Methode zur Verwendung kopiert. Die Methode verarbeitet eine kopierte Referenz und ruft die Methode getHeight
der Person auf, die als Parameter übergeben wurde.
Im Folgenden ist ein Beispiel-Hauptprogramm, in dem die Methode des Fahrgeschäfts zweimal aufgerufen wird: zuerst wird das übergebene Parameter-Objekt matt
, und dann das Parameter-Objekt jasper
:
Person matt = new Person("Matt");
matt.setWeight(86);
matt.setHeight(180);
Person jasper = new Person("Jasper");
jasper.setWeight(34);
jasper.setHeight(132);
AmusementParkRide waterTrack = new AmusementParkRide("Water track", 140);
if (waterTrack.allowedToRide(matt)) {
System.out.println(matt.getName() + " may enter the ride");
} else {
System.out.println(matt.getName() + " may not enter the ride");
}
if (waterTrack.allowedToRide(jasper)) {
System.out.println(jasper.getName() + " may enter the ride");
} else {
System.out.println(jasper.getName() + " may not enter the ride");
}
System.out.println(waterTrack);
Die Ausgabe des Programms ist:
Matt may enter the ride Jasper may not enter the ride Water track, minimum height: 140
Was, wenn wir wissen wollten, wie viele Personen das Fahrgeschäft genutzt haben?
Fügen wir dem Fahrgeschäft eine Objektvariable hinzu. Sie verfolgt die Anzahl der Personen, denen der Zutritt erlaubt wurde.
public class AmusementParkRide {
private String name;
private int lowestHeight;
private int visitors;
public AmusementParkRide(String name, int lowestHeight) {
this.name = name;
this.lowestHeight = lowestHeight;
this.visitors = 0;
}
public boolean allowedToRide(Person person) {
if (person.getHeight() < this.lowestHeight) {
return false;
}
this.visitors++;
return true;
}
public String toString() {
return this.name + ", minimum height: " + this.lowestHeight +
", visitors: " + this.visitors;
}
}
Jetzt verfolgt auch das zuvor verwendete Beispielprogramm die Anzahl der Besucher, die das Fahrgeschäft genutzt haben.
Person matt = new Person("Matt");
matt.setWeight(86);
matt.setHeight(180);
Person jasper = new Person("Jasper");
jasper.setWeight(34);
jasper.setHeight(132);
AmusementParkRide waterTrack = new AmusementParkRide("Water track", 140);
if (waterTrack.allowedToRide(matt)) {
System.out.println(matt.getName() + " may enter the ride");
} else {
System.out.println(matt.getName() + " may not enter the ride");
}
if (waterTrack.allowedToRide(jasper)) {
System.out.println(jasper.getName() + " may enter the ride");
} else {
System.out.println(jasper.getName() + " may not enter the ride");
}
System.out.println(waterTrack);
Die Ausgabe des Programms ist:
Matt may enter the ride Jasper may not enter the ride Water track, minimum height: 140, visitors: 1
Objekt als Objektvariable
Objekte können Referenzen auf andere Objekte enthalten.
Arbeiten wir weiter mit Personen und fügen dem Person
-Klasse einen Geburtstag hinzu. Eine natürliche Art, einen Geburtstag darzustellen, ist die Verwendung einer Date
-Klasse. Wir könnten den Klassennamen Date
verwenden, aber um Verwechslungen mit der ähnlich benannten vorhandenen Java-Klasse zu vermeiden, werden wir hier SimpleDate
verwenden.
public class SimpleDate {
private int day;
private int month;
private int year;
public SimpleDate(int day, int month, int year) {
this.day = day;
this.month = month;
this.year = year;
}
public int getDay() {
return this.day;
}
public int getMonth() {
return this.month;
}
public int getYear() {
return this.year;
}
@Override
public String toString() {
return this.day + "." + this.month + "." + this.year;
}
}
Da wir den Geburtstag kennen, ist es nicht notwendig, das Alter einer Person als separate Objektvariable zu speichern. Das Alter der Person kann aus ihrem Geburtstag abgeleitet werden. Nehmen wir an, dass die Klasse Person
jetzt die folgenden Variablen hat.
public class Person {
private String name;
private SimpleDate birthday;
private int weight = 0;
private int length = 0;
// ...
Erstellen wir einen neuen Konstruktor für die Klasse Person
, der es ermöglicht, den Geburtstag festzulegen:
public Person(String name, SimpleDate date) {
this.name = name;
this.birthday = date;
}
Zusammen mit dem obigen Konstruktor könnten wir der Person einen weiteren Konstruktor geben, bei dem der Geburtstag als Ganzzahlen angegeben wird.
public Person(String name, int day, int month, int year) {
this.name = name;
this.birthday = new SimpleDate(day, month, year);
}
Der Konstruktor erhält als Parameter die verschiedenen Teile des Datums (Tag, Monat, Jahr). Diese werden verwendet, um ein Datumsobjekt zu erstellen, und schließlich wird die Referenz auf dieses Datum als Wert der Objektvariable birthday
kopiert.
Ändern wir die toString
-Methode der Klasse Person
so, dass sie anstelle des Alters den Geburtstag zurückgibt:
public String toString() {
return this.name + ", born on " + this.birthday;
}
Sehen wir uns an, wie die aktualisierte Klasse Person
funktioniert.
SimpleDate date = new SimpleDate(1, 1, 780);
Person muhammad = new Person("Muhammad ibn Musa al-Khwarizmi", date);
Person pascal = new Person("Blaise Pascal", 19, 6, 1623);
System.out.println(muhammad);
System.out.println(pascal);
Muhammad ibn Musa al-Khwarizmi, born on 1.1.780 Blaise Pascal, born on 19.6.1623
Jetzt hat ein Personenobjekt die Objektvariablen name
und birthday
. Die Variable name
ist ein String, der selbst ein Objekt ist; die Variable birthday
ist ein SimpleDate
-Objekt.
Beide Variablen enthalten eine Referenz auf ein Objekt. Daher enthält ein Personenobjekt zwei Referenzen. Im Bild unten werden Gewicht und Größe überhaupt nicht berücksichtigt.

Das Hauptprogramm ist also über Stränge mit zwei Person
-Objekten verbunden. Eine Person hat einen Namen und einen Geburtstag. Da beide Variablen Objekte sind, existieren diese Attribute am anderen Ende der Stränge.
Der Geburtstag scheint eine gute Erweiterung der Person
-Klasse zu sein. Früher haben wir festgestellt, dass die Objektvariable age
mit dem Geburtstag berechnet werden kann, also wurde sie entfernt.
Objekt des gleichen Typs als Methodenparameter
Wir arbeiten weiter mit der Klasse Person
. Wir erinnern uns, dass Personen ihre Geburtstage kennen:
public class Person {
private String name;
private SimpleDate birthday;
private int height;
private int weight;
// ...
}
Wir möchten das Alter von zwei Personen vergleichen. Der Vergleich kann auf verschiedene Weise erfolgen. Wir könnten zum Beispiel eine Methode namens public int ageAsYears()
für die Klasse Person
implementieren; in diesem Fall würde der Vergleich wie folgt ablaufen:
Person muhammad = new Person("Muhammad ibn Musa al-Khwarizmi", 1, 1, 780);
Person pascal = new Person("Blaise Pascal", 19, 6, 1623);
if (muhammad.ageAsYears() > pascal.ageAsYears()) {
System.out.println(muhammad.getName() + " is older than " + pascal.getName());
}
Wir werden nun eine „objektorientiertere“ Möglichkeit kennenlernen, das Alter von Personen zu vergleichen.
Wir werden eine neue Methode boolean olderThan(Person compared)
für die Klasse Person
erstellen. Sie kann verwendet werden, um ein bestimmtes Personenobjekt mit der als Parameter übergebenen Person anhand ihres Alters zu vergleichen.
Die Methode ist wie folgt zu verwenden:
Person muhammad = new Person("Muhammad ibn Musa al-Khwarizmi", 1, 1, 780);
Person pascal = new Person("Blaise Pascal", 19, 6, 1623);
if (muhammad.olderThan(pascal)) { // entspricht muhammad.olderThan(pascal)==true
System.out.println(muhammad.getName() + " is older than " + pascal.getName());
} else {
System.out.println(muhammad.getName() + " is not older than " + pascal.getName());
}
Das obige Programm fragt, ob al-Khwarizmi älter ist als Pascal. Die Methode olderThan
gibt true
zurück, wenn das Objekt, das verwendet wird, um die Methode aufzurufen (object.olderThan(objectGivenAsParameter)
), älter ist als das Objekt, das als Parameter übergeben wird, und false
andernfalls.
In der Praxis rufen wir die Methode olderThan
des Objekts auf, das "Muhammad ibn Musa al-Khwarizmi" entspricht, auf das durch die Variable muhammad
verwiesen wird. Die Referenz pascal
, die dem Objekt "Blaise Pascal" entspricht, wird als Parameter an diese Methode übergeben.
Das Programm druckt:
Muhammad ibn Musa al-Khwarizmi is older than Blaise Pascal
Die Methode olderThan
erhält ein Personenobjekt als Parameter. Genauer gesagt, erhält die als Methodenparameter definierte Variable eine Kopie des Werts der übergebenen Variablen. Dieser Wert ist in diesem Fall eine Referenz auf ein Objekt.
Die Implementierung der Methode ist unten illustriert. Beachten Sie, dass die Methode an mehr als einer Stelle einen Wert zurückgeben kann — hier wurde der Vergleich in mehrere Teile aufgeteilt, basierend auf den Jahren, den Monaten und den Tagen:
public class Person {
// ...
public boolean olderThan(Person compared) {
// 1. Vergleichen Sie zuerst die Jahre
int ownYear = this.getBirthday().getYear();
int comparedYear = compared.getBirthday().getYear();
if (ownYear < comparedYear) {
return true;
}
if (ownYear > comparedYear) {
return false;
}
// 2. Dasselbe Geburtsjahr, vergleichen Sie die Monate
int ownMonth = this.getBirthday().getMonth();
int comparedMonth = compared.getBirthday().getMonth();
if (ownMonth < comparedMonth) {
return true;
}
if (ownMonth > comparedMonth) {
return false;
}
// 3. Dasselbe Geburtsjahr und -monat, vergleichen Sie die Tage
int ownDay = this.getBirthday().getDay();
int comparedDay = compared.getBirthday().getDay();
if (ownDay < comparedDay) {
return true;
}
return false;
}
}
Pausieren wir einen Moment, um über Abstraktion, eines der Prinzipien der objektorientierten Programmierung, nachzudenken. Die Idee hinter der Abstraktion besteht darin, den Programmcode so zu konzeptionieren, dass jedes Konzept seine eigenen klaren Verantwortlichkeiten hat. Wenn wir uns die obige Lösung ansehen, stellen wir jedoch fest, dass die Vergleichsfunktionalität besser in der Klasse SimpleDate
als in der Klasse Person
platziert wäre.
Wir erstellen eine Methode namens public boolean before(SimpleDate compared)
für die Klasse SimpleDate
. Die Methode gibt den Wert true
zurück, wenn das als Parameter übergebene Datum nach (oder am gleichen Tag wie) dem Datum des Objekts, dessen Methode aufgerufen wird, liegt.
public class SimpleDate {
private int day;
private int month;
private int year;
public SimpleDate(int day, int month, int year) {
this.day = day;
this.month = month;
this.year = year;
}
public String toString() {
return this.day + "." + this.month + "." + this.year;
}
// wird verwendet, um zu überprüfen, ob dieses Datumsobjekt (`this`)
// vor dem Datumsobjekt liegt, das als Parameter (`compared`) übergeben wird.
public boolean before(SimpleDate compared) {
// zuerst die Jahre vergleichen
if (this.year < compared.year) {
return true;
}
if (this.year > compared.year) {
return false;
}
// Jahre sind gleich, vergleichen Sie die Monate
if (this.month < compared.month) {
return true;
}
if (this.month > compared.month) {
return false;
}
// Jahre und Monate sind gleich, vergleichen Sie die Tage
if (this.day < compared.day) {
return true;
}
return false;
}
}
Obwohl die Objektvariablen year
, month
und day
gekapselt (private
) sind, können wir ihre Werte durch Schreiben von compared.*variableName*
lesen. Dies liegt daran, dass auf eine private
-Variable von allen Methoden zugegriffen werden kann, die in dieser Klasse enthalten sind. Beachten Sie, dass die Syntax hier dem Aufruf einer Objektmethode entspricht. Anders als beim Aufruf einer Methode beziehen wir uns jedoch auf ein Feld eines Objekts, sodass die Klammern, die einen Methodenaufruf anzeigen, nicht geschrieben werden.
Ein Beispiel, wie die Methode verwendet wird:
public static void main(String[] args) {
SimpleDate d1 = new SimpleDate(14, 2, 2011);
SimpleDate d2 = new SimpleDate(21, 2, 2011);
SimpleDate d3 = new SimpleDate(1, 3, 2011);
SimpleDate d4 = new SimpleDate(31, 12, 2010);
System.out.println(d1 + " is earlier than " + d2 + ": " + d1.before(d2));
System.out.println(d2 + " is earlier than " + d1 + ": " + d2.before(d1));
System.out.println(d2 + " is earlier than " + d3 + ": " + d2.before(d3));
System.out.println(d3 + " is earlier than " + d2 + ": " + d3.before(d2));
System.out.println(d4 + " is earlier than " + d1 + ": " + d4.before(d1));
System.out.println(d1 + " is earlier than " + d4 + ": " + d1.before(d4));
}
14.2.2011 is earlier than 21.2.2011: true 21.2.2011 is earlier than 14.2.2011: false 21.2.2011 is earlier than 1.3.2011: true 1.3.2011 is earlier than 21.2.2011: false 31.12.2010 is earlier than 14.2.2011: true 14.2.2011 is earlier than 31.12.2010: false
Lassen Sie uns die Methode olderThan
der Klasse Person
so anpassen, dass wir von nun an die Vergleichsfunktionalität nutzen, die Datumsobjekte bieten.
public class Person {
// ...
public boolean olderThan(Person compared) {
if (this.birthday.before(compared.getBirthday())) {
return true;
}
return false;
// oder direkter zurückgeben:
// return this.birthday.before(compared.getBirthday());
}
}
Jetzt ist der konkrete Vergleich von Daten in der Klasse implementiert, zu der er logisch (basierend auf den Klassennamen) gehört.
Vergleich der Gleichheit von Objekten (equals)
Beim Arbeiten mit Strings haben wir gelernt, dass Strings mit der Methode equals
verglichen werden müssen. So wird es gemacht.
Scanner scanner = new Scanner(System.in);
System.out.println("Enter two words, each on its own line.")
String first = scanner.nextLine();
String second = scanner.nextLine();
if (first.equals(second)) {
System.out.println("The words were the same.");
} else {
System.out.println("The words were not the same.");
}
Bei primitiven Variablen wie int
kann der Vergleich zweier Variablen mit zwei Gleichheitszeichen erfolgen. Dies liegt daran, dass der Wert einer primitiven Variablen direkt im „Kasten der Variablen“ gespeichert wird. Der Wert von Referenzvariablen ist im Gegensatz dazu eine Adresse des referenzierten Objekts; der „Kasten“ enthält also eine Referenz auf den Speicherort. Die Verwendung von zwei Gleichheitszeichen vergleicht die Gleichheit der in den „Kästen der Variablen“ gespeicherten Werte — bei Referenzvariablen würden solche Vergleiche die Gleichheit der Speicherreferenzen untersuchen.
Die Methode equals
ähnelt der Methode toString
insofern, als sie verwendet werden kann, auch wenn sie nicht in der Klasse definiert wurde. Die Standardimplementierung dieser Methode vergleicht die Gleichheit der Referenzen. Lassen Sie uns dies mit Hilfe der zuvor geschriebenen SimpleDate
-Klasse beobachten.
SimpleDate first = new SimpleDate(1, 1, 2000);
SimpleDate second = new SimpleDate(1, 1, 2000);
SimpleDate third = new SimpleDate(12, 12, 2012);
SimpleDate fourth = first;
if (first.equals(first)) {
System.out.println("Variables first and first are equal");
} else {
System.out.println("Variables first and first are not equal");
}
if (first.equals(second)) {
System.out.println("Variables first and second are equal");
} else {
System.out.println("Variables first and second are not equal");
}
if (first.equals(third)) {
System.out.println("Variables first and third are equal");
} else {
System.out.println("Variables first and third are not equal");
}
if (first.equals(fourth)) {
System.out.println("Variables first and fourth are equal");
} else {
System.out.println("Variables first and fourth are not equal");
}
Variables first and first are equal Variables first and second are not equal Variables first and third are not equal Variables first and fourth are equal
Es gibt ein Problem mit dem obigen Programm. Obwohl zwei Daten (first und second) genau dieselben Werte für Objektvariablen haben, sind sie aus der Sicht der Standardmethode equals
unterschiedlich.
Wenn wir in der Lage sein möchten, zwei Objekte unseres eigenen Designs mit der Methode equals
zu vergleichen, muss diese Methode in der Klasse definiert werden. Die Methode equals
wird so definiert, dass sie ein boolescher Wert zurückgibt — der Rückgabewert zeigt an, ob die Objekte gleich sind.
Die Methode equals
wird so implementiert, dass sie verwendet werden kann, um das aktuelle Objekt mit jedem anderen Objekt zu vergleichen. Die Methode erhält ein Objekt vom Typ Object
als einzigen Parameter — alle Objekte sind vom Typ Object
, zusätzlich zu ihrem eigenen Typ. Die Methode equals
vergleicht zunächst, ob die Adressen gleich sind: Wenn ja, sind die Objekte gleich. Danach wird überprüft, ob die Typen der Objekte gleich sind: Wenn nicht, sind die Objekte nicht gleich. Als Nächstes wird das Objekt vom Typ Object
, das als Parameter übergeben wurde, mit einem Typecast in den Typ des untersuchten Objekts konvertiert, sodass die Werte der Objektvariablen verglichen werden können. Unten ist der Gleichheitsvergleich für die Klasse SimpleDate
implementiert.
public class SimpleDate {
private int day;
private int month;
private int year;
public SimpleDate(int day, int month, int year) {
this.day = day;
this.month = month;
this.year = year;
}
public int getDay() {
return this.day;
}
public int getMonth() {
return this.month;
}
public int getYear() {
return this.year;
}
public boolean equals(Object compared) {
// wenn die Variablen an derselben Position liegen, sind sie gleich
if (this == compared) {
return true;
}
// wenn der Typ des verglichenen Objekts nicht `SimpleDate` ist, sind die Objekte nicht gleich
if (!(compared instanceof SimpleDate)) {
return false;
}
// Konvertieren Sie das Objekt `Object` in ein `SimpleDate`-Objekt namens `comparedSimpleDate`.
SimpleDate comparedSimpleDate = (SimpleDate) compared;
// wenn die Werte der Objektvariablen gleich sind, sind die Objekte gleich
if (this.day == comparedSimpleDate.day &&
this.month == comparedSimpleDate.month &&
this.year == comparedSimpleDate.year) {
return true;
}
// andernfalls sind die Objekte nicht gleich
return false;
}
@Override
public String toString() {
return this.day + "." + this.month + "." + this.year;
}
}
Vergleichsfunktionalität für Person-Objekte
Eine ähnliche Vergleichsfunktionalität kann auch für Person-Objekte implementiert werden. Im Folgenden wurde der Vergleich für Person-Objekte implementiert, die kein separates SimpleDate-Objekt besitzen. Beachten Sie, dass die Namen von Personen Strings (also Objekte) sind, weshalb die equals
-Methode zum Vergleich verwendet wird.
public class Person {
private String name;
private int age;
private int weight;
private int height;
// Konstruktoren und Methoden
public boolean equals(Object compared) {
// Wenn die Variablen sich an derselben Speicherposition befinden, sind sie gleich
if (this == compared) {
return true;
}
// Wenn das verglichene Objekt nicht vom Typ Person ist, sind die Objekte nicht gleich
if (!(compared instanceof Person)) {
return false;
}
// Konvertiere das Objekt in ein Person-Objekt
Person comparedPerson = (Person) compared;
// Wenn die Werte der Objektvariablen gleich sind, sind die Objekte gleich
if (this.name.equals(comparedPerson.name) &&
this.age == comparedPerson.age &&
this.weight == comparedPerson.weight &&
this.height == comparedPerson.height) {
return true;
}
// Andernfalls sind die Objekte nicht gleich
return false;
}
// .. Methoden
}
Objektgleichheit und Listen
Schauen wir uns an, wie die Methode equals
mit Listen verwendet wird. Angenommen, wir haben die zuvor beschriebene Klasse Bird
ohne equals
-Methode.
public class Bird {
private String name;
public Bird(String name) {
this.name = name;
}
}
Lassen Sie uns eine Liste erstellen und einen Vogel hinzufügen. Danach überprüfen wir, ob dieser Vogel in der Liste enthalten ist.
ArrayList<Bird> birds = new ArrayList<>()
Bird red = new Bird("Red");
if (birds.contains(red)) {
System.out.println("Red is on the list.");
} else {
System.out.println("Red is not on the list.");
}
birds.add(red);
if (birds.contains(red)) {
System.out.println("Red is on the list.");
} else {
System.out.println("Red is not on the list.");
}
System.out.println("However!");
red = new Bird("Red");
if (birds.contains(red)) {
System.out.println("Red is on the list.");
} else {
System.out.println("Red is not on the list.");
}
Red is not on the list. Red is on the list. However! Red is not on the list.
Wir können im obigen Beispiel feststellen, dass wir in einer Liste nach unseren eigenen Objekten suchen können. Zuerst, als der Vogel noch nicht zur Liste hinzugefügt wurde, wird er nicht gefunden — und nach dem Hinzufügen wird er gefunden. Wenn das Programm das red
-Objekt durch ein neues Objekt ersetzt, das genau denselben Inhalt wie zuvor hat, ist es nicht mehr gleich dem Objekt auf der Liste und kann daher nicht in der Liste gefunden werden.
Die Methode contains
einer Liste verwendet die equals
-Methode, die für die Objekte definiert ist, bei der Suche nach Objekten. Im obigen Beispiel hat die Klasse Bird
keine Definition für diese Methode, daher kann ein Vogel mit genau demselben Inhalt — aber einer anderen Referenz — nicht in der Liste gefunden werden.
Lassen Sie uns die equals
-Methode für die Klasse Bird
implementieren. Die Methode prüft, ob die Namen der Objekte gleich sind — wenn die Namen übereinstimmen, gelten die Vögel als gleich.
public class Bird {
private String name;
public Bird(String name) {
this.name = name;
}
public boolean equals(Object compared) {
// Wenn die Variablen sich an derselben Speicherposition befinden, sind sie gleich
if (this == compared) {
return true;
}
// Wenn das verglichene Objekt nicht vom Typ Bird ist, sind die Objekte nicht gleich
if (!(compared instanceof Bird)) {
return false;
}
// Konvertiere das Objekt in ein Bird-Objekt
Bird comparedBird = (Bird) compared;
// Wenn die Werte der Objektvariablen gleich sind, sind die Objekte ebenfalls gleich
return this.name.equals(comparedBird.name);
/*
// Der Vergleich der Namen oben entspricht dem folgenden Code
if (this.name.equals(comparedBird.name)) {
return true;
}
// Andernfalls sind die Objekte nicht gleich
return false;
*/
}
}
Jetzt erkennt die contains-Methode der Liste Vögel mit identischem Inhalt.
ArrayList<Bird> birds = new ArrayList<>()
Bird red = new Bird("Red");
if (birds.contains(red)) {
System.out.println("Red is on the list.");
} else {
System.out.println("Red is not on the list.");
}
birds.add(red);
if (birds.contains(red)) {
System.out.println("Red is on the list.");
} else {
System.out.println("Red is not on the list.");
}
System.out.println("However!");
red = new Bird("Red");
if (birds.contains(red)) {
System.out.println("Red is on the list.");
} else {
System.out.println("Red is not on the list.");
}
Red is not on the list. Red is on the list. However! Red is on the list.
Objekt als Rückgabewert einer Methode
Wir haben gesehen, dass Methoden boolesche Werte, Zahlen und Strings zurückgeben können. Es ist leicht zu erraten, dass eine Methode ein Objekt eines beliebigen Typs zurückgeben kann.
Im nächsten Beispiel präsentieren wir einen einfachen Zähler, der die Methode clone
besitzt. Diese Methode kann verwendet werden, um eine Kopie des Zählers zu erstellen, also ein neues Zählerobjekt, das denselben Wert zum Zeitpunkt seiner Erstellung hat wie der zu klonende Zähler.
public class Counter {
private int value;
// Beispiel für die Verwendung mehrerer Konstruktoren:
// Sie können einen anderen Konstruktor von einem Konstruktor aus aufrufen, indem Sie this verwenden
// Beachten Sie, dass der this-Aufruf an der ersten Zeile des Konstruktors stehen muss
public Counter() {
this(0);
}
public Counter(int initialValue) {
this.value = initialValue;
}
public void increase() {
this.value = this.value + 1;
}
public String toString() {
return "value: " + value;
}
public Counter clone() {
// Erstellen Sie ein neues Zählerobjekt, das den Wert des geklonten Zählers als Anfangswert erhält
Counter clone = new Counter(this.value);
// Geben Sie die Kopie an den Aufrufer zurück
return clone;
}
}
Ein Beispiel zur Verwendung von Zählern folgt:
Counter counter = new Counter();
counter.increase();
counter.increase();
System.out.println(counter); // gibt 2 aus
Counter clone = counter.clone();
System.out.println(counter); // gibt 2 aus
System.out.println(clone); // gibt 2 aus
counter.increase();
counter.increase();
counter.increase();
counter.increase();
System.out.println(counter); // gibt 6 aus
System.out.println(clone); // gibt 2 aus
clone.increase();
System.out.println(counter); // gibt 6 aus
System.out.println(clone); // gibt 3 aus
Unmittelbar nach dem Klonvorgang sind die in der Kopie und im geklonten Objekt enthaltenen Werte identisch. Sie sind jedoch zwei verschiedene Objekte, sodass das Erhöhen des Werts eines Zählers den Wert des anderen in keiner Weise beeinflusst.
Ebenso könnte ein Factory
-Objekt verwendet werden, um neue Car
-Objekte zu erstellen und zurückzugeben. Im Folgenden finden Sie eine Skizze des Fabrikentwurfs – die Fabrik kennt auch die Marke der hergestellten Autos.
public class Factory {
private String make;
public Factory(String make) {
this.make = make;
}
public Car procuceCar() {
return new Car(this.make);
}
}