タイトル: staticキーワード
SEOタイトル: Java static キーワード完全ガイド
| この記事の要点 |
|
static とは何か
static は Java(C# / C++ も同様)のキーワードで、クラス全体で共有されるメンバを宣言します。インスタンス(new したオブジェクト)ごとに別の値を持つ通常メンバとは対照的です。
| 種類 | 所属 | アクセス方法 |
|---|---|---|
| インスタンス変数 | 各オブジェクト | obj.field |
| static 変数 | クラス全体 | ClassName.field |
| インスタンスメソッド | 各オブジェクト | obj.method() |
| static メソッド | クラス全体 | ClassName.method() |
static 変数 (クラス変数)
public class Counter {
public static int total = 0; // ★ static 変数(全インスタンス共有)
private int id; // インスタンス変数(個別)
public Counter() {
total++; // 共有値をインクリメント
this.id = total;
}
}
Counter c1 = new Counter(); // total = 1, c1.id = 1
Counter c2 = new Counter(); // total = 2, c2.id = 2
Counter c3 = new Counter(); // total = 3, c3.id = 3
System.out.println(Counter.total); // ★ クラス名でアクセス: 3
System.out.println(c1.id); // 1
static メソッド
インスタンス化せずに呼べるメソッド。状態を持たない純粋関数に向きます。
public class MathUtils {
public static int square(int n) {
return n * n;
}
public static double average(int[] nums) {
int sum = 0;
for (int n : nums) sum += n;
return (double) sum / nums.length;
}
}
// new せずに呼べる
int sq = MathUtils.square(5); // 25
double avg = MathUtils.average(new int[]{1,2,3}); // 2.0
// 標準ライブラリの例
double pi = Math.PI; // static 定数
double s = Math.sqrt(16.0); // static メソッド
int n = Integer.parseInt("42"); // static メソッド
String s2 = String.valueOf(123); // static メソッド
static の制約
| 制約 | 説明 |
|---|---|
| this 不可 | static メソッドからは this を使えない(インスタンスが無い) |
| インスタンスメンバアクセス不可 | static から非 static フィールドを直接参照できない |
| オーバーライド不可 | static は隠蔽 (hiding) されるだけ。多態性なし |
| abstract と併用不可 | abstract static は無意味(abstract は実体オーバーライド前提) |
static 初期化ブロック
クラスがロードされる時に1 度だけ実行されるブロック。複雑な初期化に。
public class Config {
public static final Map<String, String> SETTINGS;
static {
// クラスロード時に 1 回だけ実行される
SETTINGS = new HashMap<>();
SETTINGS.put("env", System.getenv("APP_ENV"));
SETTINGS.put("debug", System.getenv("APP_DEBUG"));
// ファイルから設定読込なども可能
}
// インスタンス初期化ブロック(static 無し)は new ごとに実行
{
System.out.println("instance init");
}
}
static インナークラス (Nested Class)
外部クラスのインスタンスに依存しない内部クラス。普通の内部クラス (inner class) と違い、外部の this を持たないのでメモリリークを防げます。
public class Outer {
private int x;
// ❌ 普通の内部クラス: Outer の this を暗黙保持 → リーク要注意
class Inner {
void show() { System.out.println(x); } // 外部 x にアクセス可
}
// ✅ static インナークラス: Outer のインスタンス無しで作れる
static class StaticInner {
void show() { System.out.println("hi"); }
// System.out.println(x); ← ★ コンパイルエラー: static から非 static 不可
}
}
// 普通の内部: 外部インスタンスが必要
Outer.Inner inner = new Outer().new Inner();
// static: 外部不要
Outer.StaticInner si = new Outer.StaticInner();
static import
毎回 Math. と書きたくない場合に使う構文(Java 5+)。
// 通常
import java.lang.Math;
double a = Math.PI * Math.sqrt(16);
// static import
import static java.lang.Math.PI;
import static java.lang.Math.sqrt;
double b = PI * sqrt(16); // クラス名省略可
// ワイルドカード
import static java.lang.Math.*;
double c = PI * sqrt(16) + abs(-3);
// 注意: 乱用するとどのクラスのメソッドか分からなくなる
// JUnit5 の assertEquals / assertTrue 等で常用される
import static org.junit.jupiter.api.Assertions.*;
@Test
void testAdd() {
assertEquals(4, 2 + 2); // クラス名不要で読みやすい
}
シングルトンパターンと static
public class Logger {
private static Logger instance; // ★ static フィールドで唯一性を保証
private Logger() { } // 外部から new 禁止
public static synchronized Logger getInstance() {
if (instance == null) {
instance = new Logger();
}
return instance;
}
public void log(String msg) {
System.out.println("[LOG] " + msg);
}
}
Logger.getInstance().log("hello");
ユーティリティクラス
状態を持たない static メソッドの集まり。private コンストラクタでインスタンス化を防ぐのが定石。
public final class StringUtils {
private StringUtils() { // ★ インスタンス化禁止
throw new AssertionError("no instances");
}
public static boolean isBlank(String s) {
return s == null || s.trim().isEmpty();
}
public static String reverse(String s) {
return new StringBuilder(s).reverse().toString();
}
}
StringUtils.isBlank(" "); // true
// StringUtils s = new StringUtils(); ← コンパイル可だが実行時 AssertionError
static のメモリ管理
| 領域 | JVM バージョン | 説明 |
|---|---|---|
| PermGen | Java 7 以前 | クラス定義 / static フィールドが入る固定領域。サイズ不足で PermGen space エラー |
| Metaspace | Java 8+ | ネイティブメモリに移動。デフォルト無制限(最大値設定可: -XX:MaxMetaspaceSize) |
static フィールドはクラスがアンロードされるまで解放されません。巨大なオブジェクトを static に保持するとメモリリークの原因に。
static のアンチパターン
- グローバル状態の濫用: テストごとに状態がリセットされず、テスト独立性が失われる
- static でないとできない処理: ほとんどないので、可能なら DI で渡す
- static 変数で動作変更: ある画面でセットした値が別画面に影響
- Singleton の濫用: テストでモック注入が困難。代わりに DI コンテナを推奨
FAQ
Q: static メソッドはなぜテストしにくい?
A: モック化が困難(Mockito 標準ではモック不可、PowerMock や mockito-inline で対応)。可能ならインスタンスメソッドにして DI で差し替えられるように。
Q: static final と final static の違いは?
A: 順序は任意で同じ意味。慣習的に static final の順。public static final で「クラス定数」。
Q: PHP の static:: は?
A: 遅延静的束縛 (Late Static Binding)。self:: は宣言クラス、static:: は呼び出し元クラスを指します。継承時の挙動が変わります。