What changed in Java 15

Text blocks, and helpful NullPointerExceptions by default

Two quality-of-life changes that developers notice every day: multi-line strings without escaping, and NPE messages that name what was null.

Released 2020-09 · 2 features · 5 questions · 2 scenarios

Features

Text blocks

standardJEP 378

Multi-line string literals delimited by triple quotes, with incidental indentation stripped based on the closing delimiter's position.

The problem it solved

Embedded SQL, JSON and HTML required concatenation with \n and escaped quotes, making the literal unreadable and diffs noisy.

How you did it before

String concatenation across lines with explicit \n and backslash-escaped quotes, or loading the text from a resource file to avoid the mess.

Journey: Preview in 13 (JEP 355) and 14, final in 15.

Asked as

  • What problem do text blocks solve, and what are the traps?
  • How is incidental indentation determined?
  • Do text blocks interpret escape sequences?

Scenario question

  • A test class holds twelve JSON fixtures as concatenated strings with escaped quotes, and a reviewer asks for text blocks.

    What do you watch for in the conversion?

    What a good answer weighs

    Indentation is computed from the least-indented line including the closing delimiter, so moving that delimiter silently changes the content — and a JSON fixture compared with an exact string will then fail. Trailing spaces are stripped unless escaped. The mature answer also asks whether fixtures that large belong in resource files instead.

Helpful NullPointerExceptions

standardJEP 358

NPE messages describe precisely which expression was null, including which link of a chained call. Introduced in 14 behind a flag, on by default from 15.

The problem it solved

On a line like a.b().c().d(), the exception told you only that something was null, so diagnosis meant a debugger or extra logging on a line you might not be able to reproduce.

How you did it before

Splitting the chain across lines to find the culprit from the line number, or adding logging and waiting for it to happen again.

Journey: Java 14 (JEP 358) opt-in via -XX:+ShowCodeDetailsInExceptionMessages; default from 15.

Asked as

  • How do you debug a NullPointerException on a chained call?
  • Why does the message sometimes say <local1> instead of a variable name?

Scenario question

  • Production logs show "Cannot invoke String.length() because <local3> is null", but locally the same code names the variable.

    Why the difference, and does it matter?

    What a good answer weighs

    The production build was compiled without debug information, so local variable names are not in the class file and the JVM falls back to a slot number. Maven and Gradle include debug info by default, so something in the pipeline strips it. It matters precisely because it removes the benefit of the feature in the one environment where you need it most.

Other versions