Was die Fragen in dieser Reihe betrifft, sollte jeder, der Java studiert, sie verstehen. Natürlich spielt es keine Rolle, ob Sie Java nur zum Spaß lernen. Wenn Sie der Meinung sind, dass Sie die Anfängerstufe überschritten haben, diese Themen aber nicht gut verstehen, fügen Sie sich bitte dem Anfängerteam hinzu.
Frage 1: Was sage ich?
String s = „Hallo Welt!“;
Viele Leute haben dies getan, aber was genau deklarieren wir? Die Antwort lautet normalerweise: ein String mit dem Inhalt „Hallo Welt!“. Solche vagen Antworten sind oft die Quelle unklarer Konzepte. Wenn Sie eine genaue Antwort geben würden, würde wahrscheinlich die Hälfte der Leute falsch antworten.
Diese Anweisung deklariert einen Verweis auf ein Objekt mit dem Namen „s“, das auf jedes Objekt vom Typ „String“ verweisen kann. Derzeit zeigt es auf das Objekt vom Typ „Hallo Welt!“. Das ist es, was wirklich passiert ist. Wir haben kein String-Objekt deklariert, sondern lediglich eine Referenzvariable, die nur auf das String-Objekt verweisen kann. Wenn Sie also nach der Aussage gerade einen weiteren Satz ausführen:
String string = s;
Wir haben eine weitere Referenz deklariert, die nur auf das String-Objekt verweisen kann, mit dem Namen string. Es wird kein zweites Objekt generiert, das weiterhin auf das ursprüngliche Objekt zeigt, das heißt, es zeigt auf dasselbe Objekt wie s.
Frage 2: Was ist der Unterschied zwischen der Methode „==“ und „equals“?
Der ==-Operator wird speziell verwendet, um die Werte von Variablen auf Gleichheit zu vergleichen. Am einfachsten zu verstehen ist:
Kopieren Sie den Codecode wie folgt:
int a=10;
int b=10;
Dann ist a==b wahr.
Aber was schwer zu verstehen ist, ist:
Kopieren Sie den Codecode wie folgt:
String a=new String("foo");
String b=new String("foo");
Dann gibt a==b false zurück.
Laut dem vorherigen Beitrag sind Objektvariablen tatsächlich Referenzen, und ihre Werte verweisen auf die Speicheradresse, an der sich das Objekt befindet, nicht auf das Objekt selbst. Sowohl a als auch b verwenden den neuen Operator, was bedeutet, dass zwei Zeichenfolgen mit dem Inhalt „foo“ im Speicher generiert werden. Da es „zwei“ gibt, befinden sie sich natürlich an unterschiedlichen Speicheradressen. Die Werte von a und b sind tatsächlich die Werte von zwei verschiedenen Speicheradressen. Wenn Sie also den Operator „==“ verwenden, ist das Ergebnis falsch. Es stimmt, dass die Objekte, auf die a und b zeigen, den Inhalt „foo“ haben und „gleich“ sein sollten, aber der ==-Operator beinhaltet keinen Vergleich der Objektinhalte.
Der Vergleich von Objektinhalten ist genau das, was die Methode „equals“ tut.
Sehen Sie sich an, wie die Methode equal des Object-Objekts implementiert ist:
Kopieren Sie den Codecode wie folgt:
boolescher Wert gleicht(Objekt o){
return this==o;
}
Objektobjekte verwenden standardmäßig den Operator ==. Wenn Ihre selbst erstellte Klasse also die Methode „equals“ nicht überschreibt, erhält Ihre Klasse das gleiche Ergebnis, wenn sie „equals“ und „==“ verwendet. Es ist auch ersichtlich, dass die Methode „equals“ von Object nicht das Ziel erreicht, das die Methode „equals“ erreichen sollte: zu vergleichen, ob der Inhalt zweier Objekte gleich ist. Da die Antwort vom Ersteller der Klasse bestimmt werden sollte, überlässt Object diese Aufgabe dem Ersteller der Klasse.
Schauen Sie sich eine Extremklasse an:
Kopieren Sie den Codecode wie folgt:
Klassenmonster{
privater String-Inhalt;
...
boolean equals(Object another){ return true;}
}
Ich habe die Methode „equals“ überschrieben. Diese Implementierung führt dazu, dass Vergleiche zwischen Monster-Instanzen unabhängig von ihrem Inhalt immer „true“ zurückgeben.
Wenn Sie also die Methode „equals“ verwenden, um festzustellen, ob der Inhalt eines Objekts gleich ist, sollten Sie dies bitte nicht als selbstverständlich betrachten. Weil Sie vielleicht denken, dass sie gleich sind, der Autor dieser Klasse jedoch nicht der Meinung ist und die Implementierung der Methode equal der Klasse von ihm gesteuert wird. Wenn Sie die Methode „equals“ oder eine auf Hash-Code basierende Sammlung (HashSet, HashMap, HashTable) verwenden müssen, überprüfen Sie bitte das Java-Dokument, um zu bestätigen, wie die „equals“-Logik dieser Klasse implementiert ist.
Frage 3: Hat sich String geändert?
NEIN. Da String als unveränderliche Klasse konzipiert ist, sind alle seine Objekte unveränderliche Objekte. Bitte schauen Sie sich den folgenden Code an:
Kopieren Sie den Codecode wie folgt:
String s = "Hallo";
s = s + „Welt!“;
Hat sich das von s angezeigte Ziel geändert? Diese Schlussfolgerung lässt sich leicht aus der Schlussfolgerung im ersten Artikel dieser Serie ableiten. Mal sehen, was passiert ist. In diesem Code zeigte s ursprünglich auf ein String-Objekt mit dem Inhalt „Hallo“, und dann führten wir eine +-Operation für s aus. Hat sich das Objekt, auf das s zeigt, geändert? Die Antwort ist nein. Zu diesem Zeitpunkt zeigt s nicht mehr auf das Originalobjekt, sondern auf ein anderes String-Objekt mit dem Inhalt „Hallo Welt!“. Das Originalobjekt existiert noch im Speicher, aber die Referenzvariable s zeigt nicht mehr darauf.
Aus der obigen Erklärung können wir leicht eine andere Schlussfolgerung ableiten. Wenn Zeichenfolgen häufig auf verschiedene Weise oder unvorhersehbar geändert werden, verursacht die Verwendung von Zeichenfolgen zur Darstellung einer Zeichenfolge einen hohen Speicheraufwand. Da das String-Objekt nach seiner Erstellung nicht mehr geändert werden kann, ist ein String-Objekt erforderlich, um jede einzelne Zeichenfolge darzustellen. Zu diesem Zeitpunkt sollten Sie die Verwendung der StringBuffer-Klasse in Betracht ziehen, die Änderungen ermöglicht, anstatt für jede andere Zeichenfolge ein neues Objekt zu generieren. Darüber hinaus ist der Richtlinienwechsel zwischen diesen beiden Typen sehr einfach.
Gleichzeitig können wir auch wissen, dass Sie nicht jedes Mal einen neuen String erstellen müssen, wenn Sie einen String mit demselben Inhalt verwenden möchten. Wenn wir beispielsweise eine String-Referenzvariable mit dem Namen s im Konstruktor initialisieren und auf den Anfangswert setzen möchten, sollten wir Folgendes tun:
Kopieren Sie den Codecode wie folgt:
Demo der öffentlichen Klasse {
private String s;
…
öffentliche Demo {
s = „Anfangswert“;
}
…
}
Anstelle von s = new String("Initial Value");
Letzterer ruft jedes Mal den Konstruktor auf, um ein neues Objekt zu generieren, was eine geringe Leistung und einen hohen Speicherverbrauch aufweist und bedeutungslos ist. Da das String-Objekt nicht geändert werden kann, kann nur ein String-Objekt zur Darstellung einer Zeichenfolge mit demselben Inhalt verwendet werden . Mit anderen Worten: Wenn Sie den obigen Konstruktor mehrmals aufrufen, um mehrere Ziele zu erstellen, verweisen alle Attribute vom Typ String auf dasselbe Ziel.
Die obige Schlussfolgerung basiert auch auf der Tatsache, dass Guangzhou Java Training bei String-Konstanten bei gleichem Inhalt davon ausgeht, dass sie dasselbe String-Objekt darstellen. Durch den Aufruf des Konstruktors mit dem Schlüsselwort new wird immer ein neues Ziel erstellt, unabhängig davon, ob der Inhalt derselbe ist.
Warum die String-Klasse als unveränderliche Klasse beschrieben werden sollte, hängt von ihrem Zweck ab. Tatsächlich sind nicht nur String, sondern auch viele Klassen in der Java-Standardklassenbibliothek unveränderlich. Bei der Entwicklung eines Systems müssen wir manchmal unveränderliche Klassen beschreiben, um eine Reihe verwandter Werte zu übergeben, was auch ein Ausdruck zielorientierten Denkens ist. Unveränderliche Klassen haben einige Vorteile, da ihr Zweck beispielsweise schreibgeschützt ist, gibt es keine Probleme beim gleichzeitigen Zugriff durch mehrere Threads. Natürlich gibt es auch einige Mängel. Beispielsweise benötigt jede Situation ein Objekt, um sie darzustellen, was zu Funktionsproblemen führen kann. Daher bietet die Java-Standardklassenbibliothek auch eine variable Version, nämlich StringBuffer.