全体像
static は「クラスに属するもの」、インスタンスは「オブジェクト(個体)に属するもの」です。static フィールドやメソッドは全インスタンスで共有され、クラスがロードされた時点で使えます。インスタンスフィールドやメソッドは new で作られた“その個体”の状態と振る舞いに結びつきます。設計の肝は「共有すべき事実は static、個体ごとの違いはインスタンス」にきっちり分けることです。
フィールドの違い(状態の持ち方)
インスタンスフィールド(個体ごとの状態)
各オブジェクトが独立の値を持ちます。同じクラスから作っても、値は個体ごとに別です。
public final class Counter {
private int value; // インスタンスフィールド
public void inc() { value++; }
public int value() { return value; }
}
var c1 = new Counter();
var c2 = new Counter();
c1.inc();
System.out.println(c1.value()); // 1
System.out.println(c2.value()); // 0(別個体)
Javastatic フィールド(クラス全体で共有)
全インスタンスで共通の一つの値を共有します。定数、設定、作成数の記録など「全体の事実」に限定するのが安全です。
public final class Counter {
private int value;
private static int created = 0; // static フィールド(共有)
public Counter() { created++; }
public static int createdCount() { return created; }
}
var a = new Counter();
var b = new Counter();
System.out.println(Counter.createdCount()); // 2(全体共有)
Javaメソッドの違い(振る舞いの持ち方)
インスタンスメソッド(個体の振る舞い)
そのオブジェクトの状態(フィールド)を使う動作はインスタンスメソッドで表します。
public final class Rectangle {
private final int w, h;
public Rectangle(int w, int h) { this.w = w; this.h = h; }
public int area() { return w * h; } // 個体の状態を使う
}
Javastatic メソッド(個体に依存しない計算)
個体の状態を使わない共通処理は static にします。引数だけで結果が決まる“純粋関数”に寄せると再利用性が高まります。
public final class MathUtil {
private MathUtil() {}
public static int clamp(int x, int min, int max) {
return Math.max(min, Math.min(max, x));
}
}
Javastatic メソッドからインスタンスフィールドへは直接触れません。必要ならインスタンスを引数で受け取ります。
public final class User {
private String name;
public String name() { return name; }
public static void print(User u) { // インスタンスを受け取る
System.out.println(u.name());
}
}
Java初期化・ライフサイクル・メモリの違い(重要ポイントの深掘り)
いつ初期化され、いつまで生きるか
- static フィールド・static ブロックは「クラスロード時に一度だけ」初期化されます。アプリのライフタイムに近い長寿命です。
- インスタンスフィールドは「new のたびに」初期化され、そのオブジェクトが到達不能(参照が切れる)になると GC により回収されます。
public final class Config {
public static final java.util.Locale JP = java.util.Locale.JAPAN; // クラスロード時に一度
private final java.time.Instant createdAt = java.time.Instant.now(); // new のたび
}
Java初期化の順序と安全性
new の内部は「デフォルト値 → フィールド初期化式 → 親コンストラクタ → 子コンストラクタ」。軽い既定値はフィールド初期化式に、検証と確定はコンストラクタへ寄せると安全です。static の複雑な初期化は static ブロックへ。ただし乱用すると読みにくく、テストが難しくなります。
設計の指針と使い分け
共有すべきは static、違いはインスタンス
- 共有の事実(定数、アプリ設定、クラス全体の統計)だけを static にする。
- 個体の状態(属性、残高、位置、履歴)はインスタンスに閉じ込める。
- そのオブジェクトに紐づく動作はインスタンスメソッド、共通計算は static メソッド。
public final class EmailValidator {
private EmailValidator() {}
private static final java.util.regex.Pattern P =
java.util.regex.Pattern.compile("^[^\\s@]+@[^\\s@]+$"); // 共有の定数
public static boolean isValid(String s) { // 共通の純粋関数
if (s == null) return false;
return P.matcher(s.trim()).matches();
}
}
Javastatic の乱用を避ける
static を多用して共有状態を増やすと、テストが困難になり、並行性や副作用の追跡が難しくなります。依存はコンストラクタで注入し、差し替え可能にして柔軟性を保ちましょう。
例題で身につける
例 1: 共有の既定値と個体の状態の分離
public final class Mailer {
public static String defaultFrom = "noreply@example.com"; // 共有(必要なら final)
private final String to;
private final String subject;
public Mailer(String to, String subject) {
this.to = to;
this.subject = subject;
}
public String from() { return defaultFrom; } // 共有値を参照
}
JavadefaultFrom を変えると全インスタンスに影響しますが、to/subject は各インスタンス固有です。
例 2: 集計のための static、操作のためのインスタンス
public final class BankAccount {
private static int created = 0; // 作成数の共有集計
private int balance;
public BankAccount(int initial) {
if (initial < 0) throw new IllegalArgumentException();
this.balance = initial;
created++;
}
public void deposit(int amount) { if (amount > 0) balance += amount; }
public int balance() { return balance; }
public static int createdCount() { return created; } // 共有情報を返す
}
Java口座の操作はインスタンスメソッド、全体の統計は static に。
例 3: ユーティリティとしての static 純粋関数
public final class Strings {
private Strings() {}
public static String normalize(String s) {
return s == null ? "" : s.trim().replaceAll("\\s+", " ");
}
}
Javaインスタンスに依存しない共通処理は static でまとめると、依存が減り、テストが容易になります。
よくあるつまずきと回避
- static メソッドからインスタンスフィールドへ直接触れようとして「インスタンスがない」エラーに遭遇します。インスタンスを引数で受けるか、インスタンスメソッドへ移してください。
- なんでも public static にすると、隠蔽が壊れ、変更が難しくなります。原則として状態は private インスタンスフィールドに、操作は検証付きメソッドに。
- 共有 mutable な static フィールドは並行性バグの温床です。可能なら final(不変)にし、必要な共有更新は同期やアトミック型を検討します。
仕上げのアドバイス(重要部分のまとめ)
static は「クラスの共有資産」、インスタンスは「個体の状態と振る舞い」。共有すべき事実だけを static にし、個体の違いはインスタンスに閉じ込める。個体の状態を使う動作はインスタンスメソッド、共通計算は static メソッドへ。初期化の順序とライフサイクルの違いを理解し、static の乱用を避ける——この使い分けが自然にできると、設計は読みやすく、変更に強くなります。
