Java String-Formatierung
Java-Strings formatieren mit String.format und System.out.printf mithilfe von Formatspezifizierern wie %s, %d, %f und %n.
Konkatenation (+) eignet sich gut für kurze, einfache Strings. Sobald man eine Zahl auf eine bestimmte Anzahl von Dezimalstellen runden, Spalten durch Auffüllen ausrichten, ein Datum in konsistentem Format einbetten oder Werte allgemein formatieren statt nur einfügen möchte, greift man auf die printf-artige API zurück, die Java von C übernommen hat.
Es gibt drei eng verwandte Einstiegspunkte, die alle im echten Code vorkommen:
String.format(fmt, args...)— gibt einen formatiertenStringzurück."...".formatted(args...)— Instanzform, hinzugefügt in Java 15. Identisches Ergebnis, besser geeignet zum Verketten.System.out.printf(fmt, args...)— gibt direkt auf einenPrintStream(oder einen beliebigenFormatter) aus.
Alle drei teilen dieselbe Formatspezifizierer-Syntax. Einmal lernen, überall anwenden.
Formatspezifizierer
Ein Spezifizierer hat die Form %[flags][width][.precision]conversion. Der Konvertierungsbuchstabe ist das einzige Pflichtfeld. Der Rest steuert Breite, Ausrichtung, Auffüllung und Präzision.
String s = String.format("%-10s | %5d | %8.2f", "apples", 42, 3.14159);
// "apples | 42 | 3.14"Die nützlichsten Konvertierungen:
| Konvertierung | Argument | Bedeutung |
|---|---|---|
%s | beliebig | toString() des Werts |
%d | ganzzahlig | Dezimalzahl |
%f | Gleitkommazahl | Festkommadarstellung |
%e | Gleitkommazahl | wissenschaftliche Notation |
%g | Gleitkommazahl | kürzere Darstellung aus %e und %f |
%x, %o | ganzzahlig | Hexadezimal / Oktal |
%c | Zeichen | einzelnes Zeichen |
%b | beliebig | true/false (null → "false") |
%n | keins | plattformspezifisches Zeilentrennzeichen |
%% | keins | ein literales % |
%t... | Datum/Uhrzeit | eine ganze Familie — %tF, %tT, usw. |
Die Breite füllt die Ausgabe auf mindestens N Zeichen auf; die Präzision bedeutet „Dezimalstellen" bei Gleitkommazahlen und „maximale Zeichenanzahl" bei Strings.
Flags: Ausrichtung, Vorzeichen, Nullauffüllung, Gruppierung
Eine Handvoll Flags steht zwischen % und der Breite:
-— linksbündig in der Breite ausrichten. Standardmäßig rechtsbündig.0— mit Nullen auf die Breite auffüllen (nur bei numerischen Konvertierungen).+— bei Zahlen immer ein Vorzeichen anzeigen (+42,-7).(Leerzeichen) — bei positiven Zahlen ein führendes Leerzeichen anzeigen, ähnlich wie+, aber mit einem Leerzeichen.,— Stellen mit dem tausender Trennzeichen des Gebietsschemas gruppieren.(— negative Zahlen in Klammern einschließen, im Buchhaltungsstil.
String.format("%08d", 42); // "00000042"
String.format("%,d", 1234567); // "1,234,567"
String.format("%+.2f", 3.14159); // "+3.14"
String.format("%-10s|", "hi"); // "hi |"Breite und Präzision
Breite ist die Mindest-Feldbreite — wenn der formatierte Wert breiter ist, wird nichts abgeschnitten.
Präzision bedeutet je nach Konvertierung unterschiedliches:
%.3f— drei Stellen nach dem Dezimalpunkt.%.10s— den String auf höchstens 10 Zeichen kürzen.%.4e— vier Stellen Mantissenpräzision.
Breite und Präzision zu kombinieren ist üblich, wenn Spalten ausgerichtet und Zahlen gerundet sein sollen:
String.format("%10.4f", Math.PI); // " 3.1416"
String.format("%-10.4s", "abcdef"); // "abcd "%n vs. \n
%n gibt das plattformspezifische Zeilentrennzeichen aus: "\n" unter Unix, "\r\n" unter Windows. \n ist immer genau ein Byte. Für Dateien und Protokollausgaben, bei denen das Zeilenende präzise vorgegeben ist, ist \n vorzuziehen und bewusst zu wählen. Für Konsolenausgaben, die auf dem jeweiligen Betriebssystem, auf dem die JVM läuft, korrekt aussehen sollen, ist %n die sicherere Wahl.
Argumentindizes: Wiederverwendung und Neuanordnung
Ein Spezifizierer der Form %N$... bezieht sich auf das N-te Argument (1-basiert). Nützlich, wenn ein Wert mehr als einmal in einer Vorlage erscheint oder wenn die natürliche Lesereihenfolge von der Argumentreihenfolge abweicht:
String.format("%1$s, %1$s, %1$s!", "go"); // "go, go, go!"
String.format("%2$s before %1$s", "lunch", "tea"); // "tea before lunch"Dies ist das richtige Werkzeug beim Lokalisieren von Vorlagen — verschiedene Sprachen stellen Substantive an unterschiedliche Positionen, und ein Übersetzer kann Platzhalter neu anordnen, ohne die Aufrufstelle anzufassen.
Gebietsschema beeinflusst Zahlen und Datumsangaben
Die Zahlenformatierung richtet sich nach dem Standard-Gebietsschema der JVM, sofern es nicht überschrieben wird. In en-US erhält man 3.14; in de-DE erhält man 3,14; in fr-FR werden Tausender mit einem geschützten Leerzeichen gruppiert. Für benutzerorientierte Ausgaben ist das meist erwünscht. Für Datenformate — JSON, CSV, Protokolldateien, alles maschinenlesbare — ist es ein Desaster, das auf seinen Einsatz in Frankfurt wartet.
Für maschinenlesbare Ausgaben immer explizit ein Gebietsschema übergeben:
String json = String.format(Locale.ROOT, "{\"price\": %.2f}", 19.95);
// "{\"price\": 19.95}" — always, regardless of JVM localeLocale.ROOT bedeutet „keine gebietsschemaspezifische Formatierung" — Punkt als Dezimaltrennzeichen, keine Gruppierung. Locale.US ist die andere übliche Wahl für denselben Zweck. Das Gefährliche ist, kein Gebietsschema zu übergeben und einfach anzunehmen.
Datum und Uhrzeit formatieren
%t ist eine Meta-Konvertierung: der folgende Buchstabe wählt das Feld aus. Dasselbe Date-, Calendar-, Long- (Millisekunden) oder java.time.temporal.TemporalAccessor-Argument kann auf viele Arten formatiert werden:
LocalDateTime now = LocalDateTime.of(2026, 5, 29, 14, 30, 15);
String.format("%tF", now); // "2026-05-29" — ISO date
String.format("%tT", now); // "14:30:15" — 24-hour time
String.format("%tA", now); // "Friday" — locale-dependent
String.format("%1$tF %1$tT", now); // "2026-05-29 14:30:15"Für alles, was über schnelle Formatierung hinausgeht, ist die java.time.format.DateTimeFormatter-API flexibler und gebietsschemabewusster — aber %tF und ähnliche bleiben in Protokollzeilen nützlich.
Häufige Fallstricke
- Falsche Konvertierung für den Typ.
String.format("%d", 3.14)wirft zur Laufzeit eineIllegalFormatConversionException—%derwartet ganzzahlig,%ferwartet Gleitkomma. Der Compiler kann das nicht prüfen. - Fehlende Argumente. Das Vergessen eines Platzhalterarguments wirft eine
MissingFormatArgumentException. - Gebietsschemaabhängige Dezimaltrennung in Maschinenausgaben. Oben behandelt.
%saufnull. Ergibt"null". In Protokollen in Ordnung, in benutzerorientierter Ausgabe peinlich.+für formatierte Zahlen verwenden."Price: " + 19.95ergibt"Price: 19.95", aber"Price: " + 0.1 + 0.2ergibt"Price: 0.10.2", nicht"Price: 0.3"— Konkatenation, keine Addition.
Ein ausgearbeitetes Beispiel
Ein kleiner Auftragsübersichts-Formatierer, der Breite, Präzision, Ausrichtung, Gruppierung, Gebietsschema und %t-Datumskonvertierung einsetzt. Die Ausgabe ist ein übersichtlicher zweispaltiger Bericht — die Art von Ding, die man sonst mit padLeft-Hilfsmethoden manuell aufbaut.
Zwei Dinge sind zu beachten: Das %<tT in der Zeile „Placed" verwendet das vorherige Argument wieder (< ist das Rückverweisflag), wodurch ein redundantes zweites placedAt vermieden wird. Und die JSON-Zeile verwendet Locale.ROOT — derselbe Code auf einer deutschen JVM gibt immer noch 1339.43 aus, nicht 1339,43, was genau das ist, was ein JSON-Parser erwartet.
Was kommt als Nächstes
Strings zu erstellen ist eine Hälfte der Aufgabe. Sie zu vergleichen — Gleichheit, Reihenfolge, Groß-/Kleinschreibung, der Unterschied zwischen == und equals — ist die andere Hälfte und eine häufige Quelle subtiler Fehler. Weiter zu Java-String-Vergleich.