パソコンのこと

Java 26 は入れるべき? 良さそうな点と注意点を現場向けに整理

1 Mins read

Java 26 が 2026/03/17 に GA。

今回も preview 系は多いんだけど、HTTP/3、G1 GC、仮想スレッドまわりなど、地味に効きそうな変更が結構ある。

逆に、古い API や昔の JVM フラグをそのまま抱えてる環境だと、上げた時に引っかかりそうな点も見えてきた。

特に Java 8 からだと差分が大きいので、Java 26 の新機能だけ見るより、どこでハマるかを先に見たほうがいい感じ。

ざっくり図

Java 8 から Java 26 への移行フロー
Java 8 から Java 26 へ上げる前に、まず LTS で中間整理を入れる流れ

Java 8 運用中
	|
	+-- 古い API / ライブラリ棚卸し
	|      - javax.xml.bind
	|      - Thread.stop
	|      - sun.*
	|      - 古い JVM フラグ
	|
	+-- まずは LTS で検証
	|      - Java 17 か 21 で一度テスト
	|
	+-- その後 Java 26 の差分確認
			 - HTTP/3
			 - G1 改善
			 - 仮想スレッド周辺
			 - セキュリティ既定値の変化

良さそうな点

java.net.http.HttpClient が HTTP/3 対応になった。

アプリ側のコードを大きく変えずに、HTTP/3 が使える余地が広がるのは素直に便利そう。

あとは G1 GC の同期削減、巨大オブジェクト回収の改善、AOT Object Cache の Any GC 対応など、性能系の修正がわりと堅い。

仮想スレッドも、クラス初期化待ちで carrier thread を握りっぱなしにしにくくなってるので、変な詰まり方を減らせそう。

地味にうれしいのはこのへん。

  • ProcessAutoCloseable 対応
  • UUID.ofEpochMillis() が追加されて UUIDv7 系の扱いがしやすい
  • ByteOrder が enum 化されて switch で扱いやすい

例えば HTTP Client は、こんな感じでコードの見た目を大きく変えずに恩恵を受けやすい。

var client = HttpClient.newBuilder()
	.version(HttpClient.Version.HTTP_3)
	.build();

var request = HttpRequest.newBuilder()
	.uri(URI.create("https://example.com/api/status"))
	.GET()
	.build();

var response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode());

それと ProcessAutoCloseable になったので、外部コマンド実行の後始末も少し素直になる。

try (var process = new ProcessBuilder("java", "-version").start()) {
	var exitCode = process.waitFor();
	System.out.println("exit=" + exitCode);
}

注意したい点

まず Thread.stop() は削除済み。

昔の保守コードに残っていると、JDK 26 ではもうコンパイル段階で止まる。

Applet API も削除されているので、古い資料やサンプルをそのまま引っ張っている環境は当然ながら要注意。

それと、JVM フラグ整理も進んでいて、-XmaxjitcodesizeMaxRAMAggressiveHeap あたりを惰性で使っていると見直し対象になる。

RMI over TLS は endpoint identification が既定で効くので、証明書の SAN が雑な環境は通信失敗が出るかもしれない。

HttpRequest.Builder.timeout() がレスポンス本文受信まで含むようになった点も、既存の timeout 設計次第では体感差が出そう。

あと Java 8 のまま長く運用していた環境は、Java 26 へ一気に上げる前に、新旧対比でこのへんを意識したほうがいい。

  • Java 8 は classpath 前提で動いていたが、Java 9 以降のモジュール境界や内部 API 依存が表に出やすい
  • Java 11 で Java EE / CORBA 系モジュールが削除されているので、javax.xml.bind などが残っていると別途対応が要る
  • 反射やセキュリティ既定値が厳しくなっていて、昔は動いていたコードが警告や失敗になることがある
  • 古い TLS 設定、keystore、RMI 通信は上げた直後に事故りやすい

それと日本の業務システムだと、文字コードも地味に本丸。

特に銀行系や基幹系は、バックオフィスやホスト連携でいまだに Shift_JIS 系が前提のことがある。

ここで雑に「今どき UTF-8 でしょ」と寄せると、画面は動くのに帳票や外部連携だけ文字化け、みたいな嫌な事故が起きやすい。

Java 8 の時代は、Windows 上でたまたま default charset が windows-31j になっていて動いていたコードが結構ある。

でも JDK 18 以降は default charset が UTF-8 基本になっているので、new String(bytes)FileReader のような「暗黙の文字コード頼み」は、そのまま持っていくと差分になりやすい。

さらに実務では Shift_JISwindows-31j を同一視しすぎないほうがいい。

Java の charset 一覧上はどちらも使えるけど、windows-31j / MS932 は Windows 系拡張を含むので、丸数字、環境依存文字、IBM/NEC 拡張の扱いで期待値がズレることがある。

銀行系のファイル授受やホスト接続だと、相手が求めているのは「Shift_JIS と言いながら実質 CP932」なのか、「本当に Shift_JIS の範囲だけ」なのか、あるいは IBM 系ホストコードまで含むのかを先に確認したほうが安全。

日本語周りで見るなら、このへんは移行前チェックに入れておいたほうがいい。

  • バイト列変換で charset を明示しているか
  • Shift_JISwindows-31j を混同していないか
  • 丸数字、波ダッシュ、全角チルダ、機種依存文字、外字の round-trip を確認したか
  • 帳票 CSV、固定長ファイル、ホスト送受信の文字単位とバイト単位を取り違えていないか
  • 置換文字で握りつぶさず、変換不能文字を検知できるか

なので、Java 8 からなら「いきなり 26 本番」より、まず 17 か 21 の LTS でビルドとテストを通して、そこで古い依存を削ってから 26 を見るほうが現実的。

Java 26 自体は面白いんだけど、Java 8 からの差分を吸収する作業のほうが実務では重いはず。

上げる前に見るところ

まずは古い API とフラグを雑にでも洗うのが早い。

grep -R "Thread\.stop\|Xmaxjitcodesize\|AggressiveHeap\|MaxRAM" ./

Java 8 由来の依存もまとめて見たいなら、これも追加で流しておくと良さそう。

grep -R "javax\.xml\.bind\|javax\.activation\|CORBA\|sun\." ./

文字コード前提を雑に洗うなら、このへんも引っかけておくと見落としが減る。

grep -R "Shift_JIS\|MS932\|windows-31j\|Cp943\|Cp930\|EBCDIC\|file.encoding" ./

Java 側でも、暗黙の default charset に乗らず、変換不能文字を検知する書き方に寄せたほうが事故りにくい。

var charset = Charset.forName("windows-31j");
var encoder = charset.newEncoder()
	.onMalformedInput(CodingErrorAction.REPORT)
	.onUnmappableCharacter(CodingErrorAction.REPORT);

var bytes = encoder.encode(CharBuffer.wrap("顧客コード①"));
System.out.println(bytes.remaining());

ざっくり比較するとこんな感じ。

  • Java 8 から見た Java 26: 差分が大きいので移行案件
  • Java 17 から見た Java 26: 新機能評価と既定値差分の確認が中心
  • Java 21 から見た Java 26: 追従コストは比較的軽め

もう少し実務寄りに並べるとこう。

観点 Java 8 Java 17 Java 21 Java 26
現場での立ち位置 古い基盤がまだ多い まず移行先として堅い いまの主力候補 先行評価と追従候補
移行難易度 ここから上げるのが重い 8 からの受け皿にしやすい 17 からなら延長しやすい 21 以降からなら比較的軽い
気にする点 Java EE 系削除、内部 API 依存 反射や module 境界 仮想スレッド導入判断 HTTP/3、GC改善、既定値差分
向いている進め方 まず棚卸し 先に CI を通す 本番標準化しやすい 検証環境で差分確認

Java 8 の案件だと、Java 26 の新機能に目が行く前に、まず Java 8 時代の負債をどう剥がすかのほうが本題になりやすい。

逆にすでに Java 17 や 21 にいるなら、Java 26 は「全面移行」というより、性能改善や既定値変更をどう取り込むかを見るフェーズだと思う。

あとはこのあたりを CI で確認しておくと安心。

  • HttpClient の timeout やヘッダまわり
  • 証明書検証が絡む RMI / TLS 通信
  • jlink を使ったランタイム作成
  • XML Signature や古い keystore 依存
  • Shift_JIS / windows-31j / ホスト連携ファイルの round-trip テスト

preview / incubator 機能は面白いんだけど、本番投入というよりは評価用として見たほうがまだ自然かなという印象。

まとめ

Java 26 は派手な一撃というより、性能、標準 API、運用上の安全側変更が積まれている感じ。

普通の業務システムだと、HTTP/3、GC、仮想スレッド改善あたりは前向き。

一方で、古いコードや古い運用フラグが残っている現場ほど、先に棚卸ししてから上げたほうが事故は少なそう。

日本語環境なら、特に文字コードは後回しにしないほうがいい。

Shift_JIS 系やホスト連携が残っている現場では、Java 26 の良さを評価する前に、default charset 依存と日本語 round-trip をつぶすほうが優先度が高いはず。

特に Java 8 からなら、先に 17 か 21 で中間整理を入れて、そのうえで Java 26 の恩恵を取りに行くほうが筋が良さそう。