全体像
private は「同じクラスの中からだけ見える」というアクセス修飾子です。外部のクラスはもちろん、継承したサブクラスからも直接は触れません。目的はカプセル化——内部の状態や実装詳細を隠し、外部には安全な操作だけを“窓口”として見せること。これにより、整合性が守られ、変更に強く、誤用されない設計になります。
使える対象と基本ルール
private はフィールド(インスタンス変数/static 変数)、メソッド、コンストラクタ、内部クラス(ネストクラス)に付けられます。トップレベルのクラスには付けられません。private メンバーは「そのクラスの本文内」からしか参照できず、他クラスはゲッターや適切な操作メソッド経由でしか関与できません。
public final class Account {
private int balance; // 外部から直接触れない
private static final int FEE = 100; // 手数料の実装詳細(外へ隠す)
public Account(int initial) {
if (initial < 0) throw new IllegalArgumentException("initial>=0");
this.balance = initial;
}
public int balance() { // 読み取りの窓口(契約)
return balance;
}
public boolean withdraw(int amount) { // 変更の窓口(検証付き)
if (amount <= 0 || amount + FEE > balance) return false;
balance -= (amount + FEE);
return true;
}
private void audit(String msg) { // 内部だけの補助
System.out.println("[AUDIT] " + msg);
}
}
Javaカプセル化の目的(重要ポイントの深掘り)
private の最大の価値は「整合性ルールを破られない」ことです。フィールドを public にすると、どこからでも値を自由に書き換えられ、バリデーションをすり抜けます。private にして検証付きメソッドを窓口化すれば、常に正しい状態だけが許されます。さらに、内部実装を外から隠すことで、後から中身を差し替えても外部のコードは壊れません。これは変更耐性(後方互換性)を高める鍵です。
public final class Product {
private int price; // 直接代入を禁止
public Product(int price) {
if (price < 0) throw new IllegalArgumentException("price>=0");
this.price = price;
}
public int price() { return price; }
public void reprice(int newPrice) { // 必ず検証を通る
if (newPrice < 0) throw new IllegalArgumentException("price>=0");
this.price = newPrice;
}
}
Javaコンストラクタやヘルパーを private にする設計
コンストラクタを private にすると、外部からの new を禁止できます。代わりに static ファクトリ(of や create)を用意すると、生成ルールを一箇所へ集約でき、キャッシュやバリデーションも管理しやすくなります。複雑な前処理・共通化したいロジックは private ヘルパーメソッドに閉じ込めると、公開APIを“薄く・安定”に保てます。
public final class Email {
private final String value;
private Email(String value) { this.value = value; } // 生成の入口を閉じる
public static Email of(String raw) { // 公開ファクトリ
var v = normalize(raw);
if (!isValid(v)) throw new IllegalArgumentException("invalid email");
return new Email(v);
}
public String value() { return value; }
private static String normalize(String s) { // 内部ヘルパー
return s == null ? "" : s.trim().toLowerCase(java.util.Locale.ROOT);
}
private static boolean isValid(String s) {
int at = s.indexOf('@');
return at > 0 && at == s.lastIndexOf('@') && at < s.length() - 1;
}
}
Java内部クラスと private の使い道
内部の一時的な構造やパーサ用のトークンなど、外へ見せる必要がない型は private の内部クラスに閉じると安全です。外側クラスだけが利用でき、APIの表面積を増やさずに済みます。テストで内部を直接触りたくなるときは、公開メソッド経由で振る舞いを検証するか、パッケージ構成を見直して内部を package-private にする方が健全です。
public final class Parser {
public Result parse(String s) {
var tks = tokenize(s);
return buildResult(tks);
}
private static class Token { // 外へは見せない内部型
final String text;
Token(String text) { this.text = text; }
}
private java.util.List<Token> tokenize(String s) {
var list = new java.util.ArrayList<Token>();
for (var part : (s == null ? "" : s.trim()).split("\\s+")) {
if (!part.isEmpty()) list.add(new Token(part));
}
return list;
}
private Result buildResult(java.util.List<Token> tokens) {
return new Result(tokens.size());
}
}
public record Result(int count) {}
Javaよくある誤解と回避策(重要ポイントの深掘り)
「テストで触りたいから public にする」は誤りです。可視性を緩めると本番コードも内部詳細に依存し、変更が難しくなります。テストは公開APIを通じて振る舞いを検証し、必要ならパッケージ境界を工夫して package-private を活用しましょう。また、getter/setter を機械的に public で生成するより、「意味のある操作」を公開する方が整合性と可読性が高まります。さらに、private フィールド+final による不変設計は並行性とテストの強力な味方です。
public final class Token {
private final String value; // 不変+private
public Token(String v) {
if (v == null || v.isBlank()) throw new IllegalArgumentException("token");
this.value = v;
}
public String value() { return value; }
}
Java例題で身につける
// 1) 不正な初期状態を拒否し、内部詳細は隠す
public final class Config {
private String region = "ap-northeast-1"; // 既定値は内部管理
private int timeoutMs = 3000;
public Config(String region, int timeoutMs) {
if (timeoutMs <= 0) throw new IllegalArgumentException("timeout>0");
if (region != null && !region.isBlank()) this.region = region.trim();
this.timeoutMs = timeoutMs;
}
public String region() { return region; } // 読み取りのみ公開
public int timeoutMs() { return timeoutMs; }
}
// 2) 生成と検証の集中(private コンストラクタ+ファクトリ)
public final class Url {
private final java.net.URI uri;
private Url(java.net.URI uri) { this.uri = uri; } // 外から new させない
public static Url parse(String s) {
try { return new Url(new java.net.URI(s)); }
catch (java.net.URISyntaxException e) { throw new IllegalArgumentException("bad url: " + s, e); }
}
public java.net.URI toUri() { return uri; }
}
Java仕上げのアドバイス(重要部分のまとめ)
private は「クラスの外から触らせない」ための鍵で、カプセル化の中心です。フィールドや内部ヘルパーは private に閉じ、外へ見せるのは検証付きの“意味のある操作”だけに絞る。必要ならコンストラクタを private にしてファクトリへ集約し、内部型は private の内部クラスに閉じる。テストを理由に可視性を緩めず、公開APIの契約で振る舞いを検証する——この基本を徹底すれば、整合性が守られ、変更に強く、読みやすいクラス設計が手に入ります。
。
