Tech Archive Help

05 Java – Zeichenketten

Zeichenketten (auch Strings genannt) sind eine der häufigsten angewendeten Datentypen. Sie repräsentieren eine Folge von Zeichen, gehören zu den Referenzdatentypen (Objekte) und sind daher keine primitiven Datentypen. Wird eine Variable vom Typ String angelegt, enthält diese nicht die Zeichenkette selbst, sondern eine Referenz (Verweis) auf das eigentliche Objekt der Klasse String. Ein char ist ein primitiver Datentyp und kann nur ein einziges Zeichen darstellen. Sollen mehr dargestellt werden muss auf einen String zurückgegriffen werden.

Konkatenation

Im vorherigen Kapitel 4 – Ein- und Ausgaben gab es bereits einen kleinen Einblick zu Strings. Wenn zwei Strings mit einem + verbunden werden, wird das eine Konkatenation (Verkettung, engl. concatenation) genannt. Dies ist auch zwischen Strings und primitiven Datentypen möglich.

String name = "Kesares"; System.out.println(name); // Kesares
System.out.println("42" + 5); // 425
int x = 2; int y = 40; String s = x + " + " + y + " = " + (x + y); System.out.println(s); // 2 + 40 = 42

String-Pool und Java-Heap

Ein String ist ein spezielles Objekt, das für die Arbeit mit Texten verwendet wird. Obwohl Strings in Java Objekte sind, unterscheiden sich ihre Erzeugung und Verwaltung von der vieler anderer Objekte.

Normalerweise werden Objekte mit dem new-Keyword erstellt, was zu einem neuen Objekt auf dem Java-Heap führt.

String name = new String("Kesares");

In diesem Fall werden zwei String-Objekte erstellt: eines auf dem Java-Heap, das durch den new-Operator erzeugt wird und ein weiteres im sogenannten String-Pool. Der String-Pool ist ein spezieller Speicherbereich, der von der JVM verwaltet wird und zur Optimierung von Speicherverbrauch und Leistung dient.

Der String-Pool speichert einmal erstellte String-Literale und stellt sicher, dass alle identischen Literale auf denselben Speicherort im Pool verweisen. Dies bedeutet, dass bei der Nutzung von Literalen wie "Kesares" die JVM nicht jedes Mal ein neues String-Objekt erstellen muss, sondern auf das bereits im Pool vorhandene Objekt verweist.

Die bevorzugte und performantere Schreibweise zur Erstellung eines String-Objekts ist daher:

String s = "Kesares";

In diesem Fall wird, sofern bereits eine Zeichenkette mit dieser Zeichenfolge existiert, kein weiteres String-Objekt angelegt und alle Verweise auf das Literal "Kesares" zeigen auf das gleiche Objekt im Pool. Diese Methode reduziert den Speicherverbrauch und verbessert die Leistung, da die JVM unnötige Duplikate von String-Objekten vermeidet und die Verwaltung von Speicher effizienter gestaltet wird.

String-Methoden

In der Java-Programmierung gibt es zahlreiche Operationen, die auf einem String-Objekt durchgeführt werden können. Diese Methoden werden direkt über die jeweiligen String-Objekte aufgerufen, um verschiedene Bearbeitungen oder Abfragen durchzuführen. Im Folgenden sind einige der wichtigsten Methoden beschrieben, die für die Arbeit mit Strings von Bedeutung sind.

Methode

Beschreibung

length()

Gibt die Anzahl der Zeichen (inklusive Leerzeichen) im String zurück.

equals(otherString)

Vergleicht den aktuellen String mit einem übergebenen String, um festzustellen, ob sie inhaltlich identisch sind. Gibt true zurück, wenn die beiden Strings gleich sind, andernfalls false.

equalsIgnoreCase(otherString)

Vergleicht den aktuellen String mit einem übergebenen String, um festzustellen, ob sie inhaltlich identisch sind, wobei die Groß- und Kleinschreibung ignoriert wird. Gibt true zurück, wenn die beiden Strings inhaltlich gleich sind, andernfalls false.

charAt(index)

Liefert das Zeichen an der übergebenen Position (Index) im String.

substring(start, end)

Extrahiert einen Teilstring aus dem Originalstring. Die Methode nimmt einen Start- und einen Endindex entgegen und gibt den entsprechenden Teilstring zurück.

toLowerCase()

Konvertiert alle Zeichen im String in Kleinbuchstaben.

toUpperCase()

Konvertiert alle Zeichen im String in Großbuchstaben.

startsWith(prefix)

Prüft, ob der String mit einer bestimmten Zeichenfolge (Präfix) beginnt. Gibt true zurück, wenn dies der Fall ist, andernfalls false.

endsWith(suffix)

Prüft, ob der String mit einer bestimmten Zeichenfolge (Suffix) endet. Gibt true zurück, wenn dies der Fall ist, andernfalls false.

indexOf(substring)

Sucht nach der ersten Position einer übergebenen Zeichenfolge im String und gibt den Index dieser Position zurück. Falls die Zeichenfolge nicht gefunden wird, wird -1 zurückgegeben.

lastIndexOf(substring)

Im Gegensatz zu indexOf(substring) sucht diese Methode nach der letzten Position einer übergebenen Zeichenfolge im String und gibt deren Index zurück. Auch hier wird -1 zurückgegeben, wenn die Zeichenfolge nicht gefunden wird.

trim()

Entfernt führende und nachfolgende Leerzeichen aus dem String.

replace(oldChar, newChar)

Ersetzt bestimmte Zeichen im String durch andere Zeichen.

split(delimiter)

Zerlegt den String anhand eines angegebenen Trennzeichens und gibt ein Array von Strings zurück.

contains(substring)

Überprüft, ob der String eine bestimmte Zeichenfolge enthält. Gibt true zurück, wenn die Zeichenfolge vorhanden ist, andernfalls false.

valueOf(value)

Wandelt einen anderen Datentyp in einen String um. Das Ergebnis ist eine String-Darstellung des übergebenen Wertes.

isEmpty()

Überprüft, ob der String leer ist (d.h., keine (Leer-)Zeichen enthält). Sie gibt true zurück, wenn der String leer ist, andernfalls false.

isBlank()

Überprüft, ob der String leer oder nur aus Leerzeichen besteht. Im Gegensatz zur Methode isEmpty(), berücksichtigt isBlank() auch Strings, die nur Leerzeichen, Tabs oder andere Whitespace-Zeichen enthalten. Gibt true zurück, wenn der String leer ist oder nur aus Whitespace-Zeichen besteht, andernfalls false.

String name = "Kesares"; name.length(); // 7 name.equals("Kesares"); // true name.charAt(3); // 'a' name.startsWith("s"); // false name.endsWith("res"); // true name.substring(3); // "ares" name.substring(3, 6); // "are" name.indexOf("e"); // 1 name.lastIndexOf("e"); // 5 name.toLowerCase(); // "kesares" name.toUpperCase(); // "KESARES" name.replace('e', 'i'); // "Kisaris" " Kesares ".trim(); // "Kesares" name.split("a"); // ["Kes", "res"] name.contains("sar"); // true name.valueOf(42); // "42" name.isEmpty(); // false name.isBlank(); // false

Escape Sequenzen

Folgende Liste wurde bereits in Kapitel 2 – Datentypen präsentiert. Auch in Strings können diese verwendet werden.

Escape Sequenz

Beschreibung

\t

Horizontaler Tabulator

\r

Carriage Return (Wagenrücklauf)

\n

Zeilenumbruch

\uXXXX

Unicode XXXX (hexadezimal)

\b

Backspace (Rückschritt)

\f

Form Feed (Seitenumbruch)

\'

Hochkommata

\"

Anführungszeichen

\\

Backslash

Konvertierung

An einigen Stellen muss im Programm möglicherweise ein String in einen anderen Datentyp konvertiert werden. Java stellt für diese Anwendungsfälle die parse-Methoden zur Verfügung. Einen kleinen Einblick in diese Methoden gab es bereits in Kapitel 4 – Ein- und Ausgaben.

Die parse-Methoden sind statisch und werden daher über die Wrapper-Klassen der jeweiligen primitiven Datentypen angesprochen. Der Rückgabewert der Methoden entspricht dem jeweiligen Datentyp.

byte b = Byte.parseByte("127"); short s = Short.parseShort("32767"); int i = Integer.parseInt("2147483647"); long l = Long.parseLong("9223372036854775807"); float f = Float.parseFloat("3.4028235e38"); double d = Double.parseDouble("1.7976931348623157e308"); boolean bool = Boolean.parseBoolean("true");
String name = "Kesares"; char c = name.charAt(2); System.out.println(c); // s

Formatierung

Um Strings zu formatieren, gibt es mehrere Möglichkeiten und Varianten. Hier wird sich auf die folgenden beiden Vorgehensweisen konzentriert. Beide Varianten sind vom Prinzip identisch.

String s = String.format(format, args...) System.out.println(s); System.out.printf(format, args...)

Die format()-Methode ist eine statische Methode der Klasse String. Sie formatiert Strings nach einer bestimmten Vorgabe und gibt einen String mit den eingebetteten Argumenten zurück.

Der erste Parameter der Methode ist der eigentliche String, der am Ende beispielsweise auf der Konsole ausgegeben wird. Dieser kann zudem bestimmte Spezifizierer und Flags enthalten, die der Methode mitteilen, wie sie die Argumente formatieren soll. Der zweite Parameter ist ein Vararg (siehe Variable Parameteranzahl), mit den Werten, die für die jeweiligen Spezifizierer eingesetzt werden sollen.

Die Formatierungen des format-Strings werden nach folgendem Schema geschrieben.

%[flags][width][.precision]conversion

Die Anzahl an Spezifizierer muss der Anzahl an Argumenten gleichen.

Spezifizierer und Flags

Spezifizierer

Beschreibung

%n

Zeilenumbruch

%%

Prozentzeichen

%x

Hexadezimalsystem

%o

Oktalsystem

%f

Fließkommazahl

%b

Boolean

%s

String

%c

Character

%d

Dezimalzahl

%t

Datum und Zeit

%e

Wissenschaftliche Notation

Flag (optional)

Beschreibung

-

Linksbündige Ausgabe innerhalb der angegebenen Breite.

+

Zeigt immer das Vorzeichen (+ oder -) für numerische Werte an.

0

Auffüllen mit Nullen (anstelle von Leerzeichen) für Zahlen.

,

Tausendertrennzeichen verwenden (z.B. 1.000). Zeichen ist abhängig von der Spracheinstellung des Systems.

(

Negative Zahlen in Klammern anzeigen.

Die conversion ist der eigentliche Spezifizierer. Optional kann noch eine Breite und die Präzision angegeben werden.

  • conversion – der eigentliche Spezifizierer wie %d, %s, %f usw.

  • width – bestimmt die minimale Anzahl an Zeichen, die ausgegeben werden sollen. Wenn der Wert kürzer ist, wird er mit Leerzeichen aufgefüllt.

  • .precision – gibt die Anzahl der Nachkommastellen für Fließkommazahlen oder die maximale Anzahl an Zeichen für Strings an.

  • %n$ – gibt das n-te Argument an, dass an die format()-Methode übergeben wird. Dadurch ist es möglich die Reihenfolge der Argumente zu vertauschen oder die Argumente mehrmals verwenden zu können.

System.out.println(String.format("Hello %s!", "World")); System.out.println(String.format("The number is %d.", 42)); System.out.println(String.format("The price is %.2f euro.", 12.3456)); System.out.println(String.format("The number in hexadecimal: %x", 255)); System.out.println(String.format("The number in octal: %o", 8)); System.out.println(String.format("The character is %c.", 'A')); System.out.println(String.format("The value is %b.", true)); System.out.println(String.format("First line%nSecond line")); System.out.println(String.format("Wert: %8.2f", 123.4567)); System.out.println(String.format("Name: %-10s", "Java")); System.out.println(String.format("The amount is %,d.", 1000000)); System.out.println(String.format("%2$d + %1$d = %3$d", 5, 3, 8));

Textblock

Zeilenumbrüche kommen immer wieder vor. Gerade wenn es um Bildschirmausgaben oder eingebetteten Code wie HTML, JSON, SQL, etc. geht. In Java 15 wurden Textblöcke eingeführt, mit denen sich mehrzeilige Strings leichter bilden lassen, als über Konkatenationen. Eingeleitet wird ein Textblock mit drei doppelten Anführungsstrichen """.

String text = """ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> </body> </html> """;

Die Klassen StringBuilder und StringBuffer (Advanced)

Strings sind immutable und Konkatenationen sind teuer. Die Klassen StringBuilder und StringBuffer existieren zum Zweck der Veränderung von Strings. Werden mehrere Verkettungen durchgeführt (z.B. in Schleifen ab einer bestimmten Anzahl an Iterationen), ist ein StringBuilder-Objekt sinnvoller.

Soll eine Methode am Ende die Verkettung des StringBuilder-Objekts als String liefern, muss noch die toString()-Methode über den StringBuilder aufgerufen werden.

public static String getConcatString() { StringBuilder builder = new StringBuilder(); for (int i = 0; i < 10; i++) { builder.append(i); builder.append("-"); } return builder.toString(); }

Folgendes Beispiel misst die Zeit um die jeweiligen Strings mittels StringBuilder und Konkatenation zu bilden.

long now = 0; long timeTaken = 0; now = System.nanoTime(); StringBuilder builder = new StringBuilder(); for (int i = 0; i < 1000; i++) { builder.append("-"); } timeTaken = System.nanoTime() - now; System.out.printf("1st time: %d ns%n", timeTaken); now = System.nanoTime(); String text = ""; for (int i = 0; i < 1000; i++) { text += "-"; } timeTaken = System.nanoTime() - now; System.out.printf("2nd time: %d ns%n", timeTaken);

ANSI Escape-Sequenzen

ANSI Escape-Sequenzen sind eine Reihe von Zeichenfolgen, die verwendet werden können, um Text in der Konsole zu formatieren, um z.B. die Farbe oder den Stil des Textes zu ändern. Diese Sequenzen werden typischerweise in terminalbasierten Anwendungen verwendet, die ANSI-Farbcodes unterstützen, können aber auch innerhalb von IDEs verwendet werden.

Eine ANSI Escape-Sequenz beginnt immer mit einem Escape-Zeichen wie \033 oder \u001B (beides wird in Java unterstützt, letzteres ist typischer in der Java-Umgebung) gefolgt von einem [ und einer Reihe von Codes, die durch Semikolons getrennt sind und endet mit einem m.

Um die Formatierung nach einer ANSI-Sequenz zurückzusetzen und zur Standardkonfiguration zurückzukehren, wird \u001B[0m verwendet.

System.out.println("\u001B[31mThis text is red!\u001B[0m");

Farbcodes

Code

Farbe

30

Schwarz

31

Rot

32

Grün

33

Gelb

34

Blau

35

Magenta

36

Cyan

37

Weiß

40

Schwarzer Hintergrund

41

Roter Hintergrund

42

Grüner Hintergrund

43

Gelber Hintergrund

44

Blauer Hintergrund

45

Magenta Hintergrund

46

Cyan Hintergrund

47

Weißer Hintergrund

Stile

Code

Stil

0

Alle Attribute zurücksetzen (Normaler Text)

1

Fettschrift

3

Kursivschrift

4

Unterstrichen

7

Invertiert Vordergrund- und Hintergrundfarbe

9

Durchgestrichen

Mehrere Codes können kombiniert werden, indem sie durch Semikolons getrennt werden.

System.out.println("\u001B[31mThis text is red!\u001B[0m"); System.out.println("\u001B[32mThis text is green!\u001B[0m"); System.out.println("\u001B[1;34mThis text is blue and bold!\u001B[0m");
Last modified: 19 August 2024