Java 21(前 LTS)と Java 25(最新 LTS)の新機能・改善点をコードで体感するためのサンプルプロジェクトです。 同じ機能を両バージョンで実装し、Preview → Finalized の変化や API の変更を実際に動かして確認できます。
java_sample/
├── java21/ # Java 21 LTS で動くデモ (前 LTS)
└── java25/ # Java 25 LTS で動くデモ (最新 LTS)
各モジュールに同名または対応するデモクラスが存在し、バージョン間の差分をコードレベルで比較できます。
| ツール | バージョン | 備考 |
|---|---|---|
| Gradle | 8.14 以上 | gradlew スクリプト同梱のため個別インストール不要 |
| JDK | 21 / 25 | インストール不要 — Gradle が自動でダウンロード(Foojay Toolchain Resolver) |
JDK の自動ダウンロードについて
settings.gradle.ktsにfoojay-resolver-conventionプラグインを設定済みです。 初回実行時に JDK 21・25 が~/.gradle/jdks/に自動取得されます。
# Java 21 LTS デモを実行
./gradlew :java21:run
# Java 25 LTS デモを実行
./gradlew :java25:run
# 両バージョンを続けて実行して比較
./gradlew :java21:run :java25:run| # | 機能 | Java 21 | Java 25 | JEP |
|---|---|---|---|---|
| ① | Virtual Threads | ✅ Finalized (ピン留めあり) | ✅ ピン留め解消 | 444 / 491 |
| ② | Scoped Values | ✅ Finalized | 446 → 506 | |
| ③ | Structured Concurrency | 453 → 505 | ||
| ④ | Record Patterns | ✅ Finalized | 変化なし | 440 |
| ⑤ | Pattern Matching for switch | ✅ Finalized | 変化なし | 441 |
| ⑥ | Sequenced Collections | ✅ Finalized | 変化なし | 431 |
| # | 機能 | Java 21 | Java 25 | JEP |
|---|---|---|---|---|
| ⑦ | Stream Gatherers | ❌ なし | ✅ Finalized | 485 |
| ⑧ | Flexible Constructor Bodies | ❌ なし | ✅ Finalized | 513 |
| ⑨ | Module Import Declarations | ❌ なし | ✅ Finalized | 511 |
✅ =
--enable-preview不要 /⚠️ =--enable-preview必要 / ❌ = 未サポート
JEP 444 (Java 21 Finalized) → JEP 491 (Java 24 改善)
Java 21 で Finalized になった Virtual Threads。ただし Java 21 では synchronized ブロック内でキャリアスレッドへの「ピン留め」が発生し、スループットが低下する問題があった。Java 24 (JEP 491) でこの問題が解消され、Java 25 でも改善が継続。
| バージョン | synchronized 内での挙動 |
|---|---|
| Java 21 | ピン留め発生 → ReentrantLock を推奨 |
| Java 25 | ピン留め解消 → synchronized も安全 |
// Java 21: ReentrantLock を使うのが推奨だった
var lock = new ReentrantLock();
executor.submit(() -> {
lock.lock();
try { doWork(); } finally { lock.unlock(); }
});
// Java 25: synchronized でも安全 (JEP 491)
executor.submit(() -> {
synchronized (MONITOR) { doWork(); } // ピン留めなし
});📄 VirtualThreadsDemo.java(java21 / java25 でコメントが異なる)
JEP 446(Java 21: 1st Preview) → JEP 506(Java 25: Finalized)
スレッドローカル変数の安全な代替。Java 21 では 1st Preview として登場し、4 バージョンの改良を経て Java 25 で Finalized。
| 比較項目 | ThreadLocal | ScopedValue |
|---|---|---|
| 可変性 | ミュータブル | イミュータブル |
| スコープ | 不明確 | 明示的(where-run) |
| コスト | 相対的に高い | 低い |
private static final ScopedValue<String> CURRENT_USER = ScopedValue.newInstance();
ScopedValue.where(CURRENT_USER, "Alice").run(() -> {
System.out.println(CURRENT_USER.get()); // → Alice
ScopedValue.where(CURRENT_USER, "Admin").run(() ->
System.out.println(CURRENT_USER.get()) // → Admin
);
System.out.println(CURRENT_USER.get()); // → Alice(元に戻る)
});Java 21 と Java 25 のコードは全く同じ。 違いは
java21/build.gradle.ktsの--enable-previewフラグのみ。
📄 ScopedValuesDemo.java(java21 / java25 共通)
JEP 453(Java 21: 1st Preview) → JEP 505(Java 25: 5th Preview)
関連する並行タスクをひとつのスコープで管理する API。 Java 25 で API が Breaking Change を含む大幅刷新。
// コンストラクタでポリシーを指定
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
var user = scope.fork(() -> fetchUser());
var order = scope.fork(() -> fetchOrder());
scope.join().throwIfFailed(); // ← 手動でエラーチェック
System.out.println(user.get() + order.get());
}
// 最速結果を採用
try (var scope = new StructuredTaskScope.ShutdownOnSuccess<String>()) {
scope.fork(() -> fetchFromPrimary());
scope.fork(() -> fetchFromCache());
scope.join();
String result = scope.result(); // ← 別メソッドで結果取得
}// static factory method + Joiner パターン
try (var scope = StructuredTaskScope.open(
StructuredTaskScope.Joiner.<String>allSuccessfulOrThrow())) {
scope.fork(() -> fetchUser());
scope.fork(() -> fetchOrder());
scope.join() // ← join() が Stream<Subtask> を返す
.map(Subtask::get)
.forEach(System.out::println);
}
// 最速結果を採用
try (var scope = StructuredTaskScope.open(
StructuredTaskScope.Joiner.<String>anySuccessfulResultOrThrow())) {
scope.fork(() -> fetchFromPrimary());
scope.fork(() -> fetchFromCache());
String result = scope.join(); // ← join() が直接結果を返す
}📄 StructuredConcurrencyDemo.java(java21 / java25 で異なる実装)← 最も差分が大きい
JEP 440 / Java 21 Finalized
instanceof や switch でレコードのコンポーネントを直接分解できる。ネストしたレコードも一度に分解可能。Java 25 でも変化なし。
record Point(int x, int y) {}
record Line(Point start, Point end) {}
// ネストした Record の分解
if (line instanceof Line(Point(int x1, int y1), Point(int x2, int y2))) {
System.out.println("Line: (" + x1 + "," + y1 + ") → (" + x2 + "," + y2 + ")");
}📄 RecordPatternsDemo.java(java21 のみ ─ Java 21 固有デモ)
JEP 441 / Java 21 Finalized
switch での型パターン・null ハンドリング・guard 条件 (when) が Finalized。Java 25 でも変化なし。
String desc = switch (obj) {
case Integer i when i < 0 -> "負の整数: " + i;
case Integer i -> "正の整数: " + i;
case String s -> "文字列: " + s;
case null -> "null!";
default -> "その他";
};📄 PatternMatchingSwitchDemo.java(java21 のみ ─ Java 21 固有デモ)
JEP 431 / Java 21 Finalized
List・Set・Map に getFirst()・getLast()・addFirst()・reversed() など統一 API を追加。Java 25 でも変化なし。
var list = new ArrayList<>(List.of("A", "B", "C"));
list.getFirst(); // → "A" (Java 21 以前: list.get(0))
list.getLast(); // → "C" (Java 21 以前: list.get(list.size() - 1))
list.reversed(); // → ["C", "B", "A"]📄 SequencedCollectionsDemo.java(java21 のみ ─ Java 21 固有デモ)
JEP 485 / Java 24 Finalized(Java 21 にはない)
Stream に任意の中間操作を追加できる拡張 API。windowFixed・scan・fold などの組み込み Gatherer を提供。
// 3つずつのグループに分割
List.of(1, 2, 3, 4, 5, 6, 7)
.stream()
.gather(Gatherers.windowFixed(3))
.forEach(System.out::println);
// → [1, 2, 3] [4, 5, 6] [7]📄 StreamGatherersDemo.java(java25 のみ)
JEP 513 / Java 25 Finalized(Java 21 にはない)
コンストラクタ内で super() / this() の前にコードを書けるようになった。引数の検証・変換をコンストラクタ内で完結できる。
class Circle extends Shape {
Circle(double radius) {
double validated = Math.abs(radius); // ← super() より前に検証!
super("circle", validated);
}
}📄 FlexibleConstructorDemo.java(java25 のみ)
JEP 511 / Java 25 Finalized(Java 21 にはない)
import module <モジュール名>; 1行でモジュール全体をインポートできる。
// Java 25 Finalized
import module java.base; // ← これ1行で java.base の全型が使える!
public class Example {
List<String> list = List.of("Java", "25"); // import なしで使える
Map<String, Integer> map = new HashMap<>();
}📄 ModuleImportDemo.java / ModuleImportExample.java(java25 のみ)
tasks.withType<JavaCompile> {
options.compilerArgs.addAll(listOf("--enable-preview"))
options.release = 21
}
// --enable-preview が必要な機能:
// ScopedValues (JEP 446 - 1st Preview)
// Structured Concurrency (JEP 453 - 1st Preview)
// --enable-preview 不要:
// Virtual Threads, Record Patterns, Pattern Matching for switch, Sequenced Collectionstasks.withType<JavaCompile> {
options.compilerArgs.addAll(listOf("--enable-preview"))
options.release = 25
}
// まだ --enable-preview 必要:
// Structured Concurrency (JEP 505 - 5th Preview)
// --enable-preview 不要 (Finalized):
// Scoped Values, Stream Gatherers, Flexible Constructor Bodies, Module Import Declarations| 項目 | 内容 |
|---|---|
| 言語 | Java 21 (LTS) / Java 25 (LTS) |
| ビルドツール | Gradle 8.14(Kotlin DSL) |
| JDK 管理 | Gradle Toolchain + Foojay Resolver(自動ダウンロード) |