Java | オブジェクト指向:クラスとオブジェクトの違い

Java Java
スポンサーリンク

全体像

「クラス」は設計図、「オブジェクト」は設計図から作られた実体です。クラスはフィールド(状態)とメソッド(振る舞い)を定義し、new でインスタンス化すると、その時点の具体的な状態を持つオブジェクトがメモリ上に生まれます。同じクラスからは、似た性質を持つ複数のオブジェクトを作れますが、それぞれは独立した存在です。


クラスとは何か(設計図の役割)

クラスは「どんなデータを持ち、何ができるか」を定義します。フィールドの型、メソッドの名前と引数、可視性(public/private)などのルールを決める場所です。クラスそのものは動きません。プログラム中で意味と形を与える枠組みであり、実際に動くのはそのクラスから作られたオブジェクトです。

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 = name == null ? "" : name.trim();
    }
    public String id() { return id; }
    public String name() { return name; }
    public void rename(String newName) {
        if (newName == null || newName.isBlank()) return;
        this.name = newName.trim();
    }
}
Java

この User クラスは「id と name を持ち、名前を変更できる」という設計図です。ここでは「どうやって名前を変更するか(trim など)」のルールが内側に閉じ込められています。これがカプセル化の基本です。


オブジェクトとは何か(実体の役割)

オブジェクトはクラスから new で作られた「具体的なひとかたまり」です。同じ設計図でも、中の状態はオブジェクトごとに異なります。生成のたびにメモリ上へ独立のインスタンスが割り当てられ、メソッド呼び出しでその個体の状態だけが変わります。

User a = new User("U-1", " Taro ");
User b = new User("U-2", " Hanako ");

// それぞれが独立した実体
a.rename("Taro Yamada");
System.out.println(a.name()); // Taro Yamada
System.out.println(b.name()); // Hanako(b は影響されない)
Java

ここで a と b は同じ User クラスから作られましたが、別々の実体です。設計図は共有、状態は独立。この切り分けが「クラスとオブジェクトの違い」の核心です。


重要ポイントの深掘り:静的メンバーとインスタンスの違い

クラスには「インスタンスごとに違うもの」と「クラス全体で共有されるもの」があります。インスタンスフィールド・メソッドは各オブジェクトの状態や振る舞いに紐づきます。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; }
}

Counter c1 = new Counter();
Counter c2 = new Counter();
c1.inc();
System.out.println(c1.value());        // 1(c1 の状態)
System.out.println(c2.value());        // 0(c2 は別個体)
System.out.println(Counter.createdCount()); // 2(クラス全体の数)
Java

static を使う場面は「全体設定」「定数」「作成数の記録」のような共有状態に限るのが安全です。個々のオブジェクトのふるまいは、インスタンスメンバーで表現します。


重要ポイントの深掘り:同一性(identity)と同値性(equality)

オブジェクトには「同一性(同じインスタンスか)」と「同値性(中身が同じか)」の二つの概念があります。== は同一性の比較、equals は同値性の比較です。値オブジェクトでは equals/hashCode を適切に定義すると、コレクションのキーや重複判定に使えます。

User u1 = new User("U-1", "A");
User u2 = new User("U-1", "A");
System.out.println(u1 == u2);       // false(別インスタンス)
System.out.println(u1.equals(u2));  // User で equals を未定義なら false

// 値オブジェクトの例(record は equals/hashCode が自動実装)
public record Money(int amount, String currency) {}
Money m1 = new Money(100, "JPY");
Money m2 = new Money(100, "JPY");
System.out.println(m1 == m2);       // false
System.out.println(m1.equals(m2));  // true(値が同じ)
Java

「同じデータなら同じと見なしたい」型には equals/hashCode を整備し、「唯一の存在を区別したい」型には同一性を意識します。この見極めが設計の質を左右します。


メモリ・ライフサイクルの違い

クラスは「読み込まれると JVM に登録される設計情報」で、アプリ起動中に共有されます。オブジェクトは「必要な時に new で作られ、不要になればガベージコレクションで回収される」一時的な実体です。コンストラクタは「初期状態を確立する場所」、デストラクタはありません(Java は GC が開放を担います)。

public final class Temp {
    private final String name;
    public Temp(String name) { this.name = name; }
    public String name() { return name; }
}

Temp t = new Temp("once"); // この瞬間に実体が生まれる
// t を参照しない状態が続き、GC が不要と判断すればメモリが回収される
Java

「いつ作るか・いつ捨てるか」を意識し、必要な範囲でだけオブジェクトを生かす設計が、メモリ効率と読みやすさにつながります。


例題で実感する違い

設計図(クラス)側でルールと機能を定義し、実体(オブジェクト)がそのルールに沿って動く例を見ます。

public final class Cart {
    private final java.util.List<Item> items = new java.util.ArrayList<>();
    public void add(Item it) {
        if (it == null || it.price() < 0) throw new IllegalArgumentException("invalid item");
        items.add(it);
    }
    public int total() {
        return items.stream().mapToInt(Item::price).sum();
    }
}
public record Item(String name, int price) {}

Cart c1 = new Cart();
Cart c2 = new Cart();                 // 同じ設計図から別オブジェクト
c1.add(new Item("Pen", 120));
c2.add(new Item("Note", 350));
System.out.println(c1.total());       // 120(c1 の状態)
System.out.println(c2.total());       // 350(c2 の状態)
Java

ここで「検証のやり方」「合計の計算」はクラスに定義された共通のルールです。しかし「何を入れたか」「合計はいくつか」はオブジェクトごとに異なる、という違いがはっきり分かります。


よくあるつまずきと回避

クラスとオブジェクトを混同して「クラスを直接使おう」と考えがちですが、動くのはオブジェクトです。インスタンス化(new)してから、インスタンスメソッドを呼ぶのが基本です。また、static を多用して「全部共有」にすると、テスト困難や副作用の連鎖を招きます。共有すべきものだけを static に留め、ふるまいはインスタンスで表現する方が健全です。さらに、equals を定義しないまま「中身が同じなら同じ」と期待すると違和感が生まれます。値オブジェクトには equals/hashCode を整備し、参照の比較(==)と用途を使い分けましょう。


仕上げのアドバイス(重要部分のまとめ)

クラスは「設計とルールの入れ物」、オブジェクトは「その設計から生まれた具体的な実体」。インスタンス化して初めて状態が宿り、メソッドでその個体が振る舞います。static はクラス全体の共有に限定し、ふるまいはインスタンスで表す。== は同一性、equals は同値性——この違いを押さえると、設計とコードが一気にクリアになります。

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