Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

Inhaltsverzeichnis
Vorwort
1 Java ist auch eine Sprache
2 Sprachbeschreibung
3 Klassen und Objekte
4 Der Umgang mit Zeichenketten
5 Mathematisches
6 Eigene Klassen schreiben
7 Angewandte Objektorientierung
8 Exceptions
9 Generics, innere Klassen
10 Die Klassenbibliothek
11 Threads und nebenläufige Programmierung
12 Datenstrukturen und Algorithmen
13 Raum und Zeit
14 Dateien und Datenströme
15 Die eXtensible Markup Language (XML)
16 Grafische Oberflächen mit Swing
17 Grafikprogrammierung
18 Netzwerkprogrammierung
19 Verteilte Programmierung mit RMI und Web–Services
20 JavaServer Pages und Servlets
21 Applets
22 Midlets und die Java ME
23 Datenbankmanagement mit JDBC
24 Reflection und Annotationen
25 Logging und Monitoring
26 Sicherheitskonzepte
27 Java Native Interface (JNI)
28 Dienstprogramme für die Java-Umgebung
Stichwort

Download:
- ZIP, ca. 14,1 MB
Buch bestellen
Ihre Meinung?

Spacer
<< zurück
Java ist auch eine Insel (8. Auflage) von Christian Ullenboom
Programmieren mit der Java Standard Edition Version 6
Buch: Java ist auch eine Insel (8. Auflage)

Java ist auch eine Insel (8. Aufl.)
8., aktual. Auflage, geb., mit DVD
1.475 S., 49,90 Euro
Galileo Computing
ISBN 978-3-8362-1371-4
Pfeil 4 Der Umgang mit Zeichenketten
Pfeil 4.1 Einzelne Zeichen mit der Character-Klasse behandeln
Pfeil 4.2 Strings und deren Anwendung
Pfeil 4.2.1 String-Literale als String-Objekte für konstante Zeichenketten
Pfeil 4.2.2 String-Länge und Test auf Leerstring
Pfeil 4.2.3 Nach enthaltenen Zeichen und Zeichenfolgen suchen
Pfeil 4.2.4 Gut, dass wir verglichen haben
Pfeil 4.2.5 String-Teile extrahieren
Pfeil 4.2.6 Strings anhängen, Groß-/Kleinschreibung und Leerraum
Pfeil 4.2.7 Suchen und ersetzen
Pfeil 4.2.8 String-Objekte mit Konstruktoren neu anlegen
Pfeil 4.3 Konvertieren zwischen Primitiven und Strings
Pfeil 4.3.1 Unterschiedliche Typen in String-Repräsentationen konvertieren
Pfeil 4.3.2 String in primitives Element konvertieren
Pfeil 4.4 Veränderbare Zeichenketten mit StringBuilder/StringBuffer
Pfeil 4.4.1 Anlegen von StringBuilder-/StringBuffer-Objekten
Pfeil 4.4.2 Die Länge eines StringBuilder-/StringBuffer-Objekts
Pfeil 4.4.3 Daten anhängen
Pfeil 4.4.4 Zeichen(folgen) setzen, erfragen, löschen und umdrehen
Pfeil 4.4.5 Vergleichen von String/StringBuilder/StringBuffer
Pfeil 4.4.6 hashCode() bei StringBuffer/StringBuilder
Pfeil 4.5 Sprachabhängiges Vergleichen und Normalisierung
Pfeil 4.5.1 Die Klasse Collator
Pfeil 4.5.2 Effiziente interne Speicherung für die Sortierung
Pfeil 4.5.3 Normalisierung
Pfeil 4.6 Reguläre Ausdrücke
Pfeil 4.6.1 Arbeiten mit der Fassade: String.matches()
Pfeil 4.6.2 Die Klassen Pattern und Matcher
Pfeil 4.6.3 Quantifizierer und Wiederholungen
Pfeil 4.6.4 Finden und nicht matchen
Pfeil 4.6.5 Gierige und nicht gierige Operatoren
Pfeil 4.6.6 Mit MatchResult alle Ergebnisse einsammeln
Pfeil 4.7 Zerlegen von Zeichenketten
Pfeil 4.7.1 Splitten von Zeichenketten mit split()
Pfeil 4.7.2 Die Klasse Scanner
Pfeil 4.7.3 StringTokenizer
Pfeil 4.7.4 BreakIterator als Zeichen-, Wort-, Zeilen- und Satztrenner
Pfeil 4.8 Zeichenkodierungen und Base64
Pfeil 4.8.1 Über die Klasse String Kodierungen vornehmen
Pfeil 4.8.2 Konvertieren mit OutputStreamWriter-Klassen
Pfeil 4.8.3 Das Paket java.nio.charset
Pfeil 4.8.4 Base64-Kodierung
Pfeil 4.9 Formatieren von Ausgaben
Pfeil 4.9.1 Formatieren mit format() aus String
Pfeil 4.9.2 Die Format-Klassen im Überblick
Pfeil 4.9.3 Zahlen, Prozente und Währungen mit NumberFormat und DecimalFormat formatieren
Pfeil 4.10 Zum Weiterlesen


Galileo Computing - Zum Seitenanfang

4.2 Strings und deren Anwendung Zur nächsten ÜberschriftZur vorigen Überschrift

Ein String 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.

Java sieht drei Klassen vor, die Zeichenfolgen verwalten. Sie unterscheiden sich in drei Punkten: Sind die Zeichenfolgen unveränderbar (immutable) oder veränderbar (mutable) und sind die Operationen auf den Zeichenketten gegen nebenläufige Zugriffe aus mehreren Threads abgesichert.


Verwaltet immutable Zeichenketten

Verwaltet mutable Zeichenketten

Thread-sicher

String

StringBuffer

Nicht Thread-sicher

-

StringBuilder


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 thread-sicher, denn eine Synchronisation ist nur dann nötig, wenn es Änderungen geben kann. Mit Objekten vom Typ String lässt sich nach Zeichen oder Teilzeichenketten suchen, 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.

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, während das auf StringBuilder nicht zutrifft. Die Unterscheidung ist bei String nicht nötig, denn wenn Objekte nachträglich nicht veränderbar sind, machen parallele Lesezugriffe keine Schwierigkeiten.


Beispiel String-Objekte selbst lassen sich nicht verändern, aber natürlich lässt sich eine Referenz auf ein anderes String-Objekt legen:

String s = "tutego"; 
s = "TUTEGO";

Die Klassen entsprechen der idealen Umsetzung der objektorientierten Idee (mit der wir uns in Kapitel 6, »Eigene Klassen schreiben«, intensiv auseinandersetzen): Die Daten sind gekapselt, das heißt, die tatsächliche Zeichenkette ist in der Klasse als privates char-Array gegen Zugriffe von außen gesichert, und selbst die Länge ist ein privates Attribut der Klasse, die nur über eine Methode zugänglich ist. Die Klassen nehmen uns also die lästige Arbeit ab, selbst Zeichenfolgen in Feldern verwalten zu müssen.

String: Eine Klasse mit bevorzugter Behandlung

Die Entwickler von Java haben eine Symbiose zwischen dem String als Objekt und dem String als eingebauten Datentyp vorgenommen. Die Sprache ermöglicht zwar die direkte Konstruktion von String-Objekten etwa aus String-Literalen (Zeichenketten in doppelten Anführungszeichen) und die Konkatenation (Aneinanderreihung von Strings mit +) von mehreren Strings, doch intern ist die Aneinanderreihung über Methoden der Klassen String, StringBuilder beziehungsweise StringBuffer realisiert. Mit dem Plus auf String-Objekten ist also ein besonderer Operator auf der Klasse String definiert, der nicht eigenständig auf anderen Klassen definiert werden kann. Java unterstützt keine überladenen Operatoren für Klassen, und dieses Plus ist ein Abweichler.


Galileo Computing - Zum Seitenanfang

4.2.1 String-Literale als String-Objekte für konstante Zeichenketten Zur nächsten ÜberschriftZur vorigen Überschrift

Damit wir Zeichenketten nutzen können, muss ein Objekt der Klasse String oder StringBuilder/StringBuffer erzeugt worden sein. Nutzen wir String-Literale, so müssen wir die String-Objekte nicht von Hand mit new erzeugen, denn ein Ausdruck in doppelten Anführungszeichen ist schon automatisch ein String-Objekt. Das bedeutet auch, dass hinter dem String-Literal gleich ein Punkt für den Methodenaufruf stehen kann.


Beispiel "Hi Chris".length() liefert die Länge der Zeichenkette. Das Ergebnis ist 8. Leerzeichen und Sonderzeichen zählen mit.



Galileo Computing - Zum Seitenanfang

4.2.2 String-Länge und Test auf Leerstring Zur nächsten ÜberschriftZur vorigen Überschrift

String-Objekte verwalten intern die Zeichenreihe, die sie repräsentieren, und bieten eine Vielzahl von Methoden, um die Eigenschaften des Objekts preiszugeben. Eine Methode haben wir schon benutzt: length(). Für String-Objekte ist diese so implementiert, dass die Anzahl der Zeichen im String (die Länge des Strings) zurückgegeben wird. Um herauszufinden, ob der String keine Zeichen hat, lässt sich neben length() == 0 auch die Methode isEmpty() nutzen.

Listing 4.2 LengthAndEmptyDemo.java, main()

System.out.println( "".length() );   // 0 
System.out.println( "".isEmpty() );  // true 
System.out.println( " ".length() );  // 1 
System.out.println( " ".isEmpty() ); // false 
String s = null; 
System.out.println( s.length() );    // Exception in thread "main"  
                                     // java.lang.NullPointerException 
System.out.println( s.isEmpty() );   // Exception in thread "main"  
                                     // java.lang.NullPointerException

Hinweis Während das .NET-Framework etwa eine statische Funktion IsNullOrEmpty(String) anbietet, um zu testen, ob die übergebene Referenz null oder die Zeichenkette leer ist, so muss das in Java getrennt getestet werden. Hier ist eine eigene Utility-Funktion praktisch:

Listing 4.3 LengthAndEmptyDemo.java, isNullOrEmpty()

/** 
 * Checks if a String is {@code null} or empty ({@code ""}). 
 * 
 * <pre> 
 * StringUtils.isEmpty(null) == true 
 * StringUtils.isEmpty(&quot;&quot;) == true 
 * StringUtils.isEmpty(&quot; &quot;) == false 
 * StringUtils.isEmpty(&quot;chris&quot;) == false 
 * StringUtils.isEmpty(&quot; chris &quot;) == false 
 * </pre> 
 *
 * @param str The String to check, may be {@code null}. 
 * @return {@code true} if the String is empty or {@code null}, Zeilen-Umbruch 
           {@code false} otherwise. 
 */ 
public static boolean isNullOrEmpty( String str ) 
{ 
  return str == null || str.length() == 0; 
}
Ob der String nur aus Leerzeichen besteht, testet die Funktion nicht.



Galileo Computing - Zum Seitenanfang

4.2.3 Nach enthaltenen Zeichen und Zeichenfolgen suchen Zur nächsten ÜberschriftZur vorigen Überschrift

Die Objektmethode contains(CharSequence) testet, ob ein Teilstring (engl. substring) in der Zeichenkette vorkommt, und liefert true, wenn das der Fall ist. CharSequence ist unter anderem die gemeinsame Schnittstelle von String, StringBuilder und StringBuffer.


Beispiel Wenn im String s das Spam-Wort »Viagra« vorkommt, gehe in den Rumpf:

if ( s.contains( "Viagra" ) ) 
  ...

Fundstelle mit indexOf() zurückgeben

Die Methode contains() ist nicht mit einem char überladen, kann also nicht nach einem einzelnen Zeichen suchen, es sei denn, der String bestünde nur aus einem Zeichen. Dazu ist indexOf() in der Lage: Die Methode liefert die Fundstelle eines Zeichens beziehungsweise Teilstrings. Findet indexOf() nichts, liefert sie –1.


Beispiel Ein Zeichen mit indexOf() suchen:

String s = "Ernest Gräfenberg"; 
int index1 = s.indexOf( 'e' );                     // 3 
int index2 = s.indexOf( 'e', index1 + 1 );         // 11

Die Belegung von index1 ist 3, da an der Position 3 das erste Mal ein »e« vorkommt. Die zweite Methode indexOf() sucht mit dem zweiten Ausdruck index1 + 1 ab der Stelle 4 weiter. Das Resultat ist 11.


Wie contains() unterscheidet die Suche zwischen Groß- und Kleinschreibung. Die Zeichen in einem String sind wie Array-Elemente ab 0 durchnummeriert. Ist der Index kleiner 0, so wird dies ignoriert und automatisch auf 0 gesetzt.


Beispiel Beschreibt das Zeichen c ein Escape-Zeichen, etwa einen Tabulator oder ein Return, dann soll die Bearbeitung weitergeführt werden.

if ( "\b\t\n\f\r\"\\".indexOf(c) >= 0 ) { 
  ... 
}

contains() konnten wir nicht verwenden, da der Parametertyp nur CharSequence, aber kein char ist.


Die indexOf()-Methode ist nicht nur mit char parametrisiert, sondern auch mit String (CharSequence wäre vielleicht sinnvoller, da kämen dann etwa noch StringBuilder/StringBuffer dazu).


Beispiel indexOf() mit der Suche nach einem Teilstring:

String str = "In Deutschland gibt es immer noch ein Ruhrgebiet, " + 
             "obwohl es diese Krankheit schon lange nicht mehr geben soll."; 
String s = "es"; 
int index = str.indexOf( s, str.indexOf(s) + 1 );       // 57

Die nächste Suchposition wird ausgehend von der alten Finderposition errechnet. Das Ergebnis ist 57, da dort zum zweiten Mal das Wort »es« auftaucht.


Vom Ende an suchen

Genauso wie am Anfang gesucht werden kann, ist es auch möglich, am Ende zu beginnen.


Beispiel Hierzu dient die Methode lastIndexOf():

String str = "May the Force be with you."; 
int index  = str.lastIndexOf( 'o' );                 // 23

Genauso wie bei indexOf() existiert eine überladene Version, die rückwärts ab einer bestimmten Stelle nach dem nächsten Vorkommen von »a« sucht. Wir schreiben:

index = str.lastIndexOf( 'o', index – 1 );          // 9

Die Parameter der char-orientierten Methoden indexOf() und lastIndexOf() sind alle vom Typ int und nicht, wie man spontan erwarten könnte, vom Typ char und int. Das zu suchende Zeichen wird als erstes int-Argument übergeben. Die Umwandlung des char in ein int nimmt der Java-Compiler automatisch vor, sodass dies nicht weiter auffällt. Bedauerlicherweise kann es dadurch aber zu Verwechslungen bei der Reihenfolge der Argumente kommen: Bei s.indexOf(start, c) wird der erste Parameter start als Zeichen interpretiert und das gewünschte Zeichen c als Startposition der Suche.

Anzahl Teilstrings einer Zeichenkette

Bisher bietet die Java-Bibliothek keine direkte Funktion, um die Anzahl Teilstrings einer Zeichenkette herauszufinden. Eine solche Funktion ist jedoch schnell geschrieben:

Listing 4.4 CountMatches.java, frequency()

static int frequency( String source, String part ) 
{ 
  if ( source == null || source.isEmpty() || part == null || part.isEmpty() ) 
    return 0; 
 
  int count = 0; 
 
  for ( int pos = 0; (pos = source.indexOf( part, pos )) != –1; count++ ) 
    pos += part.length(); 
 
  return count; 
}

Galileo Computing - Zum Seitenanfang

4.2.4 Gut, dass wir verglichen haben Zur nächsten ÜberschriftZur vorigen Überschrift

Um Strings zu vergleichen, gibt es viele Möglichkeiten und Optionen. Zunächst sind da equals() für absolute Übereinstimmung und equalsIgnoreCase() für einen Vergleich, der unabhängig von der Groß-/Kleinschreibung ist. Ob ein String mit einem Wort beginnt oder endet, sagen startsWith() und endsWith(). Zum Vergleichen von Teilen gibt es regionMatches(), eine Methode, die auch unabhängig von der Groß-/Kleinschreibung arbeiten kann.

equals()

Als Erstes gibt es die aus der Klasse Object geerbte und in der Klasse String überschriebene Methode equals(). Die Methode gibt true zurück, falls die Strings gleich lang sind und Zeichen für Zeichen übereinstimmen.


Hinweis Während die allermeisten Skript-Sprachen und auch C# Zeichenkettenvergleiche mit == erlauben, ist die Semantik für Java immer eindeutig: Der Vergleich mit == ist nur dann wahr, wenn die beiden Referenzen gleich sind, also zwei String-Objekte identisch sind; die Gleichheit reicht nicht aus.


Dazu ein Beispiel. Bei dem Vergleich mit == ist das Ergebnis ein anderer als mit equals().

Listing 4.5 EqualsDemo.java, main()

String input = javax.swing.JOptionPane.showInputDialog( "Passwort" ); 
System.out.println( input == "heinzelmann" );              //    (1) 
System.out.println( input.equals( "heinzelmann" ) );       //    (2.1) 
System.out.println( "heinzelmann".equals( input ) );       //    (2.2)

Unter der Annahme, dass input die Zeichenketten »heinzelmann« referenziert, ergibt der Vergleich (1) über == den Wert false, da das von showInputDialog() gelieferte String-Objekt ein ganz anderes ist als das, was uns die virtuelle Maschine für den Test bereitstellt (später dazu mehr). Nur der equals()-Vergleich (2.1) und (2.2) ist hier korrekt, da hier die puren Zeichen verglichen werden, und die sind dann gleich.

Grundsätzlich sind Variante (2.1) und (2.2) gleich, da equals() symmetrisch ist. Doch gibt es einen Vorteil bei (2.2), denn da kann input auch null sein, und es gibt nicht wie bei (2.1) eine NullPointerException.

equalsIgnoreCase()

equals() beachtet beim Vergleich die Groß- und Kleinschreibung. Mit equalsIgnoreCase() bietet die Java-Bibliothek eine zusätzliche Methode, um Zeichenketten ohne Beachtung der Groß-/Kleinschreibung zu vergleichen.


Beispiel equals() liefert für result1 den Wert false, und equalsIgnoreCase() liefert für result2 den Wert true:

String str = "REISEPASS"; 
boolean result1 = str.equals( "Reisepass" );             // false 
boolean result2 = str.equalsIgnoreCase( "ReISePaSs" );   // true

Lexikografische Vergleiche mit Größer/kleiner-Relation

Wie equals() und equalsIgnoreCase() vergleichen auch die Methoden compareTo(String) und compareToIgnoreCase(String) den aktuellen String mit einem anderen String. Nur ist der Rückgabewert von compareTo() kein boolean, sondern ein int. Das Ergebnis signalisiert, ob das Argument lexikografisch kleiner oder größer als das String-Objekt ist beziehungsweise mit diesem übereinstimmt. Das ist zum Beispiel in einer Sortierfunktion wichtig. Der Sortieralgorithmus muss beim Vergleich zweier Strings wissen, wie sie einzusortieren sind.


Beispiel Drei Strings in ihrer lexikografischen Ordnung. Alle Vergleiche ergeben true.

System.out.println( "Justus".compareTo( "Bob" )    > 0 ); 
System.out.println( "Justus".compareTo( "Justus" ) == 0 ); 
System.out.println( "Justus".compareTo( "Peter" )  < 0 );

Da im ersten Fall »Justus» lexikografisch größer ist als »Bob«, ist die numerische Rückgabe der Methode compareTo() größer 0.


Der von compareTo() vorgenommene Vergleich basiert nur auf der internen numerischen Kodierung der Unicode-Zeichen. Dabei berücksichtigt compareTo() nicht die landestypischen Besonderheiten, etwa die übliche Behandlung der deutschen Umlaute. Dafür müssten wir Collator-Klassen nutzen, die später in diesem Kapitel vorgestellt werden.

compareToIgnoreCase() ist vergleichbar mit equalsIgnoreCase(), bei der die Groß-/Kleinschreibung keine Rolle spielt. Sun implementiert das mit einem Comparator, der zwei beliebige Objekte – für Zeichenketten natürlich vom Typ String – in eine Reihenfolge bringt. Der Vergleicher ist auch für uns zugänglich.

Endet der String mit ..., beginnt er mit ...?

Interessiert uns, ob der String mit einer bestimmten Zeichenfolge beginnt (wir wollen dies »Präfix« nennen), so rufen wir die startsWith()-Methode auf. Eine ähnliche Methode gibt es für Suffixe: endsWith(). Sie überprüft, ob ein String mit einer Zeichenfolge am Ende übereinstimmt.


Beispiel Die Methode endsWith() ist praktisch zum Testen von Dateinamenendungen, startsWith() für URL-Protokolle:

String  url    = "www.tutego.com"; 
boolean isCom  = url.endsWith( ".com" );                   // true 
boolean isHttp = "http://tutego.com".startsWith( "http" ); // true

String-Teile mit regionMatches() vergleichen

Eine Erweiterung der Ganz-oder-gar-nicht-Vergleichsmethoden bietet regionMatches(), die Teile einer Zeichenkette mit Teilen einer anderen vergleicht. Nimmt das erste Argument von regionMatches() den Wahrheitswert true an, dann spielt die Groß-/Kleinschreibung keine Rolle – damit lässt sich dann auch ein startsWith() und endsWith() mit Vergleichen unabhängig von Groß-/Kleinschreibung durchführen. Der Rückgabewert ist wie bei equalsXXX() ein boolean.


Beispiel Der Aufruf von regionMatches() ergibt true.

String s = "Deutsche Kinder sind zu dick"; 
boolean b = s.regionMatches( 9, "Bewegungsarmut bei Kindern", 19, 6 );

Die Methode beginnt den Vergleich am neunten Zeichen, also bei »K« im String s, und dem 19. Buchstaben in dem Vergleichsstring, ebenfalls ein »K«. Dabei beginnt die Zählung der Zeichen wieder bei 0. Ab diesen beiden Positionen werden sechs Zeichen verglichen. Im Beispiel ergibt der Vergleich von »Kinder« und »Kinder« dann true.


Beispiel Sollte der Vergleich unabhängig von der Groß-/Kleinschreibung stattfinden, ist das erste Argument der überladenen Methode true:

String s = "Deutsche KINDER sind zu dick"; 
boolean b = s.regionMatches( true, 9, "Bewegungsarmut bei kindern", 19, 6 );


Galileo Computing - Zum Seitenanfang

4.2.5 String-Teile extrahieren Zur nächsten ÜberschriftZur vorigen Überschrift

Die vielleicht wichtigste Methode der Klasse String ist charAt(int index). Diese Methode liefert das entsprechende Zeichen an einer Stelle, die »Index« genannt wird. Dies bietet eine Möglichkeit, die Zeichen eines Strings (zusammen mit der Methode length()) zu durchlaufen. Ist der Index kleiner null oder größer beziehungsweise gleich der Anzahl der Zeichen im String, so löst die Methode eine StringIndexOutOfBoundsException [Mit 31 Zeichen gehört dieser Methodenname schon zu den längsten. Übertroffen wird er aber noch um 5 Zeichen von TransformerFactoryConfigurationError. Im Spring-Paket gibt es aber JdbcUpdateAffectedIncorrectNumberOfRowsException – auch nicht von schlechten Eltern.] mit der Fehlerstelle aus.


Beispiel Liefere das erste und letzte Zeichen im String s:

String s = "Ich bin nicht dick! Ich habe nur weiche Formen."; 
char first = s.charAt( 0 );                          // 'I' 
char last  = s.charAt( s.length() – 1 );             // '.'

Wir müssen bedenken, dass die Zählung wieder bei 0 beginnt. Daher müssen wir von der Länge des Strings eine Stelle abziehen. Da der Vergleich auf den korrekten Bereich bei jedem Zugriff auf charAt() stattfindet, ist zu überlegen, ob der String bei mehrmaligem Zugriff nicht stattdessen einmalig in ein eigenes Zeichen-Array kopiert werden sollte.

Teile eines Strings als String mit substring()

Wollen wir einen Teilstring aus der Zeichenkette erfragen, so greifen wir zur Methode substring(). Sie existiert in zwei Varianten – beide liefern ein neues String-Objekt zurück, das dem gewünschten Ausschnitt des Originals entspricht.


Beispiel substring(int) liefert eine Teilzeichenkette ab einem Index bis zum Ende. Das Ergebnis ist ein neues String-Objekt.

String s1 = "Infiltration durch Penetration"; 
//  Position                    19 
String s2 = s1.substring( 19 );                  // Penetration

Der Index von substring(int) gibt die Startposition (null-basiert) an, ab der Zeichen in die neue Teilzeichenkette kopiert werden. substring(int) liefert den Teil von diesem Zeichen bis zum Ende des ursprünglichen Strings – es ergibt s.substring(0) gleich s.

Wollten wir die Teilzeichenkette genauer spezifizieren, so nutzen wir die zweite Variante, substring(int,int). Ihre Argumente geben den Anfang und das Ende des gewünschten Ausschnitts an:


Beispiel Schneide einen Teil des Strings aus.

String träne = "'Jede Träne kitzelt auch die Wange.'"; 
//              0     6    11 
System.out.println( träne.substring( 6, 11 ) ); // Träne

Während die Startposition inklusiv ist, ist die Endposition exklusiv. Das heißt, bei der Endposition gehört das Zeichen nicht mehr zur Teilzeichenkette.


Die Methode substring(int) ist nichts anderes als eine Spezialisierung von substring(int, int), denn die erste Variante mit dem Startindex lässt sich auch als s.substring(beginIndex, s.length()) schreiben.

Selbstverständlich kommen nun diverse Indexüberprüfungen hinzu – eine StringIndexOutOfBoundsException meldet fehlerhafte Positionsangaben wie bei charAt().

Mit getChars() Zeichenfolgen als Array aus dem String extrahieren

Während charAt() nur ein Zeichen liefert, kopiert getChars() mehrere Zeichen aus einem angegebenen Bereich des Strings in ein übergebenes Feld.


Beispiel Kopiere Teile des Strings in ein Feld:

String s = "Blasiussegen"; 
char[] chars = new char[ 5 ]; 
int srcBegin = 7; 
s.getChars( srcBegin, srcBegin + 5, chars, 0 ); 
System.out.println( new String(chars) );  // segen

s.getChars() kopiert ab Position 7 aus dem String s 5 Zeichen in die Elemente des Arrays chars. Das erste Zeichen aus dem Ausschnitt steht dann in chars[0].


Die Methode getChars() muss natürlich wieder testen, ob die gegebenen Argumente im grünen Bereich liegen, das heißt, ob der Startwert nicht < 0 ist und ob der Endwert nicht über die Größe des Strings hinausgeht. Passt das nicht, löst die Methode eine StringIndexOutOfBoundsException aus. Liegt zudem der Startwert hinter dem Endwert, gibt es ebenfalls eine StringIndexOutOfBoundsException, die anzeigt, wie groß die Differenz der Positionen ist. Am besten ist es, die Endposition aus der Startposition zu berechnen, wie es im obigen Beispiel geschehen ist. Passen alle Zeichen in das Feld, kopiert die Implementierung der Methode getChars() mittels System.arraycopy() die Zeichen aus dem internen Array des String-Objekts in das von uns angegebene Ziel.

Möchten wir den kompletten Inhalt eines Strings als ein Array von Zeichen haben, so können wir die Methode toCharArray() verwenden. Intern arbeitet die Methode auch mit getChars(). Als Ziel-Array legt toCharArray() nur ein neues Array an, das wir dann zurückbekommen.


Galileo Computing - Zum Seitenanfang

4.2.6 Strings anhängen, Groß-/Kleinschreibung und Leerraum Zur nächsten ÜberschriftZur vorigen Überschrift

Obwohl String-Objekte selbst unveränderlich sind, bietet die Klasse String Methoden an, die aus einer Zeichenkette Teile herausnehmen oder ihr Teile hinzufügen. Diese Änderungen werden natürlich nicht am String-Objekt vorgenommen, sondern die Methode liefert eine Referenz auf ein neues String-Objekt mit verändertem Inhalt zurück.

Anhängen an Strings

Eine weitere Methode erlaubt das Anhängen von Teilen an einen String. Wir haben dies schon öfters mit dem Plus-Operator realisiert. Die Methode der String-Klasse dazu heißt concat(String). Wir werden später sehen, dass es die StringBuilder-/StringBuffer-Klassen noch weiter treiben und eine überladene Methode append() mit der gleichen Funktionalität anbieten. Das steckt auch hinter dem Plus-Operator. Der Compiler wandelt dies automatisch in eine Kette von append()-Aufrufen um.


Beispiel Hänge das aktuelle Tagesdatum hinter eine Zeichenkette:

String s1 = "Das aktuelle Datum ist: "; 
String s2 = new Date().toString(); 
String s3 = s1.concat( s2 ); // Das aktuelle Datum ist: Tue Jun 05 14:46:41 CEST 2005

Die concat()-Methode arbeitet relativ zügig und effizienter als der Plus-Operator, der einen temporären String-Puffer anlegt. Doch mit dem Plus-Operator ist es hübscher anzusehen. (Aber wie das so ist: Sieht nett aus, aber ...)


Beispiel Ähnlich wie im oberen Beispiel können wir Folgendes schreiben:

String s3 = "Das aktuelle Datum ist: " + new Date().toString();

Es geht sogar noch kürzer, denn der Plus-Operator ruft automatisch toString() bei Objekten auf:

String s3 = "Das aktuelle Datum ist: " + new Date();

concat() legt ein internes Feld an, kopiert die beiden Zeichenreihen per getChars() hinein und liefert mit einem String-Konstruktor die resultierende Zeichenkette.

Groß-/Kleinschreibung

Die Klasse Character deklariert einige statische Funktionen, um einzelne Zeichen in Groß-/Kleinbuchstaben umzuwandeln. Die Schleife, die das für jedes Zeichen übernimmt, können wir uns sparen, denn dazu gibt es die Methoden toUpperCase() und toLowerCase() in der Klasse String. Interessant ist an beiden Methoden, dass sie einige sprachabhängige Feinheiten beachten. So zum Beispiel, dass es im Deutschen nicht wirklich ein großes »ß« gibt, denn »ß« wird zu »SS«. Gammelige Textverarbeitungen bekommen das manchmal nicht auf die Reihe, und im Inhaltsverzeichnis steht dann so etwas wie »SPAß IN DER NAßZELLE«. Aber bei möglichen Missverständnissen müsste »ß« auch zu »SZ« werden, vergleiche »SPASS IN MASZEN« mit »SPASS IN MASSEN« (ein ähnliches Beispiel steht im Duden). Diese Umwandlung ist aber nur von Klein nach Groß von Bedeutung. Für beide Konvertierungsrichtungen gibt es jedoch im Türkischen Spezialfälle, bei denen die Zuordnung zwischen Groß- und Kleinbuchstaben von der Festlegung in anderen Sprachen abweicht.


Beispiel Konvertierung von Groß- in Kleinbuchstaben und umgekehrt:

String s1 = "Spaß in der Naßzelle."; 
String s2 = s1.toLowerCase().toUpperCase();       // SPASS IN DER NASSZELLE. 
System.out.println( s2.length() – s1.length() );  // 2

Das Beispiel dient zugleich als Warnung, dass sich im Fall von »ß« die Länge der Zeichenkette vergrößert. Das kann zu Problemen führen, wenn vorher Speicherplatz bereitgestellt wurde. Dann könnte die neue Zeichenkette nicht mehr in den Speicherbereich passen. Arbeiten wir nur mit String-Objekten, haben wir dieses Problem glücklicherweise nicht. Aber berechnen wir etwa für einen Texteditor die Darstellungsbreite einer Zeichenkette in Pixel auf diese Weise, dann sind Fehler vorprogrammiert.

Um länderspezifische Besonderheiten zu berücksichtigen, lassen sich die toXXXCase()-Methoden zusätzlich mit einem Locale-Objekt füttern. (Wir gehen in Kapitel 10, »Die Klassenbibliothek«, auf Sprachumgebungen und die Klasse Locale ein.) Die parameterlosen Methoden wählen die Sprachumgebung gemäß den Länder-Einstellungen des Betriebssystems:

public String toLowerCase() { 
  return toLowerCase( Locale.getDefault() ); 
}

Ähnliches steht bei toUpperCase().

Leerraum entfernen

In einer Benutzereingabe oder Konfigurationsdatei steht nicht selten vor oder hinter dem wichtigen Teil eines Texts Leerraum wie Leerzeichen oder Tabulatoren. Vor der Bearbeitung sollten sie entfernt werden. Die String-Klasse bietet dazu trim() an.


Beispiel Entferne Leer- und ähnliche Füllzeichen am Anfang und Ende eines Strings.

String s = " \tSprich zu der Hand.\n  \t "; 
System.out.println( "'" + s.trim() + "'" ); // 'Sprich zu der Hand.'

Andere Modesprachen wie Visual Basic bieten dazu noch trim()-Methoden an, die nur die Leerzeichen vorher oder nachher verwerfen. Die Java-Bibliothek bietet das so einfach nicht. [Wieder ein Hinweis, dass Visual Basic einfach die bessere Sprache ist ...]


Galileo Computing - Zum Seitenanfang

4.2.7 Suchen und ersetzen Zur nächsten ÜberschriftZur vorigen Überschrift

Da String-Objekte unveränderlich sind, kann eine Veränderungsmethode nur einen neuen String mit den Veränderungen zurückgeben. Wir finden in Java vier Methoden, die suchen und ersetzen:

  • replace( char oldChar, char newChar ). Ersetzt einzelne Zeichen.
  • replace( CharSequence target, CharSequence replacement). Ersetzt eine Zeichenkette durch eine andere Zeichenkette.
  • replaceAll( String regex, String replacement). Ersetzt alle Strings, die durch einen regulären Ausdruck beschrieben werden.
  • replaceFirst( String regex, String replacement). Ersetzt den ersten String, der durch einen regulären Ausdruck beschrieben wird.

Ersetzen ohne reguläre Ausdrücke

Die replace(char, char)-Methode ersetzt einzelne Zeichen.


Beispiel Ändere den in einer Zeichenkette vorkommenden Buchstaben »o« in »u«:

String s1 = "Honolulu"; 
String s2 = s1.replace( 'o', 'u' );          // s2 = "Hunululu"

Das String-Objekt mit dem Namen s1 wird selbst nicht verändert. Es wird nur ein neues String-Objekt mit dem Inhalt »Hunululu« erzeugt und von replace() zurückgegeben.


Gibt es etwas zu ersetzen, erzeugt replace() intern ein neues Zeichenfeld, führt die Ersetzungen durch und konvertiert das interne Zeichenfeld in ein String-Objekt, was die Rückgabe ist. Gab es nichts zu ersetzen, bekommen wir das gleiche String-Objekt zurück, das die Anfrage stellte. Die replace()-Methode ersetzt immer alle Zeichen. Eine Variante, die nur das erste Zeichen ersetzt, müssen wir uns selbst schreiben.

Eine zweite überladene Variante, replace(CharSequence, CharSequence), sucht nach allen auftretenden Zeichenfolgen und ersetzt sie durch eine andere Zeichenfolge. Der Ersetzungsstring kann auch leer sein.


Beispiel Im String s soll »Schnecke« durch »Katze« ersetzt werden:

String s = "Schnecken erschrecken, wenn Schnecken an Schnecken schlecken, " + 
           "weil zum Schrecken vieler Schnecken Schnecken nicht schmecken."; 
System.out.println( s.replace("Schnecke", "Katze") );

Das Ergebnis auf dem Bildschirm ist »Katzen erschrecken, wenn Katzen an Katzen schlecken, weil zum Schrecken vieler Katzen Katzen nicht schmecken.«


Suchen und ersetzen mit regulären Ausdrücken

Die Methoden replaceAll() und replaceFirst() suchen in Zeichenketten mit Hilfe von regulären Ausdrücken (mehr dazu etwas später in diesem Kapitel) und nehmen Ersetzungen vor; replaceFirst() ersetzt, wie der Name schon sagt, nur das erste Auftreten.


Beispiel Mehr als zwei Leerzeichen in Folge sollen auf ein Leerzeichen komprimiert werden:

String s = "Alles  fit im   Schritt?"; 
System.out.println( s.replaceAll( " +", " " ) );   // Alles fit im Schritt? 
System.out.println( s.replaceFirst( " +", " " ) ); // Alles fit im   Schritt?

Weil der Suchstring immer ein regulärer Ausdruck ist und Sonderzeichen wie ».« oder »+«eine Sonderrolle einnehmen, eignen sich replaceAll() und replaceFirst() nicht direkt für allgemeine Ersetzungsaufgaben; hier ist die replace()-Methode passender.


Beispiel Für eine String-Ersetzung stellen wir replace() und replaceAll() nebeneinander:

String s; 
s = "'Tag, Karl.' 'Wie geht's, Karl?' 'Gut, Karl.' 'Kahl, Karl?' 'Ja, Karl, ganz kahl.'"; 
System.out.println( s.replace(".", "!") );

Der Aufruf ersetzt alle Punkte durch Ausrufezeichen, sodass das Ergebnis wie folgt lautet:

'Tag, Karl!' 'Wie geht's, Karl?' 'Gut, Karl!' 'Kahl, Karl?' 'Ja, Karl, ganz kahl!'

Nutzen wir s.replaceAll(".", "!"), führt das nicht zum Erfolg, sondern nur zu der Zeichenkette:

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

Der Punkt steht in regulären Ausdrücken für beliebige Zeichen. Erst, wenn er mit \\ ausmaskiert wird – wegen des Sonderstatus vom »\« muss auch dieses Zeichen selbst ausmaskiert werden –, liefert die Anweisung wie in s.replaceAll("\\.", "!") das gewünschte Ergebnis. Die statische Methode Pattern.quote(String) maskiert die Pattern-Sonderzeichen für uns aus, sodass auch s.replaceAll(Pattern.quote("."), "!") gut funktioniert.


Galileo Computing - Zum Seitenanfang

4.2.8 String-Objekte mit Konstruktoren neu anlegen topZur vorigen Überschrift

Liegt die Zeichenkette nicht als String-Literal vor, lassen sich mit den unterschiedlichen Konstruktoren der String-Klasse neue String-Objekte aufbauen.


final class java.lang.String 
implements CharSequence, Comparable<String>, Serializable

  • String()
    Erzeugt ein neues Objekt ohne Zeichen (den leeren String "").
  • String( String string )
    Erzeugt ein neues Objekt mit einer Kopie von string. Es wird selten benötigt, da String-Objekte unveränderbar (immutable) sind.
  • String( char[] value )
    Erzeugt ein neues Objekt und kopiert die im char-Feld vorhandenen Zeichen in das neue String-Objekt.
  • String( char[] value, int offset, int length )
    Erzeugt wie String(char[]) einen String aus einem Ausschnitt eines Zeichenfelds. Der verwendete Ausschnitt beginnt bei dem Index offset und umfasst length Zeichen.
  • String( byte[] bytes )
    Erzeugt ein neues Objekt aus dem Byte-Feld. Das byte-Array enthält keine Unicode-Zeichen, sondern eine Folge von Bytes, die nach der Standardkodierung der jeweiligen Plattform in Zeichen umgewandelt werden.
  • String( byte[] bytes, int offset, int length )
    Erzeugt wie String(byte[]) einen String aus einem Ausschnitt eines Byte-Felds.
  • String( byte[] bytes, String charsetName ) throws UnsupportedEncodingException
    Erzeugt einen neuen String von einem Byte-Array mit Hilfe einer speziellen Zeichenkodierung, die die Umwandlung von Bytes in Unicode-Zeichen festlegt.
  • String( byte[] bytes, int offset, int length, String charset ) throws UnsupportedEncodingException Erzeugt einen neuen String mit einem Teil des Byte-Arrays mit Hilfe einer speziellen Zeichenkodierung.
  • String( StringBuffer buffer )
  • String( StringBuilder builder )
    Erzeugt aus einem veränderlichen StringBuffer-/StringBuilder-Objekt ein unveränderliches String-Objekt, das dieselbe Zeichenreihe repräsentiert.
  • String( int[] codePoints, int offset, int count )
    Erzeugt ein String-Objekt mit Unicode-Codepoints, die Zeichen über int kodieren.

Die Konstruktoren sind im Speziellen nur dann nötig, wenn aus einer Fremdrepräsentation wie einem StringBuilder, StringBuffer, char[] oder byte[] oder Teilen daraus ein String-Objekt aufgebaut werden soll.


Beispiel Erzeuge einen String einer gegebenen Länge.

Listing 4.6 GenerateStringWithGivenLength.java, generateStringWithLength()

public static String generateStringWithLength( int len, char fill ) 
{ 
  if ( len < 0 ) 
    return null; 
 
  char[] cs = new char[ len ]; 
  Arrays.fill( cs, fill ); 
  return  new String( cs ); 
}

In der String-Klasse gibt es keine Funktion, die eine Zeichenkette einer vorgegebenen Länge aus einem einzelnen Zeichen erzeugt.


Der Konstruktor String(String)

Ein Konstruktor führt leicht zur Verwirrung, und zwar der Konstruktor, der einen anderen String annimmt. So ergeben die beiden folgenden Zeilen die Referenz auf ein String-Objekt:

String rudi = "There is no spoon"; 
String rudi = new String( "There is no spoon" );

Die zweite Lösung erzeugt unnötigerweise ein zusätzliches String-Objekt, denn das Literal ist ja schon ein vollwertiges String-Objekt.

Der Konstruktor ist nur für eine Optimierung zu gebrauchen. Immer wenn mit substring() ein Teilstring aufgebaut wurde, wird die ganze Zeichenfolge als char-Feld intern referenziert. Um den Speicherbedarf in diesem Fall zu optimieren, ist dieser Konstruktor gut geeignet.

Leerer String, Leer-String oder Null-String

Die Anweisungen

String s = "";

und

String s = new String();

referenzieren in beiden Fällen String-Objekte, die keine Zeichen enthalten. Die zweite Schreibweise erzeugt aber ein neues String-Objekt, während im ersten Fall das String-Literal im Konstantenpool liegt. Ein String ohne Zeichen nennen wir leeren String, Leer-String oder Null-String. Der letzte Begriff ist leider etwas unglücklich und führt oft zur Verwechslung mit Folgendem:

String s = null; 
System.out.println( s );              // null

Hier bekommen wir keinen leeren String und bei der Benutzung im Methodenaufruf, etwa bei s.length(), eine NullPointerException.

Strings im Konstantenpool

Die JVM erzeugt für jedes Zeichenketten-Literal automatisch ein entsprechendes String-Objekt. Das geschieht für jede konstante Zeichenkette höchstens einmal, egal wie oft sie im Programmverlauf benutzt wird und welche Klassen den String nutzen. Dieses String-Objekt lebt in einem Bereich, der Konstantenpool genannt wird. [Die Java-Bibliothek implementiert hier das Entwurfsmuster Fliegengewicht (Flyweight-Pattern) der GoF.]


Hinweis Nehmen wir an, die Anweisung

System.out.println( "tutego" );

steht in einer Klasse A und in einer anderen Klasse B steht

int len = "tutego".length();

Dann gibt es die Zeichenfolge »tutego« als String-Objekt nur ein einziges Mal in der Laufzeitumgebung.


Bei konstanten Werten führt der Compiler Optimierungen durch, etwa in der Art, dass er konstante Ausdrücke gleich berechnet. Nicht nur setzt er für Ausdrücke wie 1 + 2 das Ergebnis 3 ein, auch aufgebrochene konstante String-Teile, die mit Plus konkateniert werden, fügt der Compiler zu einer Zeichenkette zusammen.


Beispiel Beide Anweisungen sehen im Bytecode gleich aus.

String s = "Operating systems are like underwear – nobody really wants to look at them. (Bill Joy)"; 
String s = "Operating systems are like underwear – nobody really wants to look at them" + '.' + " (Bill Joy)";



Ihr Kommentar

Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen.






<< zurück
  Zum Katalog
Zum Katalog: Java ist auch eine Insel





Java ist auch eine Insel
Jetzt bestellen


 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Tipp
Zum Katalog: Coding for Fun





 Coding for Fun


 Buchempfehlungen
Zum Katalog: Objektorientierte Programmierung





 Objektorientierte
 Programmierung


Zum Katalog: Einstieg in Eclipse 3.4






 Einstieg in
 Eclipse 3.4


Zum Katalog: Java 6 lernen mit Eclipse






 Java 6 lernen
 mit Eclipse


Zum Katalog: NetBeans Platform 6






 NetBeans
 Platform 6


Zum Katalog: Java und XML






 Java und XML


Zum Katalog: Visual C# 2008






 Visual C# 2008


Zum Katalog: IT-Handbuch für Fachinformatiker






 IT-Handbuch für
 Fachinformatiker


Zum Katalog: C++ von A bis Z






 C++ von A bis Z


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo




Copyright © Galileo Press 2009
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Galileo Computing]

Galileo Press, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de