5.4 Zeichenfolgen
Eine Zeichenfolge ist eine Sammlung von Zeichen (Datentyp char), die die Laufzeitumgebung geordnet im Speicher ablegt. Die Zeichen sind einem Zeichensatz entnommen, der in Java dem 16-Bit-Unicode-Standard entspricht – mit einigen Umwegen ist auch Unicode 4 mit 32-Bit-Zeichen möglich.
Zeichenfolgen lassen sich als char-Arrays aufbauen, doch Arrays sind zu unflexibel. Daher sieht Java drei zentrale Klassen vor, die Zeichenfolgen komfortabel verwalten. Sie unterscheiden sich in zwei Punkten:
Sind die Zeichenfolgen unveränderbar (immutable), dann können sie nach dem Anlegen später nicht mehr verändert werden. Oder sollen die Zeichenfolgen dauerhaft veränderbar (mutable) sein?
Sind die Operationen auf den Zeichenketten gegen nebenläufige Zugriffe aus mehreren Threads abgesichert?
Verwaltet mutable | ||
---|---|---|
Threadsicher | String | StringBuffer |
Nicht threadsicher | – | StringBuilder |
Die Klasse String
Die Klasse String repräsentiert nicht änderbare Zeichenketten (allgemein heißen Objekte, deren Zustand sich nicht verändern lässt, immutable). Daher ist ein String immer threadsicher, denn eine Synchronisation ist nur dann nötig, wenn es Änderungen am Zustand geben kann. Mit Objekten vom Typ String lässt sich nach Zeichen oder Teilzeichenketten suchen, und ein String lässt sich mit einem anderen String vergleichen, aber Zeichen im String können nicht verändert werden. Es gibt einige Methoden, die scheinbar Veränderungen an Strings vornehmen, aber sie erzeugen in Wahrheit neue String-Objekte, die die veränderten Zeichenreihen repräsentieren. So entsteht beim Aneinanderhängen zweier String-Objekte als Ergebnis ein drittes String-Objekt für die zusammengefügte Zeichenreihe.
[zB] Beispiel
String-Objekte selbst lassen sich nicht verändern, aber natürlich lässt sich eine Referenz auf ein anderes String-Objekt setzen:
String s = "tutego";
s = "TUTEGO";
Mit »verändern« meinen wir hier, dass Anweisungen den Zustand eines Objekts modifizieren, etwa indem das erste Zeichen gelöscht wird. Die Referenz umzubiegen, verändert keinen Zustand.
Die Klassen StringBuilder/StringBuffer
Die Klassen StringBuilder und StringBuffer repräsentieren im Gegensatz zu String dynamische, beliebig änderbare Zeichenreihen. Der Unterschied zwischen den API-gleichen Klassen ist lediglich, dass StringBuffer vor nebenläufigen Operationen geschützt ist, StringBuilder nicht. Die Unterscheidung ist bei Strings nicht nötig, denn wenn Objekte nachträglich nicht veränderbar sind, machen parallele Lesezugriffe keine Schwierigkeiten.
Der Basistyp CharSequence für Zeichenketten
CharSequence ist die gemeinsame Schnittstelle von String, StringBuilder und StringBuffer und wird in der Bibliothek mehrfach verwendet. Nehmen wir ein Beispiel: Die Klasse String deklariert eine Methode contains(CharSequence s), die testet, ob der Teil-String s im String vorkommt. Von welchem Typ kann nun die Variable s sein? Wir können Exemplare etwa von String, StringBuilder oder StringBuffer übergeben, weil dies alles CharSequences sind. Wir steigen später noch etwas genauer in den Typ ein. An dieser Stelle reicht es, zu wissen, dass wir uns überall, wo CharSequence steht, String, StringBuilder oder StringBuffer denken können.
Enthält ein String ein char-Array? *
Die drei Klassen String, StringBuilder, StringBuffer entsprechen der idealen Umsetzung der objektorientierten Idee (mit der wir uns in Kapitel 6, »Eigene Klassen schreiben«, intensiv auseinandersetzen): Wie genau die Zeichenfolgen gespeichert werden, dringt nicht nach außen. Interessanterweise ist ab Java 9 ein Wechsel vollzogen worden: Vor Java 9 speicherte immer ein char-Array alle Zeichen eines StringBuilder/StringBuffer/String-Objekts. In Java 9 gibt es eine Option auf eine String-Verdichtung,[ 132 ](Beschrieben in http://openjdk.java.net/jeps/254. ) sodass ein byte-Array verwendet wird, das den String entweder in der Latin-1- oder in der UTF-16-Kodierung enthält, wobei im ausschließlichen Fall der Latin-1-Zeichen ein Byte verwendet wird und so der Speicherbedarf auf die Hälfte sinkt.
Das Schöne ist also, dass die Klassen uns die lästige Arbeit abnehmen, selbst Zeichenfolgen in Arrays zu verwalten, und dass wir von den ganzen internen Optimierungen nichts mitbekommen.