Switch Expressions (Preview)
The most notable feature in Java 12 is Switch Expressions (JEP 325). The traditional switch statement had a longstanding issue where forgetting a break would cause fall-through. The new arrow (->) syntax fundamentally resolves this problem.
Think of it like a vending machine. The old switch was like a broken machine that dispensed every drink in a row when you pressed a button, while the new switch is a properly working machine that dispenses only the drink you selected.
public class SwitchExpressionDemo {
public static void main(String[] args) {
// === Traditional switch statement: risk of fall-through when break is missing ===
String day = "MONDAY";
int numLettersOld;
switch (day) {
case "MONDAY":
case "FRIDAY":
case "SUNDAY":
numLettersOld = 6;
break;
case "TUESDAY":
numLettersOld = 7;
break;
default:
numLettersOld = -1;
}
// === New switch expression: arrow syntax, returns a value ===
int numLettersNew = switch (day) {
case "MONDAY", "FRIDAY", "SUNDAY" -> 6; // Multiple cases with commas
case "TUESDAY" -> 7;
case "WEDNESDAY", "THURSDAY" -> 8;
case "SATURDAY" -> 8;
default -> -1;
};
System.out.println("Old way: " + numLettersOld);
System.out.println("New way: " + numLettersNew);
// Output:
// Old way: 6
// New way: 6
}
}
The arrow syntax has three advantages. First, no break is needed, eliminating fall-through bugs entirely. Second, multiple values can be listed with commas in a single case. Third, the switch itself becomes an expression that returns a value.
Note that in Java 12 this is a preview feature, so the --enable-preview flag is required at compile time.
javac --enable-preview --source 12 SwitchExpressionDemo.java
java --enable-preview SwitchExpressionDemo
Shenandoah GC — A New Option for Low-Latency GC
Shenandoah GC (JEP 189) is a low-latency garbage collector led by Red Hat. It aims for consistently short GC pause times regardless of heap size.
Using a highway construction analogy: if traditional GC is like shutting down all lanes for roadwork, Shenandoah is like doing construction alongside moving traffic.
| GC | Pause Time Characteristics | Heap Size Impact |
|---|---|---|
| G1 (default) | Tens to hundreds of ms | Increases with heap size |
| Shenandoah | Target under 10ms | Independent of heap size |
| ZGC | Target under 10ms | Independent of heap size |
To enable Shenandoah, add JVM options:
java -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -jar your-app.jar
G1 GC Improvements
Java 12 brought two important improvements to the default G1 GC.
Promptly Return Unused Committed Memory (JEP 346): G1 returns Java heap memory to the OS during idle time. This is particularly effective for saving memory in container environments.
Abortable Mixed Collections (JEP 344): Mixed GC can now be aborted midway if it exceeds target pause time. This improves GC pause time predictability.
Compact Number Formatting
CompactNumberFormat displays numbers concisely — 1,000 as “1K”, 1,000,000 as “1M”, etc. This is useful for dashboards and statistics screens.
import java.text.NumberFormat;
import java.util.Locale;
public class CompactNumberDemo {
public static void main(String[] args) {
// Korean locale — short format
NumberFormat shortKo = NumberFormat.getCompactNumberInstance(
Locale.KOREA, NumberFormat.Style.SHORT
);
System.out.println("1,000 -> " + shortKo.format(1000));
System.out.println("1,000,000 -> " + shortKo.format(1_000_000));
System.out.println("1,000,000,000 -> " + shortKo.format(1_000_000_000));
// English locale — long format
NumberFormat longEn = NumberFormat.getCompactNumberInstance(
Locale.US, NumberFormat.Style.LONG
);
System.out.println("2500 (EN LONG) -> " + longEn.format(2500));
// Set fraction digits
shortKo.setMaximumFractionDigits(1);
System.out.println("15,750 -> " + shortKo.format(15_750));
// Output:
// 1,000 -> 1K (locale-dependent)
// 1,000,000 -> 100M (locale-dependent)
// 1,000,000,000 -> 1B (locale-dependent)
// 2500 (EN LONG) -> 3 thousand
// 15,750 -> 15.8K (locale-dependent)
}
}
New String Methods — indent() and transform()
Java 12 added two convenience methods to the String class.
public class StringMethodsDemo {
public static void main(String[] args) {
// === indent(n): Add n spaces before each line (negative removes them) ===
String text = "First line\nSecond line\nThird line";
String indented = text.indent(4);
System.out.println("With indentation:");
System.out.println(indented);
// Output:
// First line
// Second line
// Third line
// === transform(): Apply a function to the string and return the result ===
String result = "hello, java 12"
.transform(String::toUpperCase)
.transform(s -> s.replace(" ", "_"))
.transform(s -> "[ " + s + " ]");
System.out.println(result);
// Output: [ HELLO,_JAVA_12 ]
// transform is useful for method chaining
// Transformations that previously needed intermediate variables can be chained like a pipeline
int length = "Java 12 features"
.transform(String::trim)
.transform(String::length);
System.out.println("String length: " + length);
// Output: String length: 16
}
}
transform() applies the functional pipeline pattern to String. It allows chaining transformations without intermediate variables, improving readability.
JMH Included by Default
Starting with Java 12, JMH (Java Microbenchmark Harness) is included in the JDK source tree (JEP 230). JMH is a tool that helps write accurate microbenchmarks by avoiding pitfalls like JVM warmup, JIT compilation, and dead code elimination.
Previously, JMH had to be added as a separate dependency, but from JDK 12 onward it was included by default to make it easier to track performance regressions in the JDK itself.
Summary
| Feature | Status | Key Points |
|---|---|---|
| Switch Expressions | Preview | Arrow syntax, value return, fall-through prevention |
| Shenandoah GC | Experimental | Low-latency GC independent of heap size |
| G1 GC Improvements | Final | Unused memory returned to OS, Abortable Mixed GC |
| CompactNumberFormat | Final | Concise number formatting like 1000 -> “1K” |
| String.indent() | Final | Per-line indentation add/remove |
| String.transform() | Final | Functional pipeline chaining |
| JMH Included | Final | Benchmark framework built into JDK source |
Java 12 marks the beginning of switch syntax modernization and expands JVM performance tuning options. Switch Expressions become finalized in Java 14, and Shenandoah GC is promoted to production-ready in Java 15. Don’t dismiss preview features — learning them early means a smoother transition when upgrading versions.