Ich bin ein C ++ - Typ, der Java lernt. Ich lese Effective Java und etwas hat mich verwirrt. Es heißt, niemals Code wie diesen zu schreiben:
String s = new String("silly");
Weil es unnötige String
Objekte schafft . Aber stattdessen sollte es so geschrieben werden:
String s = "No longer silly";
Ok, gut, soweit ... Angesichts dieser Klasse:
public final class CaseInsensitiveString {
private String s;
public CaseInsensitiveString(String s) {
if (s == null) {
throw new NullPointerException();
}
this.s = s;
}
:
:
}
CaseInsensitiveString cis = new CaseInsensitiveString("Polish");
String s = "polish";
Warum ist die erste Aussage in Ordnung? Sollte es nicht sein
CaseInsensitiveString cis = "Polish";
Wie kann ich mich so
CaseInsensitiveString
verhalten,String
dass die obige Aussage in Ordnung ist (mit und ohne ErweiterungString
)? Was ist es an String, das es in Ordnung macht, es einfach so wörtlich zu übergeben? Nach meinem Verständnis gibt es in Java kein "Kopierkonstruktor" -Konzept?
Antworten:
String
ist eine spezielle integrierte Klasse der Sprache. Es ist nur für dieString
Klasse , in der Sie vermeiden sollten, zu sagenWeil das Literal
"Polish"
bereits vom TypString
ist und Sie ein zusätzliches unnötiges Objekt erstellen. Für jede andere Klasse sagenist das Richtige (und in diesem Fall nur).
quelle
new String("foo")
, fragen Sie sich möglicherweise, warum der Konstruktornew String(String)
vorhanden ist. Die Antwort ist, dass es manchmal eine gute Verwendung dafür gibt: stackoverflow.com/a/390854/1442870Ich glaube, der Hauptvorteil der Verwendung der Literalform (dh "foo" anstelle des neuen Strings ("foo")) besteht darin, dass alle String-Literale von der VM "interniert" werden. Mit anderen Worten, es wird einem Pool hinzugefügt, sodass jeder andere Code, der dieselbe Zeichenfolge erstellt, die gepoolte Zeichenfolge verwendet, anstatt eine neue Instanz zu erstellen.
Zur Veranschaulichung wird der folgende Code für die erste Zeile wahr, für die zweite falsch ausgegeben:
quelle
Strings werden in Java etwas speziell behandelt, sie sind unveränderlich, so dass sie sicher durch Referenzzählung behandelt werden können.
Wenn du schreibst
dann beziehen sich s und t tatsächlich auf dasselbe Objekt, und s == t gibt true zurück, denn "==" für gelesene Objekte "ist dasselbe Objekt" (oder kann jedenfalls nicht sicher sein, ob dies Teil davon ist die tatsächliche Sprachspezifikation oder einfach ein Detail der Compiler-Implementierung - daher ist es möglicherweise nicht sicher, sich darauf zu verlassen).
Wenn du schreibst
dann s! = t (weil Sie explizit eine neue Zeichenfolge erstellt haben), obwohl s.equals (t) true zurückgibt (weil string dieses Verhalten zu equals hinzufügt).
Das, was du schreiben willst,
kann nicht funktionieren, weil Sie denken, dass die Zitate eine Art Kurzschlusskonstruktor für Ihr Objekt sind, obwohl dies tatsächlich nur für einfache alte java.lang.Strings funktioniert.
quelle
strA = strB
man stattstrA = new String(strB)
. Es hat wirklich nicht viel mit String-Internierung zu tun.Literal wird in Pool gehen und S1 wird verweisen.
Dieses Mal wird überprüft, ob das "foo" -Literal bereits in StringPool verfügbar ist oder nicht, da es jetzt existiert, sodass s2 auf dasselbe Literal verweist.
Das Literal "foo" wird zuerst in StringPool erstellt, dann über den String-Arg-Konstruktor. Das String-Objekt wird erstellt, dh "foo" im Heap aufgrund der Objekterstellung durch einen neuen Operator, dann verweist s3 darauf.
gleich wie s3
so
System.out.println(s1==s2);// **true** due to literal comparison.
und
System.out.println(s3==s4);// **false** due to object
Vergleich (s3 und s4 werden an verschiedenen Stellen im Heap erstellt)
quelle
String
s sind in Java etwas Besonderes - sie sind unveränderlich und Zeichenfolgenkonstanten werden automatisch inString
Objekte umgewandelt.Es gibt keine Möglichkeit, Ihr
SomeStringClass cis = "value"
Beispiel auf eine andere Klasse anzuwenden.Sie können auch nicht erweitern
String
, da es als deklariert istfinal
, was bedeutet, dass keine Unterklassifizierung zulässig ist.quelle
Java-Strings sind interessant. Es sieht so aus, als hätten die Antworten einige der interessanten Punkte abgedeckt. Hier sind meine zwei Cent.
Zeichenfolgen sind unveränderlich (Sie können sie nie ändern)
Zeichenfolgenvergleiche hängen davon ab, was Sie vergleichen
a1
ist nicht gleicha2
a1
unda2
sind ObjektreferenzenIch denke, Sie sind auf dem falschen Weg, wenn Sie versuchen, die kaseinsensitive Klasse zu verwenden. Lass die Saiten in Ruhe. Was Sie wirklich interessiert, ist, wie Sie die Werte anzeigen oder vergleichen. Verwenden Sie eine andere Klasse, um die Zeichenfolge zu formatieren oder Vergleiche anzustellen.
dh
Da Sie die Klasse bilden, können Sie die Vergleiche so machen, wie Sie es möchten - vergleichen Sie die Textwerte.
quelle
Das kannst du nicht. Dinge in doppelten Anführungszeichen in Java werden vom Compiler speziell als Strings erkannt, und leider können Sie dies nicht überschreiben (oder erweitern
java.lang.String
- es ist deklariertfinal
).quelle
Die beste Möglichkeit, Ihre Frage zu beantworten, besteht darin, sich mit dem "String-Konstantenpool" vertraut zu machen. In Java sind String-Objekte unveränderlich (dh ihre Werte können nach ihrer Initialisierung nicht mehr geändert werden). Wenn Sie also ein String-Objekt bearbeiten, erstellen Sie ein neues bearbeitetes String-Objekt, wobei das alte Objekt nur in einem speziellen Speicherbereich schwebt, der als "String" bezeichnet wird konstanter Pool ". Erstellen eines neuen String-Objekts durch
erstellt nur ein Zeichenfolgenobjekt im Pool und die Referenz s verweist darauf, jedoch mit
Sie erstellen zwei Zeichenfolgenobjekte: eines im Pool und das andere im Heap. Die Referenz bezieht sich auf das Objekt im Heap.
quelle
Vom ersten Punkt an wurde genug gesagt. "Polnisch" ist ein Zeichenfolgenliteral und kann nicht der CaseInsentiviveString-Klasse zugewiesen werden.
Nun zum zweiten Punkt
Obwohl Sie keine neuen Literale erstellen können, können Sie dem ersten Punkt dieses Buches für einen "ähnlichen" Ansatz folgen, sodass die folgenden Aussagen zutreffen:
Hier ist der Code.
// Teste die Klasse mit dem Schlüsselwort "assert"
Erstellen Sie also einen internen Pool von CaseInsensitiveString-Objekten und geben Sie von dort aus die entsprechende Instanz zurück.
Auf diese Weise gibt der Operator "==" true für zwei Objektreferenzen zurück, die denselben Wert darstellen .
Dies ist nützlich, wenn ähnliche Objekte sehr häufig verwendet werden und das Erstellen von Kosten teuer ist.
In der Dokumentation zur Zeichenfolgenklasse wird angegeben, dass die Klasse einen internen Pool verwendet
Die Klasse ist nicht vollständig. Einige interessante Probleme treten auf, wenn wir versuchen, den Inhalt des Objekts bei der Implementierung der CharSequence-Schnittstelle zu durchlaufen. Dieser Code ist jedoch gut genug, um zu zeigen, wie dieses Element im Buch angewendet werden kann.
Es ist wichtig zu beachten, dass bei Verwendung des internalPool- Objekts die Referenzen nicht freigegeben werden und somit kein Müllsammelobjekt ist. Dies kann zu einem Problem werden, wenn viele Objekte erstellt werden.
Es funktioniert für die String-Klasse, da es intensiv genutzt wird und der Pool nur aus "internierten" Objekten besteht.
Es funktioniert auch gut für die Boolesche Klasse, da es nur zwei mögliche Werte gibt.
Und schließlich ist dies auch der Grund, warum valueOf (int) in der Klasse Integer auf -128 bis 127 int-Werte beschränkt ist.
quelle
In Ihrem ersten Beispiel erstellen Sie einen String "albern" und übergeben ihn dann als Parameter an den Kopierkonstruktor eines anderen Strings, wodurch ein zweiter String erstellt wird, der mit dem ersten identisch ist. Da Java-Strings unveränderlich sind (was häufig Menschen sticht, die an C-Strings gewöhnt sind), ist dies eine unnötige Verschwendung von Ressourcen. Sie sollten stattdessen das zweite Beispiel verwenden, da mehrere unnötige Schritte übersprungen werden.
Das String-Literal ist jedoch kein CaseInsensitiveString, sodass Sie in Ihrem letzten Beispiel nicht das tun können, was Sie wollen. Darüber hinaus gibt es keine Möglichkeit, einen Casting-Operator wie in C ++ zu überladen, sodass buchstäblich keine Möglichkeit besteht, das zu tun, was Sie möchten. Sie müssen es stattdessen als Parameter an den Konstruktor Ihrer Klasse übergeben. Natürlich würde ich wahrscheinlich nur String.toLowerCase () verwenden und damit fertig sein.
Außerdem sollte Ihr CaseInsensitiveString die CharSequence-Schnittstelle sowie wahrscheinlich die serialisierbaren und vergleichbaren Schnittstellen implementieren. Wenn Sie Comparable implementieren, sollten Sie natürlich auch equals () und hashCode () überschreiben.
quelle
Nur weil Sie das Wort
String
in Ihrer Klasse haben, bedeutet dies nicht, dass Sie alle Besonderheiten der integriertenString
Klasse erhalten.quelle
CaseInsensitiveString
ist kein a,String
obwohl es a enthältString
. EinString
Literal zB "Beispiel" kann nur einem zugeordnet werdenString
.quelle
CaseInsensitiveString und String sind unterschiedliche Objekte. Sie können nicht tun:
weil "Polish" ein String ist, kein CaseInsensitiveString. Wenn String CaseInsensitiveString String erweitert, sind Sie in Ordnung, aber offensichtlich nicht.
Und mach dir keine Sorgen um die Konstruktion hier, du wirst keine unnötigen Objekte herstellen. Wenn Sie sich den Code des Konstruktors ansehen, wird lediglich ein Verweis auf die übergebene Zeichenfolge gespeichert. Es wird nichts Zusätzliches erstellt.
Im Fall von String s = new String ("foobar") macht es etwas anderes. Sie erstellen zuerst die Literalzeichenfolge "foobar" und dann eine Kopie davon, indem Sie daraus eine neue Zeichenfolge erstellen. Diese Kopie muss nicht erstellt werden.
quelle
wenn sie sagen zu schreiben
anstatt
Sie meinen es beim Erstellen eines String-Objekts, da beide obigen Anweisungen ein String-Objekt erstellen, die neue String () -Version jedoch zwei String-Objekte erstellt: eines im Heap und das andere im String-Konstantenpool. Daher wird mehr Speicher verwendet.
Aber wenn du schreibst
Sie erstellen keinen String, sondern ein Objekt der Klasse CaseInsensitiveString. Daher müssen Sie den neuen Operator verwenden.
quelle
Wenn ich es richtig verstanden habe, bedeutet Ihre Frage, warum wir ein Objekt nicht erstellen können, indem wir ihm direkt einen Wert zuweisen. Wir können es nicht auf eine Wrapper of String-Klasse in Java beschränken.
Um das zu beantworten, würde ich nur sagen, dass rein objektorientierte Programmiersprachen einige Konstrukte haben und dass alle Literale, wenn sie alleine geschrieben werden, direkt in ein Objekt des angegebenen Typs umgewandelt werden können.
Das bedeutet genau, wenn der Interpreter 3 sieht, wird er in ein Integer-Objekt konvertiert, da Integer der für solche Literale definierte Typ ist.
Wenn der Interpreter etwas in einfachen Anführungszeichen wie 'a' sieht, wird direkt ein Objekt vom Typ Zeichen erstellt, Sie müssen es nicht angeben, da die Sprache das Standardobjekt vom Typ Zeichen dafür definiert.
Wenn der Interpreter etwas in "" sieht, wird es als Objekt seines Standardtyps betrachtet, dh als Zeichenfolge. Dies ist ein nativer Code, der im Hintergrund arbeitet.
Dank des MIT-Videovorlesungskurses 6.00 habe ich den Hinweis für diese Antwort erhalten.
quelle
In Java erstellt die Syntax "text" eine Instanz der Klasse java.lang.String. Die Zuordnung:
ist eine einfache Aufgabe, für die kein Kopierkonstruktor erforderlich ist.
Ist illegal, was auch immer Sie tun, da die MyString-Klasse weder java.lang.String noch eine Oberklasse von java.lang.String ist.
quelle
Erstens können Sie keine Klasse erstellen, die sich von String aus erstreckt, da String eine letzte Klasse ist. Und Java verwaltet Strings anders als andere Klassen, sodass Sie dies nur mit String tun können
Aber mit Ihrer Klasse müssen Sie den Konstruktor aufrufen. Dieser Code ist also in Ordnung.
quelle
Ich möchte nur hinzufügen, dass Java Kopierkonstruktoren hat ...
Nun, das ist ein gewöhnlicher Konstruktor mit einem Objekt vom gleichen Typ wie das Argument.
quelle
In den meisten Versionen des JDK sind die beiden Versionen identisch:
String s = neuer String ("albern");
String s = "Nicht mehr albern";
Da Zeichenfolgen unveränderlich sind, verwaltet der Compiler eine Liste von Zeichenfolgenkonstanten. Wenn Sie versuchen, eine neue zu erstellen, wird zunächst überprüft, ob die Zeichenfolge bereits definiert ist. Wenn dies der Fall ist, wird ein Verweis auf die vorhandene unveränderliche Zeichenfolge zurückgegeben.
Zur Verdeutlichung: Wenn Sie "String s =" sagen, definieren Sie eine neue Variable, die Platz auf dem Stapel einnimmt, passiert genau dasselbe, ob Sie "Nicht mehr albern" oder einen neuen String ("albern") sagen - eine neue Eine konstante Zeichenfolge wird in Ihre Anwendung kompiliert und die Referenz zeigt darauf.
Ich sehe den Unterschied hier nicht. Für Ihre eigene Klasse, die nicht unveränderlich ist, ist dieses Verhalten jedoch irrelevant und Sie müssen Ihren Konstruktor aufrufen.
UPDATE: Ich habe mich geirrt! Aufgrund einer Abwärtsabstimmung und eines beigefügten Kommentars habe ich dies getestet und festgestellt, dass mein Verständnis falsch ist - neue Zeichenfolge ("Silly") erstellt tatsächlich eine neue Zeichenfolge, anstatt die vorhandene wiederzuverwenden. Ich bin mir nicht sicher, warum dies so ist (was ist der Vorteil?), Aber Code spricht lauter als Worte!
quelle
String ist eine der speziellen Klassen, in denen Sie sie ohne den neuen Sring-Teil erstellen können
es ist das gleiche wie
int x = y;
oder
char c;
quelle
Es ist ein Grundgesetz, dass Strings in Java unveränderlich sind und zwischen Groß- und Kleinschreibung unterscheiden.
quelle
Sowohl str1 als auch str2 gehören zum selben String-Objekt "foo". B'coz Java verwaltet Strings in StringPool. Wenn also eine neue Variable auf denselben String verweist, wird keine weitere Variable erstellt, sondern dieselbe in StringPool vorhandene Alerady zugewiesen .
Hier gehören sowohl str1 als auch str2 zu verschiedenen Objekten. B'coz new String () erstellt zwangsweise ein neues String-Objekt.
quelle
Java erstellt ein String-Objekt für jedes String-Literal, das Sie in Ihrem Code verwenden. Jedes Mal
""
, wenn es verwendet wird, ist es dasselbe wie ein Anrufnew String()
.Strings sind komplexe Daten, die sich nur wie primitive Daten "verhalten". String-Literale sind tatsächlich Objekte, obwohl wir so tun, als wären sie primitive Literale wie
6, 6.0, 'c',
usw. Das String-Literal"text"
gibt also ein neues String-Objekt mit Wert zurückchar[] value = {'t','e','x','t}
. Deshalb anrufenist eigentlich ähnlich wie anrufen
Hoffentlich können Sie von hier aus sehen, warum Ihr Lehrbuch dies für überflüssig hält.
Als Referenz finden Sie hier die Implementierung von String: http://www.docjar.com/html/api/java/lang/String.java.html
Es macht Spaß zu lesen und könnte zu Einsichten inspirieren. Es ist auch ideal für Anfänger, zu lesen und zu verstehen, da der Code sehr professionellen und konventionskonformen Code zeigt.
Eine weitere gute Referenz ist das Java-Tutorial zu Strings: http://docs.oracle.com/javase/tutorial/java/data/strings.html
quelle