Java | 基礎文法:static フィールド

Java Java
スポンサーリンク

Static フィールドの全体像

static フィールド(クラス変数)は「クラスに属する共有の状態」です。インスタンスを何個作っても、static フィールドは1つだけ存在し、全インスタンスから同じ値が見えます。用途は「定数」「設定値」「全体カウンタ」など。強力ですが、設計とテストを難しくするため、使いどころを絞るのがコツです。


基本構文と動き

宣言・アクセスの最小例

public class Counter {
    public static int total = 0; // 全インスタンスで共有

    private int value;
    public Counter() {
        total++; // 生成のたびに全体カウンタを増やす
    }
    public int get() { return value; }
}
Java
public class Demo {
    public static void main(String[] args) {
        System.out.println(Counter.total); // クラス名から直接参照(0)
        var c1 = new Counter();
        var c2 = new Counter();
        System.out.println(Counter.total); // 2(共有)
    }
}
Java

static フィールドは「クラス名.フィールド名」で直接アクセスできます。インスタンスからも参照できますが、意図が曖昧になるためクラス名からアクセスするのが読みやすいです。

インスタンスフィールドとの違い

class Sample {
    public static int shared = 0; // 共有
    public int value = 0;         // 個別
}
Java
  • static は「全体共有」。1つだけ存在。
  • 非 static は「各インスタンスごと」。それぞれ独立。

重要ポイントの深掘り:初期化・定数・初期化順序

デフォルト値と明示初期化

class App {
    static int x;                // 0(数値のデフォルト)
    static String name;          // null(参照型のデフォルト)
    static boolean ready = true; // 明示初期化
}
Java

static フィールドは「クラス読み込み時」に初期化されます。参照型は null から始まるため、必ず有効値で初期化するか、使用前にガードを入れます。

定数は static final+ALL_CAPS

public class Units {
    public static final double PI = 3.141592653589793;
    public static final String APP_NAME = "MyApp";
}
Java
  • final を付けると再代入不可(定数)。
  • 不変(イミュータブル)な値を置くことで安全性が上がります。
  • 変更可能な static は「グローバル可変状態」になり、バグ源になりやすいので慎重に。

静的初期化ブロック(複雑な初期化)

public class Config {
    public static final java.util.Map<String, String> DEFAULTS;
    static {
        var m = new java.util.HashMap<String, String>();
        m.put("mode", "prod");
        m.put("locale", "ja_JP");
        DEFAULTS = java.util.Collections.unmodifiableMap(m); // 不変化
    }
}
Java

計算や例外を伴う初期化は static { ... } ブロックでまとめます。公開するのは不変ビュー(unmodifiable)にすると安全です。


重要ポイントの深掘り:設計の落とし穴と対策

グローバル状態の罠(テスト困難・予期せぬ依存)

可変の static に依存すると、テストが並列化しづらく、順序依存のバグが増えます。設定値は「引数で渡す」「コンストラクタ注入」「不変オブジェクトで保持」を優先しましょう。

// 悪い例:静的な可変設定
public class Price {
    public static double TAX_RATE = 0.1;
    public static int withTax(int price) { return (int) Math.round(price * (1 + TAX_RATE)); }
}

// 良い例:引数で渡す(テスト容易)
public class Price {
    public static int withTax(int price, double taxRate) {
        return (int) Math.round(price * (1 + taxRate));
    }
}
Java

競合(スレッドセーフ)への備え

複数スレッドから書き換える場合は同期やアトミック変数が必要です。不変にできるならそれが最善です。

class Global {
    private static final java.util.concurrent.atomic.AtomicInteger total = new java.util.concurrent.atomic.AtomicInteger(0);
    public static int incAndGet() { return total.incrementAndGet(); }
}
Java

ライフサイクルと再初期化

static は「JVMのクラスローダが生存する間」保持されます。再初期化は通常ありません。リセットが必要なテストでは、明示的なリセットメソッドを用意するか、可変状態を分離したテスト用設計にします。


アクセス修飾子と可視性の設計

公開範囲を最小に

public class Settings {
    private static String endpoint = "https://example.com"; // 外部から直接触らせない
    public static String getEndpoint() { return endpoint; } // 読み取り専用API
    public static void setEndpoint(String e) { endpoint = e; } // 変更の入口を限定
}
Java
  • public にむき出しだと、どこからでも変更可能になります。
  • 読み取り専用なら public static final にする、または「getterのみ」を提供します。
  • 内部利用なら private または package-private に絞り、APIの表面積を小さく保ちます。

実用例で身につける

例 1: 定数とユーティリティの併用

public final class MathX {
    public static final int DAYS_PER_WEEK = 7;

    private MathX() {}
    public static int clamp(int v, int min, int max) {
        if (min > max) throw new IllegalArgumentException("min<=max");
        return (v < min) ? min : (v > max) ? max : v;
    }
}
Java

例 2: 全体カウンタ(スレッド安全)

public class Counters {
    private static final java.util.concurrent.atomic.AtomicInteger total = new java.util.concurrent.atomic.AtomicInteger();

    public static void onCreated() { total.incrementAndGet(); }
    public static int total() { return total.get(); }
}

class Item {
    Item() { Counters.onCreated(); }
}
Java

例 3: 静的設定の防御的公開

public class Config {
    private static final java.util.Map<String, String> DEFAULTS;
    static {
        var m = new java.util.HashMap<String, String>();
        m.put("locale", "ja_JP");
        m.put("mode", "prod");
        DEFAULTS = java.util.Collections.unmodifiableMap(m);
    }
    public static java.util.Map<String, String> defaults() { return DEFAULTS; } // 不変ビュー
}
Java

設計の指針(重要部分のまとめ)

  • static フィールドは「共有状態」。定数(static final)中心に使い、可変の static は最小限に。
  • 初期化はクラス読み込み時。複雑なら静的初期化ブロックでまとめ、不変ビューを公開して防御する。
  • 公開範囲は最小化し、直接変更させない。必要なら専用メソッドで変更の入口を制御する。
  • 可変の static はテストと並行実行の敵。引数で渡す、インスタンスへ封じる、不変化するなどで設計負担を下げる。
  • 競合が起きる設計では同期やアトミックを使う。できる限り「不変+読み取り専用」に寄せる。

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