Java Tips | 基本ユーティリティ:Javaバージョン判定

Java Java
スポンサーリンク

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 以上?
}
Java

Java 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 バージョンをログに出しておくことで、運用・調査がぐっと楽になる。

タイトルとURLをコピーしました