W3docs

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 formatierten String zurück.
  • "...".formatted(args...) — Instanzform, hinzugefügt in Java 15. Identisches Ergebnis, besser geeignet zum Verketten.
  • System.out.printf(fmt, args...) — gibt direkt auf einen PrintStream (oder einen beliebigen Formatter) 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:

KonvertierungArgumentBedeutung
%sbeliebigtoString() des Werts
%dganzzahligDezimalzahl
%fGleitkommazahlFestkommadarstellung
%eGleitkommazahlwissenschaftliche Notation
%gGleitkommazahlkürzere Darstellung aus %e und %f
%x, %oganzzahligHexadezimal / Oktal
%cZeicheneinzelnes Zeichen
%bbeliebigtrue/false (null → "false")
%nkeinsplattformspezifisches Zeilentrennzeichen
%%keinsein literales %
%t...Datum/Uhrzeiteine 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 locale

Locale.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 eine IllegalFormatConversionException%d erwartet ganzzahlig, %f erwartet Gleitkomma. Der Compiler kann das nicht prüfen.
  • Fehlende Argumente. Das Vergessen eines Platzhalterarguments wirft eine MissingFormatArgumentException.
  • Gebietsschemaabhängige Dezimaltrennung in Maschinenausgaben. Oben behandelt.
  • %s auf null. Ergibt "null". In Protokollen in Ordnung, in benutzerorientierter Ausgabe peinlich.
  • + für formatierte Zahlen verwenden. "Price: " + 19.95 ergibt "Price: 19.95", aber "Price: " + 0.1 + 0.2 ergibt "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.

java— editable, runs on the server

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.

Übungen

Übung
Sie möchten einen `double`-Gesamtbetrag als String mit Tausendertrennzeichen und genau zwei Dezimalstellen formatieren, und der Dezimalpunkt soll unabhängig vom Gebietsschema der JVM immer ein Punkt sein (damit ein JSON-Parser ihn verarbeiten kann). Welcher Aufruf ist korrekt?
Sie möchten einen `double`-Gesamtbetrag als String mit Tausendertrennzeichen und genau zwei Dezimalstellen formatieren, und der Dezimalpunkt soll unabhängig vom Gebietsschema der JVM immer ein Punkt sein (damit ein JSON-Parser ihn verarbeiten kann). Welcher Aufruf ist korrekt?
Was this page helpful?