タイトル: static変数
SEOタイトル: Java static変数(クラス変数 / インスタンス変数との違い / 使い所と落とし穴)
| この記事の要点 |
|
static 変数とは
Java の static 変数(クラス変数)は、フィールド宣言に static 修飾子を付けた変数です。最大の特徴は「インスタンスを何個作っても、変数の実体はクラスに 1 つしか存在しない」という点。クラスがロードされたタイミングで JVM のメソッドエリアに領域が確保され、プログラム終了まで存続します。
| 観点 | インスタンス変数 | static 変数 |
|---|---|---|
| 実体の数 | インスタンスごとに 1 つ | クラス全体で 1 つ |
| 初期化タイミング | new 時 | クラスロード時に 1 度だけ |
| アクセス方法 | instance.field | ClassName.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::$var や ClassName::$var でアクセス、Java は ClassName.var。アクセス演算子が違うだけで意味はほぼ同じです。
Q: 定数を public で公開すると危なくない?
A: final も付けていれば書き換え不能なので公開して問題ありません。ただし可変オブジェクト(List など)を public static final にしても中身は変更できる点に注意。List.of() など不変コレクションで包むのが安全。