10日で覚えるJavaDay 6: クラスとオブジェクト

Day 6: クラスとオブジェクト

今日学ぶこと

  • クラスとオブジェクトの基本
  • フィールドとメソッド
  • コンストラクタ
  • アクセス修飾子
  • static メンバー
  • レコード(Java 16+)

クラスとオブジェクト

クラスは設計図、オブジェクトは設計図から作った実体です。

// クラスの定義(設計図)
public class Dog {
    // フィールド(属性)
    String name;
    int age;

    // メソッド(振る舞い)
    void bark() {
        System.out.println(name + ": ワン!");
    }
}

// オブジェクトの生成(実体化)
Dog dog1 = new Dog();
dog1.name = "ポチ";
dog1.age = 3;
dog1.bark(); // ポチ: ワン!
flowchart LR
    subgraph Class["クラス(設計図)"]
        CD["Dog<br>name, age<br>bark()"]
    end
    subgraph Objects["オブジェクト(実体)"]
        O1["dog1<br>ポチ, 3歳"]
        O2["dog2<br>タロウ, 5歳"]
    end
    Class -->|"new"| O1
    Class -->|"new"| O2
    style CD fill:#3b82f6,color:#fff
    style O1 fill:#22c55e,color:#fff
    style O2 fill:#22c55e,color:#fff

コンストラクタ

オブジェクト生成時に呼ばれる初期化用メソッドです。

public class Dog {
    String name;
    int age;

    // コンストラクタ
    Dog(String name, int age) {
        this.name = name; // this = このオブジェクト
        this.age = age;
    }

    void introduce() {
        System.out.printf("%s(%d歳)%n", name, age);
    }
}

// 使用
Dog dog = new Dog("ポチ", 3);
dog.introduce(); // ポチ(3歳)

コンストラクタのオーバーロード

public class Dog {
    String name;
    int age;
    String breed;

    // 全引数コンストラクタ
    Dog(String name, int age, String breed) {
        this.name = name;
        this.age = age;
        this.breed = breed;
    }

    // 名前と年齢のみ
    Dog(String name, int age) {
        this(name, age, "不明"); // 別のコンストラクタを呼ぶ
    }

    // 名前のみ
    Dog(String name) {
        this(name, 0, "不明");
    }
}

ポイント: this()で同じクラスの別のコンストラクタを呼び出せます。これをコンストラクタチェーンと呼びます。


アクセス修飾子

public class BankAccount {
    private String owner;
    private double balance;

    public BankAccount(String owner, double initialBalance) {
        this.owner = owner;
        this.balance = initialBalance;
    }

    // getter
    public double getBalance() {
        return balance;
    }

    // 入金
    public void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
        }
    }

    // 出金
    public boolean withdraw(double amount) {
        if (amount > 0 && amount <= balance) {
            balance -= amount;
            return true;
        }
        return false;
    }
}
修飾子 クラス内 同パッケージ サブクラス どこからでも
private
(なし)
protected
public
flowchart TB
    subgraph Access["アクセス修飾子の範囲"]
        Private["private<br>クラス内のみ"]
        Default["(なし)<br>同パッケージ"]
        Protected["protected<br>+ サブクラス"]
        Public["public<br>どこからでも"]
    end
    Private --> Default --> Protected --> Public
    style Private fill:#ef4444,color:#fff
    style Default fill:#f59e0b,color:#fff
    style Protected fill:#3b82f6,color:#fff
    style Public fill:#22c55e,color:#fff

原則: フィールドはprivateにし、必要に応じてgetter/setterを提供します(カプセル化)。


getter / setter

public class Student {
    private String name;
    private int score;

    public Student(String name, int score) {
        this.name = name;
        setScore(score);
    }

    // getter
    public String getName() { return name; }
    public int getScore() { return score; }

    // setter(バリデーション付き)
    public void setScore(int score) {
        if (score >= 0 && score <= 100) {
            this.score = score;
        } else {
            throw new IllegalArgumentException("スコアは0〜100の範囲です");
        }
    }
}

static メンバー

staticはクラスに属する(オブジェクトに属さない)メンバーです。

public class Counter {
    private static int count = 0; // 全オブジェクトで共有

    private String name;

    Counter(String name) {
        this.name = name;
        count++;
    }

    // staticメソッド
    static int getCount() {
        return count;
    }

    void show() {
        System.out.printf("%s (total: %d)%n", name, count);
    }
}

// 使用
Counter a = new Counter("A");
Counter b = new Counter("B");
Counter c = new Counter("C");
System.out.println(Counter.getCount()); // 3
種類 所属 アクセス方法
インスタンスメンバー オブジェクト obj.method()
staticメンバー クラス ClassName.method()

注意: staticメソッドからthisや非staticメンバーにはアクセスできません。


toString メソッド

オブジェクトの文字列表現をカスタマイズします。

public class Dog {
    private String name;
    private int age;

    Dog(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return String.format("Dog{name='%s', age=%d}", name, age);
    }
}

Dog dog = new Dog("ポチ", 3);
System.out.println(dog); // Dog{name='ポチ', age=3}

レコード(Java 16+)

データを保持するだけのクラスを簡潔に定義できます。

// 従来のクラス(ボイラープレートが多い)
public class Point {
    private final int x;
    private final int y;
    Point(int x, int y) { this.x = x; this.y = y; }
    int x() { return x; }
    int y() { return y; }
    // + equals, hashCode, toString...
}

// レコード(1行で同等の機能)
record Point(int x, int y) {}

レコードは自動的に以下を生成します:

  • コンストラクタ
  • アクセサメソッド(x(), y()
  • equals(), hashCode(), toString()
record Student(String name, int score) {
    // カスタムメソッドも追加可能
    String grade() {
        if (score >= 90) return "A";
        if (score >= 80) return "B";
        if (score >= 70) return "C";
        return "D";
    }
}

var s = new Student("太郎", 85);
System.out.println(s.name());   // 太郎
System.out.println(s.score());  // 85
System.out.println(s.grade());  // B
System.out.println(s);          // Student[name=太郎, score=85]

推奨: データを保持するだけのクラスにはレコードを使いましょう。


実践: 銀行口座システム

public class BankAccount {
    private static int nextId = 1;

    private final int id;
    private String owner;
    private double balance;

    public BankAccount(String owner, double initialBalance) {
        this.id = nextId++;
        this.owner = owner;
        this.balance = initialBalance;
    }

    public BankAccount(String owner) {
        this(owner, 0);
    }

    public void deposit(double amount) {
        if (amount <= 0) {
            System.out.println("入金額は正の数を指定してください。");
            return;
        }
        balance += amount;
        System.out.printf("[口座%d] %,.0f円 入金 → 残高: %,.0f円%n", id, amount, balance);
    }

    public boolean withdraw(double amount) {
        if (amount <= 0 || amount > balance) {
            System.out.println("出金できません。");
            return false;
        }
        balance -= amount;
        System.out.printf("[口座%d] %,.0f円 出金 → 残高: %,.0f円%n", id, amount, balance);
        return true;
    }

    public void transfer(BankAccount to, double amount) {
        if (this.withdraw(amount)) {
            to.deposit(amount);
            System.out.printf("→ %sから%sへ %,.0f円 送金完了%n", this.owner, to.owner, amount);
        }
    }

    @Override
    public String toString() {
        return String.format("口座%d: %s (残高: %,.0f円)", id, owner, balance);
    }

    public static void main(String[] args) {
        var alice = new BankAccount("Alice", 100000);
        var bob = new BankAccount("Bob", 50000);

        System.out.println(alice);
        System.out.println(bob);
        System.out.println();

        alice.deposit(30000);
        bob.withdraw(10000);
        alice.transfer(bob, 25000);

        System.out.println();
        System.out.println(alice);
        System.out.println(bob);
    }
}

まとめ

概念 説明
クラス オブジェクトの設計図
オブジェクト クラスから生成された実体
コンストラクタ 初期化処理
this 現在のオブジェクトへの参照
アクセス修飾子 private / protected / public
カプセル化 フィールドをprivateにして保護
static クラスに属するメンバー
レコード データ保持用の簡潔なクラス(Java 16+)

重要ポイント

  1. フィールドは**private**にする(カプセル化)
  2. コンストラクタでオブジェクトを適切に初期化
  3. **static**メンバーはクラス全体で共有
  4. データクラスにはレコードを使う

練習問題

問題1: 基本

Bookクラス(タイトル、著者、ページ数)を作成し、複数の本を作って情報を表示してください。

問題2: 応用

ShoppingCartクラスを作成してください。商品の追加・削除・合計金額計算ができるようにしましょう。

チャレンジ問題

recordを使ってMoneyレコード(金額、通貨)を定義し、同じ通貨同士の加算・減算メソッドを実装してください。異なる通貨の場合はエラーにしましょう。


参考リンク


次回予告: Day 7では「継承とポリモーフィズム」について学びます。クラスの拡張と再利用の仕組みをマスターしましょう。