フィールドの全体像
フィールド(メンバ変数)は、オブジェクトが「覚えておく状態」です。クラスの中に宣言し、各オブジェクトがそれぞれの値を持ちます。Java では「インスタンスフィールド(個体ごと)」と「static フィールド(クラス全体で共有)」の2種類があり、可視性(private など)や不変性(final)を適切に使うことで、壊れにくく読みやすい設計になります。
インスタンスフィールドと static フィールド
インスタンスフィールド(個体の状態)
各オブジェクトが独立の値を持ちます。ゲッター・変更メソッドで安全に操作します。
public final class User {
private final String id; // 不変(個体ごと)
private String name; // 可変(検証して変更)
public User(String id, String name) {
if (id == null || id.isBlank()) throw new IllegalArgumentException("id");
this.id = id;
this.name = normalize(name);
}
public String id() { return id; }
public String name() { return name; }
public void rename(String newName) {
var x = normalize(newName);
if (x.isEmpty()) return;
this.name = x;
}
private static String normalize(String s) { return s == null ? "" : s.trim(); }
}
Java同じ User クラスから作ったオブジェクトでも、name はそれぞれ別の値を持ちます。
static フィールド(クラス全体の共有)
全オブジェクトで共有される値です。定数や設定、作成数など「共通の事実」に限定して使います。
public final class Counter {
private int value; // 個体の値
private static int created = 0; // 共有の作成数
public Counter() { created++; }
public void inc() { value++; }
public int value() { return value; }
public static int createdCount() { return created; }
}
Javastatic を乱用するとテストが難しくなるため、共有すべきものだけに絞るのが安全です。
可視性とカプセル化(重要ポイントの深掘り)
フィールドは原則 private にします。外から直接いじれると「整合性ルール」を破られます。代わりに、検証付きメソッドで正しい変更だけ許可します。
public final class BankAccount {
private int balance; // 外から直接触らせない
public BankAccount(int initial) {
if (initial < 0) throw new IllegalArgumentException("negative");
this.balance = initial;
}
public int balance() { return balance; }
public void deposit(int amount) {
if (amount <= 0) throw new IllegalArgumentException("amount>0");
balance += amount;
}
public boolean withdraw(int amount) {
if (amount <= 0 || amount > balance) return false;
balance -= amount;
return true;
}
}
Java「直接代入禁止」と「検証付きの窓口」の二本柱で、オブジェクトは常に正しい状態を保てます。
不変性(final)と初期化の順序(重要ポイントの深掘り)
final フィールドは「コンストラクタ完了までに一度だけ代入」され、その後は変更不可になります。不変は並行性・テストで特に強力です。
public final class Token {
private final String value;
public Token(String v) { this.value = v; } // 以後不変
public String value() { return value; }
}
Javanew の内部では「メモリ確保→デフォルト初期化(数値0、参照null)→フィールド初期化式→親→子のコンストラクタ」という順序で進みます。初期化式とコンストラクタの役割を意識すると、未初期化や null の事故を避けられます。
public final class Counter {
private int value = 10; // フィールド初期化式
public Counter() { value++; } // 11 になる
}
Javaデフォルト値と null 安全
参照型フィールドは初期化しないと null です。null を前提にしない設計(早めの初期化、空オブジェクトや Optional を使う)にすると安全性が上がります。
public final class Profile {
private String nickname = ""; // から文字で初期化(nullを避ける)
public void setNickname(String s) { nickname = s == null ? "" : s.trim(); }
}
Java配列やコレクションは、意図がなければ空で初期化しておくと扱いやすくなります。
private final java.util.List<String> tags = new java.util.ArrayList<>(); // 空から
Javathis と名前のシャドーイング
コンストラクタ引数やメソッド引数の名前がフィールド名と同じ場合、this を使って「フィールド側」を指します。これで曖昧さを防げます。
public final class Point {
private final int x, y;
public Point(int x, int y) {
this.x = x; // フィールドへ代入
this.y = y;
}
}
Javaローカル変数でフィールド名を隠す(シャドーイング)と読みづらくなるため、基本は避けます。
派生値(計算プロパティ)と公開の仕方
「計算で得られる値」はフィールドに持たず、メソッドで返すと整合性が保てます。冗長なコピーを減らし、常に最新の値が得られます。
public final class Rectangle {
private final int w, h;
public Rectangle(int w, int h) { this.w = w; this.h = h; }
public int width() { return w; }
public int height() { return h; }
public int area() { return w * h; } // 面積は計算で返す(保持しない)
}
Java特殊な修飾子(static final 定数、transient、volatile)
定数は public static final で定義し、意味のある名前で再利用します。
public final class Consts {
private Consts() {}
public static final int MAX_RETRY = 3;
public static final java.nio.charset.Charset UTF8 = java.nio.charset.StandardCharsets.UTF_8;
}
Javaシリアライズ対象から外すなら transient、複数スレッドで「可視性」を担保するなら volatile を使います(ロックやアトミック型を選ぶ方が安全な場面が多いです)。
public final class Session implements java.io.Serializable {
private transient String secret; // 保存対象から除外
private volatile boolean active; // 変更の可視性を担保
}
Java例題で身につける
例 1: 不変フィールド+検証付き変更
public final class Product {
private final String id;
private String name;
private int price;
public Product(String id, String name, int price) {
if (id == null || id.isBlank()) throw new IllegalArgumentException("id");
if (price < 0) throw new IllegalArgumentException("price>=0");
this.id = id;
this.name = normalize(name);
this.price = price;
}
public String id() { return id; }
public String name() { return name; }
public int price() { return price; }
public void rename(String newName) {
var x = normalize(newName);
if (!x.isEmpty()) this.name = x;
}
public void reprice(int newPrice) {
if (newPrice >= 0) this.price = newPrice;
}
private static String normalize(String s) { return s == null ? "" : s.trim().replaceAll("\\s+", " "); }
}
Java例 2: 共有設定と個体の状態を分離
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; }
}
Java共有値は static、送信先や件名など個体の状態はインスタンスフィールドで表します。
仕上げのアドバイス(重要部分のまとめ)
フィールドは「オブジェクトの状態」。個体ごとはインスタンスフィールド、全体共有は static フィールド。原則 private にして検証付きメソッドで操作し、不変にできるものは final にする。計算で得られる値はフィールドに持たずメソッドで返す。初期化の順序と null を理解して安全に立ち上げる——この型を守るだけで、状態管理が格段に安定します。
