String Improvements
Across Java 11–21, the String class received a steady stream of small, targeted additions — isBlank(), strip()/stripLeading()/stripTrailing(), repeat(int), lines(), and String Templates as a preview feature in Java 21 — each addressing a specific, recurring pain point that previously required either importing a third-party library (Apache Commons Lang, Guava) or writing manual, easy-to-get-subtly-wrong helper code, particularly around Unicode-aware whitespace handling and Unicode-aware blank detection, neither of which the much older trim() method actually does correctly. This entry covers exactly why trim() and isEmpty() were insufficient, the new whitespace and blank-detection methods and their Unicode-correctness improvements, repeat() and lines() and the common manual idioms they replace, and the Java 21 String Templates preview feature for safer, more readable string interpolation.
Motivation — trim() and isEmpty() Were Not Unicode-Aware or Sufficient
// ── trim() — NOT Unicode-aware, misses non-ASCII whitespace ────────────
String nbsp = "\u00A0Hello\u00A0"; // non-breaking space on both sides
System.out.println(nbsp.trim().length()); // length unchanged — trim() did NOT strip U+00A0!
System.out.println(nbsp.strip().length()); // correctly stripped — strip() IS Unicode-aware
// ── isEmpty() doesn't account for whitespace-only strings ──────────────
String whitespaceOnly = " \t ";
System.out.println(whitespaceOnly.isEmpty()); // false — has length, even though it's "blank"
// Old manual idiom — works, but easy to forget and inherits trim()'s Unicode gaps:
boolean oldBlankCheck = whitespaceOnly.trim().isEmpty();
// ── Java 11+ — correct, direct, Unicode-aware ───────────────────────────
System.out.println(whitespaceOnly.isBlank()); // true
System.out.println(" Hello ".strip()); // "Hello" — Unicode whitespace stripped
System.out.println(" Hello ".stripLeading()); // "Hello "
System.out.println(" Hello ".stripTrailing()); // " Hello"repeat() and lines() — Replacing Manual Loop Idioms
// ── Before Java 11 — manual loop or opaque one-liner for repetition ────
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 20; i++) sb.append("-");
String separatorOld = sb.toString();
// ── Java 11+ — direct, self-explanatory ─────────────────────────────────
String separator = "-".repeat(20);
System.out.println(separator); // --------------------
System.out.println("ab".repeat(3)); // "ababab"
System.out.println("x".repeat(0)); // "" — empty string
// "x".repeat(-1); // IllegalArgumentException
// Practical use — indentation, padding:
String indent = " ".repeat(4) + "nested content";
// ── lines() — correctly handles \n, \r, and \r\n, returns a Stream ──
String multilineText = "first line\nsecond line\r\nthird line\rfourth line";
long lineCount = multilineText.lines().count();
System.out.println(lineCount); // 4 — correctly split despite mixed line terminators
multilineText.lines()
.filter(line -> line.contains("second"))
.forEach(System.out::println); // "second line"
// Contrast — plain split("\n") mishandles \r\n-terminated lines:
String[] splitOld = multilineText.split("\n");
System.out.println(splitOld[1].endsWith("\r")); // true — trailing \r leaked through, a common bugString Templates (Java 21 Preview) — Safer Interpolation
// ── Before templates — concatenation or positional format strings ──────
String name = "Alice";
int age = 30;
String messageOld = "Name: " + name + ", Age: " + age; // verbose, easy to mis-concatenate
String messageFormat = String.format("Name: %s, Age: %d", name, age); // must keep args in sync
// with %s/%d order — a reordering bug here is easy to introduce and easy to miss
// ── Java 21 preview — String Templates with the built-in STR processor ─
// (requires --enable-preview on javac and java)
String message = STR."Name: \{name}, Age: \{age}";
// Variable references are visually inline with the text — no separate argument list to
// keep mentally synchronized with placeholder order
// Expressions, not just simple variables, are allowed inside \{...}:
String summary = STR."\{name} will be \{age + 1} next year";
// ── Custom template processors can validate/transform output ───────────
// Conceptual example from the JEP motivation — a SQL-safe processor that
// escapes embedded values automatically, rather than relying on the
// developer to remember to escape every interpolated value manually:
//
// PreparedStatement stmt = SQL."SELECT * FROM users WHERE name = \{userInput}";
// The processor controls how userInput is incorporated — structurally
// preventing the kind of injection that naive "..." + userInput + "..."
// concatenation into a query string would be vulnerable to.
// NOTE: preview feature — exact syntax/availability may change across
// JDK releases; confirm current status before depending on it in production.