この内容は古いバージョンです。最新バージョンを表示するには、戻るボタンを押してください。
バージョン:5
ページ更新者:guest
更新日時:2026-06-11 07:07:02

タイトル: static変数
SEOタイトル: Java static変数(クラス変数 / インスタンス変数との違い / 使い所と落とし穴)

この記事の要点
  • static 変数(クラス変数)クラスに 1 つだけ存在する変数。インスタンスを何個作っても同じ値を共有
  • インスタンス変数(フィールド)は new ごとに別領域。static 変数はクラスロード時に 1 度だけ初期化
  • アクセスは クラス名.変数名 が推奨(インスタンス経由でも書けるが誤読を招く)
  • 定数は public static final で、設定値や共有カウンタなど本当にプロセス全体で 1 個でよいものに限定
  • マルチスレッドから書き換える場合は同期が必須。安易な可変 static は不具合の温床

static 変数とは

Java の static 変数(クラス変数)は、フィールド宣言に static 修飾子を付けた変数です。最大の特徴は「インスタンスを何個作っても、変数の実体はクラスに 1 つしか存在しない」という点。クラスがロードされたタイミングで JVM のメソッドエリアに領域が確保され、プログラム終了まで存続します。

観点インスタンス変数static 変数
実体の数インスタンスごとに 1 つクラス全体で 1 つ
初期化タイミングnewクラスロード時に 1 度だけ
アクセス方法instance.fieldClassName.field
破棄GC 対象クラスがアンロードされるまで生存
用途個別の状態共有設定、カウンタ、定数

基本構文

public class Counter {
    public static int total = 0;   // クラス変数
    private int id;                // インスタンス変数

    public Counter() {
        total++;             // 全インスタンス共通のカウンタを増加
        this.id = total;     // 個別 ID を割り当て
    }
}

public class Main {
    public static void main(String[] args) {
        new Counter();
        new Counter();
        new Counter();
        System.out.println(Counter.total);  // 3
    }
}

3 回 new しても total は 1 つしか存在しないため、3 までインクリメントされます。

static final — 定数

static final を付けると定数になります。一度初期化したら以降変更不可。命名は大文字スネークケースが慣習。

public class Config {
    public static final int    MAX_RETRY    = 3;
    public static final String API_BASE_URL = "https://api.example.com";
    public static final double PI           = 3.14159265358979;
}

// 利用側
for (int i = 0; i < Config.MAX_RETRY; i++) {
    // ...
}

static イニシャライザ

複雑な初期化が必要な場合は static { ... } ブロックを使います。クラスロード時に 1 度だけ実行されます。

public class Lookup {
    public static final Map RANKS = new HashMap<>();

    static {
        RANKS.put("Bronze", 1);
        RANKS.put("Silver", 2);
        RANKS.put("Gold",   3);
    }
}

使うべきとき / 使うべきでないとき

使ってよい

  • 定数public static final で設定値、エラーコード、上限値
  • ユーティリティの内部状態Math.PI のような不変値
  • シングルトンへのアクセサSingleton.getInstance()
  • ロガーprivate static final Logger log = LoggerFactory.getLogger(...)

避けるべき

  • 可変 static フィールド — グローバル変数化してテストが困難、スレッドセーフでない
  • キャッシュ的な用途 — 明示的にライフサイクル管理できる仕組み(Guava Cache、Caffeine)に置き換える
  • 状態を持たせるサービスクラス — DI で生成する通常のクラスにする

マルチスレッドの注意

static 変数はすべてのスレッドが共有するため、可変なら同期が必須。

public class Counter {
    // 危険: 競合する可能性あり
    public static int unsafe = 0;

    // 安全: 原子的にインクリメント
    public static final AtomicInteger safe = new AtomicInteger(0);

    public static synchronized void inc() {
        unsafe++;  // synchronized で守る
    }
}

よくある質問

Q: インスタンス経由で static 変数にアクセスできる?
A: コンパイルは通りますが、警告が出ます。instance.STATIC_FIELD と書くと「実はインスタンスを使ってない」ことが読みにくくなるため、必ず ClassName.STATIC_FIELD で書きましょう。

Q: PHP の static との違いは?
A: PHP も同じく「クラスに 1 つだけ」の概念ですが、PHP は self::$varClassName::$var でアクセス、Java は ClassName.var。アクセス演算子が違うだけで意味はほぼ同じです。

Q: 定数を public で公開すると危なくない?
A: final も付けていれば書き換え不能なので公開して問題ありません。ただし可変オブジェクト(List など)を public static final にしても中身は変更できる点に注意。List.of() など不変コレクションで包むのが安全。