Javaバージョン判定は「使っていい機能の境界線を知る」ための技
業務システムでは、「このコードは Java 8 でも動かしたい」「Java 11 以上なら新しい API を使いたい」「古いランタイムで動いていたら警告を出したい」といったニーズが出てきます。
そのときに必要になるのが「今このプロセスは、どの Java バージョンで動いているのか」を判定する仕組みです。
Java は基本的に後方互換ですが、「新しい API を使うかどうか」「古いバージョンをサポート対象に含めるかどうか」は、実務ではかなり重要な判断材料になります。
だからこそ、Java バージョン判定をユーティリティとしてきちんと設計しておくと、コードがスッキリしつつ、安全な分岐が書けるようになります。
基本:System.getProperty(“java.version”) でバージョン文字列を取る
まずは生の値を見てみる
Java のバージョンは、システムプロパティ java.version から取得できます。
public class JavaVersionBasic {
public static void main(String[] args) {
String version = System.getProperty("java.version");
System.out.println("java.version = " + version);
}
}
Java実行する Java のバージョンによって、例えばこんな値になります。
Java 8 なら "1.8.0_202" のような形式
Java 11 なら "11.0.22" のような形式
Java 17 なら "17.0.10" のような形式
ここでまず押さえておきたいのは、「Java 8 までは 1.8.x のような“1.”付きの表記、それ以降は 11.0.x のような素直な数字」という歴史的事情があることです。
このせいで、「単純に文字列比較するだけ」ではうまくいかないケースが出てきます。
文字列のまま比較するのが危険な理由
“11” と “8” はいいけれど、”1.8″ と “11” は?
例えば、こんなコードを書いたとします。
String version = System.getProperty("java.version");
if (version.startsWith("1.8")) {
System.out.println("Java 8 です");
} else {
System.out.println("Java 8 以外です");
}
Javaこれは「Java 8 かどうか」を判定するだけならまだマシですが、「Java 11 以上かどうか」を判定しようとして、こう書くと危険です。
String version = System.getProperty("java.version");
if (version.compareTo("11") >= 0) {
// Java 11 以上?
}
JavaJava 8 の "1.8.0_202" と "11" を文字列として比較すると、先頭の '1' 同士で同じになり、その後の '.' と '1' の比較で「"." のほうが小さい」と判定されてしまいます。
つまり、「文字列比較でバージョンの大小を判定する」のは、形式の違いもあってかなり危険です。
ここから分かる大事なポイントは、「バージョンは“数値の並び”として扱うべきで、文字列のまま比較してはいけない」ということです。
実務で使える Javaバージョン判定ユーティリティの最小形
メジャーバージョンを数値として取り出す
まずは、「メジャーバージョン(8, 11, 17 など)だけ分かればよい」という前提で考えます。java.version からメジャーバージョンを取り出すユーティリティを作ってみましょう。
public final class JavaVersion {
private static final int major = detectMajor();
private JavaVersion() {}
private static int detectMajor() {
String v = System.getProperty("java.version");
if (v == null || v.isBlank()) {
return -1;
}
// Java 8 以前: "1.8.0_202" → "1", "8", ...
// Java 9 以降: "11.0.22" → "11", "0", ...
String[] parts = v.split("\\.");
try {
int first = Integer.parseInt(parts[0]);
if (first == 1 && parts.length > 1) {
// "1.8" のような形式 → 2番目がメジャー
return Integer.parseInt(parts[1]);
}
// それ以外は先頭がメジャー
return first;
} catch (NumberFormatException e) {
return -1;
}
}
public static int major() {
return major;
}
public static boolean isAtLeast(int requiredMajor) {
return major >= requiredMajor;
}
public static boolean isJava8() {
return major == 8;
}
public static boolean isJava11OrLater() {
return major >= 11;
}
public static boolean isJava17OrLater() {
return major >= 17;
}
}
Java使う側はこう書けます。
if (JavaVersion.isJava11OrLater()) {
System.out.println("Java 11 以上なので、新しい API を使えます");
} else {
System.out.println("Java 11 未満なので、古い書き方にしておきます");
}
Javaここで深掘りしたいポイントは三つです。
一つ目は、「java.version の形式の違い(1.8 系と 11 以降)をユーティリティの中で吸収している」ことです。
呼び出し側は「8 か 11 か 17 か」だけを意識すればよく、文字列のパースロジックを気にしなくて済みます。
二つ目は、「メジャーバージョンを int として持っている」ことです。
これにより、「11 以上か」「17 以上か」といった判定を、数値比較で安全に行えます。
三つ目は、「isJava11OrLater のようなメソッド名に“意図”が乗っている」ことです。
呼び出し側は if (JavaVersion.isJava11OrLater()) と書くだけで、「ここは Java 11 以上でだけ有効なコードだな」と一目で分かります。
バージョンによってコードを分岐させる具体例
例:Java 11 以上なら Files.readString を使う
Java 11 から Files.readString(Path) という便利メソッドが追加されました。
Java 8 でも同じことはできますが、new String(Files.readAllBytes(path), StandardCharsets.UTF_8) のように少し冗長です。
「Java 11 以上なら新しい API を使い、Java 8 では古い書き方を使う」ユーティリティを作ってみます。
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
public final class FileReaders {
private FileReaders() {}
public static String readUtf8(Path path) throws IOException {
if (JavaVersion.isJava11OrLater()) {
// Java 11 以降なら新しい API
return Files.readString(path);
} else {
// Java 8 など古いバージョン向け
byte[] bytes = Files.readAllBytes(path);
return new String(bytes, StandardCharsets.UTF_8);
}
}
}
Java呼び出し側は、Java バージョンを意識せずにこう書けます。
String content = FileReaders.readUtf8(Path.of("config.yml"));
Javaここでの重要ポイントは、「バージョンごとの差分を“呼び出し側”ではなく“ユーティリティ側”に寄せる」ことです。
アプリのあちこちで if (JavaVersion.isJava11OrLater()) と書き始めると、バージョン依存が散らばってしまいます。
「ファイル読み込み」「日時 API」「HTTP クライアント」など、バージョンで分岐するものは、できるだけ専用ユーティリティに閉じ込めるのがきれいです。
起動時に Java バージョンをログに出しておく実務テク
「どのバージョンで動いていたか」を後から追えるようにする
本番でトラブルが起きたとき、「このプロセスはどの Java バージョンで動いていたのか」が分からないと、原因調査が難しくなります。
そこで、アプリ起動時に Java バージョンをログに出しておくのは、実務でよく効くテクニックです。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class JavaVersionLogger {
private static final Logger log = LoggerFactory.getLogger(JavaVersionLogger.class);
private JavaVersionLogger() {}
public static void logJavaVersion() {
String raw = System.getProperty("java.version");
int major = JavaVersion.major();
log.info("Running on Java version: raw='{}', major={}", raw, major);
}
}
Java起動時に一度だけこう呼びます。
JavaVersionLogger.logJavaVersion();
Javaこれで、「どの Java バージョンで」「どのメジャーバージョンとして判定されていたか」がログから追えるようになります。
ここでのポイントは、「バージョンを“使うときだけ”でなく、“起動時に記録しておく”」という発想です。
Javaバージョン判定で気をつけるべきこと
「本当にバージョンで分岐すべきか?」を一度立ち止まる
Java バージョン判定は便利ですが、「本当は“ターゲットバージョンを上げる”ほうが正しいのに、無理に分岐で頑張ってしまう」パターンもあります。
例えば、プロジェクト全体として「Java 11 以上をサポート対象にする」と決められるなら、
わざわざ Java 8 向けの分岐を書かずに、新しい API だけを使ったほうがコードはシンプルになります。
逆に、「どうしても Java 8 と 11 の両方をサポートしなければならない」状況なら、
今回のようなユーティリティを使って、バージョン依存をきれいに隔離する価値があります。
つまり、「バージョン判定は“最後の手段”であり、まずはサポートバージョンの整理から考える」という感覚を持っておくと健全です。
バージョン文字列の形式に過度に依存しない
java.version の形式は、将来また変わる可能性があります。
今は「8 までは 1.8 系、それ以降は 11.0 系」というルールですが、
マイナーバージョンやパッチバージョンの付け方が変わることもあり得ます。
そのため、ユーティリティの中では「壊れにくいパース」を心がけるとよいです。
例えば、「先頭の数字を取る」「先頭が 1 で次が 8 なら 8 とみなす」といった、ある程度ゆるいロジックにしておくと、変化に耐えやすくなります。
まとめ:Javaバージョン判定ユーティリティで身につけるべき感覚
Javaバージョン判定は、「ただバージョンを知る」だけではなく、「どのバージョンまでサポートし、どこから新しい機能を使うか」を設計する行為です。
押さえておきたいポイントは次の通りです。
System.getProperty("java.version") をそのまま文字列比較せず、メジャーバージョンを数値として取り出す。
Java 8 の "1.8.x" と Java 11 以降の "11.0.x" の形式差を、ユーティリティの中で吸収する。JavaVersion.isJava11OrLater() のようなメソッドで、「どのバージョン以上か」を意図ごと表現する。
バージョンごとの差分(新旧 API の切り替え)は、呼び出し側ではなく専用ユーティリティに閉じ込める。
起動時に Java バージョンをログに出しておくことで、運用・調査がぐっと楽になる。
