Java Challengers # 2: String-Vergleich
Wie immer haben wir viel Verspätung für den Beginn des Kurses, so dass erst gestern eine zweite Lektion unter dem neuen Thread "Java Developer" abgehalten wurde. Aber das ist so, kleine Dinge im Leben, aber im Moment veröffentlichen wir weiterhin eine Reihe von Java Challengers-Artikeln, deren Übersetzung für Sie vorbereitet wurde.
In Java kapselt die String
Klasse ein char
Array ( Anmerkung des Übersetzers - mit Java 9 ist es bereits ein byte
Array, siehe Compact Strings in Java 9 ). In einfachen Worten ist String
ein Array von Zeichen, mit denen Wörter, Sätze oder andere Konstrukte zusammengesetzt werden.
Die Kapselung ist eines der mächtigsten Konzepte in der objektorientierten Programmierung. Dank der Kapselung müssen Sie nicht wissen, wie die String
Klasse funktioniert. Sie müssen nur die Methoden der Schnittstelle kennen.

Wenn Sie sich die String
Klasse in Java ansehen, können Sie sehen, wie das char
Array gekapselt ist:
public String(char value[]) { this(value, 0, value.length, null); }
Stellen Sie sich zum besseren Verständnis der Kapselung ein physisches Objekt vor: eine Maschine. Müssen Sie wissen, wie ein Auto unter der Motorhaube funktioniert, um es zu fahren? Natürlich nicht, aber Sie sollten wissen, was die Schnittstellen des Autos bewirken: das Gaspedal, die Bremsen und das Lenkrad. Jede dieser Schnittstellen unterstützt bestimmte Aktionen: Beschleunigen, Bremsen, Linksabbiegen, Rechtsabbiegen. Gleiches gilt für die objektorientierte Programmierung.
Der erste Artikel in der Java Challengers-Reihe befasste sich mit der Methodenüberladung, die in der String
Klasse weit verbreitet ist. Überladen kann Ihre Klassen wirklich flexibel machen:
public String(String original) {} public String(char value[], int offset, int count) {} public String(int[] codePoints, int offset, int count) {} public String(byte bytes[], int offset, int length, String charsetName) {}
Anstatt zu verstehen, wie die String
Klasse funktioniert, hilft Ihnen dieser Artikel zu verstehen, was sie tut und wie Sie sie in Ihrem Code verwenden.
Was ist ein String-Pool?
Die String
Klasse ist wohl die am häufigsten verwendete Klasse in Java. Wenn wir jedes Mal, wenn wir String
, ein neues Objekt im dynamischen Speicher (Speicherheap) erstellen, verschwenden wir viel Speicher. Der String-Pool löst dieses Problem, indem nur ein Objekt für jeden Zeilenwert gespeichert wird.

Linien in einer Reihe Pool
Obwohl wir mehrere String
Variablen mit den Werten Duke
und Juggy
, werden nur zwei Objekte erstellt und im dynamischen Speicher (Heap) gespeichert. Siehe das folgende Codebeispiel zum Beweis. (Denken Sie daran, dass in Java der Operator " ==
" verwendet wird, um zwei Objekte zu vergleichen und festzustellen, ob dasselbe Objekt dasselbe ist oder nicht.)
String juggy = "Juggy"; String anotherJuggy = "Juggy"; System.out.println(juggy == anotherJuggy);
Dieser Code gibt true
da die beiden String
Variablen auf dasselbe Objekt im String-Pool verweisen. Ihre Bedeutungen sind die gleichen.
Die Ausnahme ist der new
Operator.
Schauen Sie sich jetzt diesen Code an - er sieht ähnlich aus wie im vorherigen Beispiel, aber es gibt einen Unterschied.
String duke = new String("duke"); String anotherDuke = new String("duke"); System.out.println(duke == anotherDuke);
Basierend auf dem vorherigen Beispiel könnten Sie denken, dass dieser Code true
, dies ist jedoch nicht der true
. Durch Hinzufügen eines new
Operators wird ein neues String
Objekt im Speicher erstellt. Somit erstellt die JVM zwei verschiedene Objekte.
Native Methoden
Native Methoden in Java sind Methoden, die in der Sprache C kompiliert werden, normalerweise mit dem Ziel, den Speicher zu verwalten und die Leistung zu optimieren.
String-Pools und intern()
-Methode
Zum Speichern von Zeichenfolgen in einem Pool wird eine Methode namens String-Internierung verwendet.
Folgendes erzählt uns Javadoc über die intern()
-Methode:
public native String intern();
Die intern()
-Methode wird zum Speichern von Zeichenfolgen in einem Zeichenfolgenpool verwendet. Zunächst wird überprüft, ob eine bereits erstellte Zeile im Pool vorhanden ist. Wenn nicht, wird eine neue Zeile im Pool erstellt. Die Zeilenpoollogik basiert auf dem Flyweight- Muster.
Beachten Sie nun, was passiert, wenn wir mit new
zwei Zeilen erstellen:
String duke = new String("duke"); String duke2 = new String("duke"); System.out.println(duke == duke2);
Im Gegensatz zum vorherigen Beispiel mit dem new
Schlüsselwort gibt der Vergleich in diesem Fall true
. Dies liegt daran, dass durch die Verwendung der intern()
-Methode sichergestellt wird, dass sich die Zeichenfolge im Pool befindet.
equals
Methode in der String
Klasse
Mit der Methode equals()
wird überprüft, ob zwei Klassen gleich sind oder nicht. Da sich equals()
in der Object
Klasse befindet, erbt jede Java-Klasse diese. Die Methode equals()
muss jedoch überschrieben werden, damit sie ordnungsgemäß funktioniert. String
überschreibt natürlich equals()
.
Schauen Sie mal rein:
public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String aString = (String)anObject; if (coder() == aString.coder()) { return isLatin1() ? StringLatin1.equals(value, aString.value) : StringUTF16.equals(value, aString.value); } } return false; }
Wie Sie sehen können, wird der Wert der String
Klasse durch equals()
und nicht durch eine Objektreferenz verglichen. Es spielt keine Rolle, ob die Verweise auf die Objekte unterschiedlich sind; Bedingungen werden verglichen.
Allgemeine Zeichenfolgenmethoden
Bevor Sie das Problem mit dem Zeichenfolgenvergleich lösen, müssen Sie noch eines wissen.
Betrachten Sie die gängigsten Methoden der String
Klasse:
Lösen Sie das Problem mit dem Zeichenfolgenvergleich
Lassen Sie uns überprüfen, was Sie über die String
Klasse gelernt haben, indem Sie ein kleines Rätsel lösen.
In dieser Aufgabe vergleichen Sie mehrere Zeilen mit den erlernten Konzepten. Können Sie anhand des folgenden Codes den Wert jedes variablen result
bestimmen?
public class ComparisonStringChallenge { public static void main(String... doYourBest) { String result = ""; result += " powerfulCode ".trim() == "powerfulCode" ? "0" : "1"; result += "flexibleCode" == "flexibleCode" ? "2" : "3"; result += new String("doYourBest") == new String("doYourBest") ? "4" : "5"; result += new String("noBugsProject") .equals("noBugsProject") ? "6" : "7"; result += new String("breakYourLimits").intern() == new String("breakYourLimits").intern() ? "8" : "9"; System.out.println(result); } }
Was wird die Schlussfolgerung sein?
- A: 02468
- B: 12469
- C: 12579
- D: 12568
Die richtige Antwort finden Sie am Ende des Artikels.
Was ist jetzt passiert? Grundlegendes zum Verhalten von Zeichenfolgen
In der ersten Zeile sehen wir:
result += " powerfulCode ".trim() == "powerfulCode" ? "0" : "1";
In diesem Fall ist das Ergebnis false
, da beim Entfernen von Leerzeichen mit der Methode trim()
mit dem new
Operator ein neuer String
erstellt wird.
Als nächstes sehen wir:
result += "flexibleCode" == "flexibleCode" ? "2" : "3";
Hier gibt es kein Geheimnis, die Zeilen im Zeilenpool sind gleich. Dieser Vergleich gibt true
.
Dann haben wir:
result += new String("doYourBest") == new String("doYourBest") ? "4" : "5";
Die Verwendung new
Zeilen führt zur Erstellung von zwei neuen Zeilen, und es spielt keine Rolle, ob ihre Werte gleich sind oder nicht. In diesem Fall ist der Vergleich auch dann false
, wenn die Werte gleich sind.
Weiter:
result += new String("noBugsProject") .equals("noBugsProject") ? "6" : "7";
Da wir die Methode equals()
verwendet haben, wird der Wert der Zeichenfolge verglichen, nicht die Instanz des Objekts.
In diesem Fall spielt es keine Rolle, ob verschiedene Objekte vorhanden sind oder nicht, da der Wert verglichen wird. Das Ergebnis ist true
.
Endlich haben wir:
result += new String("breakYourLimits").intern() == new String("breakYourLimits").intern() ? "8" : "9";
Wie Sie bereits gesehen haben, intern()
die intern()
-Methode einen String in einen String-Pool ein. Beide Linien zeigen auf dasselbe Objekt, also in diesem Fall true
.
Häufige Zeichenfolgenfehler
Es kann schwierig sein zu bestimmen, ob zwei Linien auf dasselbe Objekt zeigen oder nicht, insbesondere wenn die Linien denselben Wert enthalten. Es ist nützlich, sich daran zu erinnern, dass die Verwendung von new
immer zur Erstellung eines neuen Objekts im Speicher führt, selbst wenn die Zeichenfolgenwerte gleich sind.
Die Verwendung von String
Methoden zum Vergleichen von Objektreferenzen kann ebenfalls schwierig sein. Die Besonderheit ist, dass, wenn die Methode etwas in der Zeile ändert, unterschiedliche Verweise auf Objekte vorhanden sind.
Einige Beispiele zur Verdeutlichung:
System.out.println("duke".trim() == "duke".trim());
Dieser Vergleich ist wahr, da die trim()
-Methode keine neue Zeile erstellt.
System.out.println(" duke".trim() == "duke".trim());
In diesem Fall generiert die erste trim()
-Methode eine neue Zeile, da die Methode ihre Aufgabe erfüllt und daher die Verknüpfungen unterschiedlich sind.
Wenn trim()
seine Arbeit erledigt, erstellt es schließlich eine neue Zeile:
Woran Sie sich bei Saiten erinnern sollten
Zeilen sind nicht veränderbar, daher kann der Zeilenstatus nicht geändert werden.
Um Speicherplatz zu sparen, speichert die JVM Zeichenfolgen in einem Zeichenfolgenpool. Beim Erstellen einer neuen Zeile überprüft die JVM ihren Wert und zeigt auf ein vorhandenes Objekt. Wenn der Pool keine Zeile mit diesem Wert enthält, erstellt die JVM eine neue Zeile.
Der Operator " ==
" vergleicht Objektreferenzen. Die Methode equals()
vergleicht Zeichenfolgenwerte. Die gleiche Regel gilt für alle Objekte.
Wenn Sie den new
Operator verwenden, wird eine neue Zeile im Heap erstellt (Anmerkung des Übersetzers - es wird im Original geschrieben, dass sie sich im Pool befindet, aber dies ist nicht der Fall , danke zagayevskiy ), selbst wenn es eine Zeile mit demselben Wert gibt.
Die Antwort
Die Antwort auf dieses Problem lautet D. Die Schlussfolgerung lautet 12568.
Fortsetzung folgt...