クラスとオブジェクト

1. クラスの基本

クラスはオブジェクト指向プログラミングの基本的な構成要素です。クラスは、データ(フィールド)と振る舞い(メソッド)を一つにまとめた設計図のようなものです。

クラスの定義


// 基本的なクラスの定義
public class Person {
    // フィールド(データ)
    String name;
    int age;
    
    // メソッド(振る舞い)
    void introduce() {
        System.out.println("私の名前は" + name + "です。" + age + "歳です。");
    }
}
            

クラスの定義には以下の要素が含まれます:

  • クラス名(上記の例では Person
  • フィールド(クラスが持つデータ)
  • メソッド(クラスが持つ振る舞い)
  • コンストラクタ(オブジェクト生成時に呼び出される特殊なメソッド)

2. オブジェクトの作成と使用

クラスはあくまで設計図であり、実際に使用するためにはオブジェクト(インスタンス)を作成する必要があります。オブジェクトはクラスから生成され、メモリ上に実体を持ちます。

オブジェクトの作成(インスタンス化)


// Personクラスのオブジェクトを作成
Person person1 = new Person();
Person person2 = new Person();

// オブジェクトのフィールドに値を設定
person1.name = "田中太郎";
person1.age = 30;

person2.name = "鈴木花子";
person2.age = 25;

// オブジェクトのメソッドを呼び出す
person1.introduce(); // 出力: 私の名前は田中太郎です。30歳です。
person2.introduce(); // 出力: 私の名前は鈴木花子です。25歳です。
            

同じクラスから作成された複数のオブジェクトは、それぞれ独立したフィールド値を持ちます。各オブジェクトは独自の状態(フィールド値)を持ちながら、同じ振る舞い(メソッド)を共有します。

3. コンストラクタ

コンストラクタはオブジェクトが生成されるときに自動的に呼び出される特殊なメソッドです。コンストラクタはオブジェクトの初期化を行うために使用されます。

コンストラクタの定義


public class Person {
    String name;
    int age;
    
    // デフォルトコンストラクタ
    public Person() {
        name = "名無し";
        age = 0;
    }
    
    // パラメータ付きコンストラクタ
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    void introduce() {
        System.out.println("私の名前は" + name + "です。" + age + "歳です。");
    }
}
            

コンストラクタの使用


// デフォルトコンストラクタを使用
Person person1 = new Person();
person1.introduce(); // 出力: 私の名前は名無しです。0歳です。

// パラメータ付きコンストラクタを使用
Person person2 = new Person("山田次郎", 28);
person2.introduce(); // 出力: 私の名前は山田次郎です。28歳です。
            

コンストラクタの特徴:

  • クラス名と同じ名前を持つ
  • 戻り値の型を指定しない(voidも書かない)
  • オーバーロード可能(引数の数や型が異なる複数のコンストラクタを定義できる)
  • 明示的に定義しない場合、Javaは自動的に引数なしのデフォルトコンストラクタを提供する

4. メソッド

メソッドはクラスの振る舞いを定義する関数です。メソッドはオブジェクトの状態(フィールド)を操作したり、特定の処理を実行したりします。

メソッドの定義と呼び出し


public class Calculator {
    // パラメータを受け取り、結果を返すメソッド
    public int add(int a, int b) {
        return a + b;
    }
    
    // 戻り値のないメソッド
    public void printResult(int result) {
        System.out.println("計算結果: " + result);
    }
    
    // オーバーロードされたメソッド(同じ名前、異なるパラメータ)
    public double add(double a, double b) {
        return a + b;
    }
}

// メソッドの使用例
Calculator calc = new Calculator();
int sum1 = calc.add(5, 3);      // 整数バージョンのaddが呼ばれる
calc.printResult(sum1);         // 出力: 計算結果: 8

double sum2 = calc.add(2.5, 3.7); // 小数バージョンのaddが呼ばれる
calc.printResult((int)sum2);      // 出力: 計算結果: 6
            

メソッドの構成要素:

  • アクセス修飾子(public, private, protected, またはなし)
  • 戻り値の型(値を返さない場合はvoid)
  • メソッド名
  • パラメータリスト(括弧内、なくても可)
  • メソッド本体(中括弧内のコード)

メソッドのオーバーロード

メソッドのオーバーロードとは、同じクラス内で同じ名前のメソッドを複数定義することです。ただし、パラメータの数や型が異なる必要があります。戻り値の型だけが異なる場合はオーバーロードにはなりません。

5. フィールド

フィールドはクラス内で定義された変数で、オブジェクトの状態を表します。フィールドはクラスのすべてのメソッドからアクセス可能です。

フィールドの定義と使用


public class BankAccount {
    // インスタンスフィールド(各オブジェクトに固有)
    private String accountNumber;
    private String ownerName;
    private double balance;
    
    // staticフィールド(クラス全体で共有)
    private static double interestRate = 0.01;
    
    // 定数フィールド
    public static final String BANK_NAME = "Java銀行";
    
    // コンストラクタ
    public BankAccount(String accountNumber, String ownerName) {
        this.accountNumber = accountNumber;
        this.ownerName = ownerName;
        this.balance = 0.0;
    }
    
    // 残高を増やすメソッド
    public void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
            System.out.println(amount + "円を入金しました。現在の残高: " + balance + "円");
        }
    }
    
    // 残高を減らすメソッド
    public void withdraw(double amount) {
        if (amount > 0 && balance >= amount) {
            balance -= amount;
            System.out.println(amount + "円を出金しました。現在の残高: " + balance + "円");
        } else {
            System.out.println("出金できません。残高不足です。");
        }
    }
    
    // 利息を計算して追加するメソッド
    public void addInterest() {
        double interest = balance * interestRate;
        balance += interest;
        System.out.println("利息" + interest + "円を追加しました。現在の残高: " + balance + "円");
    }
    
    // 静的メソッド - 金利の変更
    public static void setInterestRate(double newRate) {
        interestRate = newRate;
        System.out.println("金利を" + (newRate * 100) + "%に変更しました。");
    }
    
    // アカウント情報の表示
    public void printAccountInfo() {
        System.out.println("銀行名: " + BANK_NAME);
        System.out.println("口座番号: " + accountNumber);
        System.out.println("口座名義: " + ownerName);
        System.out.println("残高: " + balance + "円");
        System.out.println("適用金利: " + (interestRate * 100) + "%");
    }
}
            

フィールドの使用例


// 銀行口座の作成と操作
BankAccount account1 = new BankAccount("1234-5678", "田中太郎");
account1.deposit(10000);        // 10000円を入金
account1.withdraw(3000);        // 3000円を出金
account1.addInterest();         // 利息を追加
account1.printAccountInfo();    // 口座情報を表示

// 静的フィールドと静的メソッドの使用
System.out.println("銀行名: " + BankAccount.BANK_NAME);
BankAccount.setInterestRate(0.02);  // 金利を2%に変更

// 新しい金利が適用される
BankAccount account2 = new BankAccount("8765-4321", "鈴木花子");
account2.deposit(20000);
account2.addInterest();         // 新しい金利で計算される
account2.printAccountInfo();
            

6. アクセス修飾子

アクセス修飾子はクラス、フィールド、メソッドの可視性(アクセス範囲)を制御します。適切なアクセス修飾子を使用することで、カプセル化を実現し、クラスの内部実装を隠蔽できます。

主なアクセス修飾子

修飾子 クラス内 同一パッケージ サブクラス すべてのクラス
private
デフォルト
(修飾子なし)
protected
public

カプセル化の実装例


public class Employee {
    // privateフィールド - 直接アクセスできない
    private String name;
    private int employeeId;
    private double salary;
    
    // コンストラクタ
    public Employee(String name, int employeeId, double salary) {
        this.name = name;
        this.employeeId = employeeId;
        this.salary = salary;
    }
    
    // publicゲッターメソッド - フィールドの値を取得
    public String getName() {
        return name;
    }
    
    public int getEmployeeId() {
        return employeeId;
    }
    
    public double getSalary() {
        return salary;
    }
    
    // publicセッターメソッド - フィールドの値を設定(バリデーション付き)
    public void setName(String name) {
        if (name != null && !name.isEmpty()) {
            this.name = name;
        }
    }
    
    // 従業員IDは変更不可(セッターなし)
    
    public void setSalary(double salary) {
        if (salary > 0) {
            this.salary = salary;
        }
    }
    
    // 給与を増額するメソッド
    public void raiseSalary(double percentage) {
        if (percentage > 0) {
            salary += salary * (percentage / 100);
            System.out.println(name + "の給与を" + percentage + "%増額しました。新しい給与: " + salary);
        }
    }
}
            

カプセル化されたクラスの使用例


Employee emp = new Employee("山田太郎", 1001, 300000);

// ゲッターを使用してフィールドの値を取得
System.out.println("名前: " + emp.getName());
System.out.println("従業員ID: " + emp.getEmployeeId());
System.out.println("給与: " + emp.getSalary() + "円");

// セッターを使用してフィールドの値を変更
emp.setName("山田次郎");
emp.setSalary(320000);

// 無効な値は無視される
emp.setSalary(-1000);  // 負の値は無視される

// 給与の増額
emp.raiseSalary(10);  // 10%増額
            

カプセル化の利点:

  • クラスの内部実装を隠蔽し、外部からの不正アクセスを防ぐ
  • フィールドへのアクセスを制御し、データの整合性を保つ
  • 内部実装を変更しても、外部のコードに影響を与えない
  • 使用方法を明確にし、クラスの使いやすさを向上させる

7. staticメンバー

staticキーワードが付いたフィールドやメソッドは、クラスに属し、すべてのインスタンス間で共有されます。staticメンバーはオブジェクトを作成しなくても使用できます。

staticフィールドとメソッド


public class MathUtils {
    // staticフィールド(クラス変数)
    public static final double PI = 3.14159265359;
    private static int operationCount = 0;
    
    // staticメソッド(クラスメソッド)
    public static double square(double num) {
        operationCount++;
        return num * num;
    }
    
    public static double cube(double num) {
        operationCount++;
        return num * num * num;
    }
    
    public static int getOperationCount() {
        return operationCount;
    }
    
    // 非staticメソッド(インスタンスメソッド)
    public void resetCount() {
        operationCount = 0;
        System.out.println("操作カウントをリセットしました。");
    }
}
            

staticメンバーの使用例


// staticメンバーはクラス名から直接アクセス可能
System.out.println("円周率: " + MathUtils.PI);
double area = MathUtils.PI * MathUtils.square(5);
System.out.println("半径5の円の面積: " + area);

double squared = MathUtils.square(4);
System.out.println("4の2乗: " + squared);

double cubed = MathUtils.cube(3);
System.out.println("3の3乗: " + cubed);

// 操作カウントの取得
System.out.println("実行された操作の数: " + MathUtils.getOperationCount());

// 非staticメソッドを使用するにはインスタンスが必要
MathUtils utils = new MathUtils();
utils.resetCount();
System.out.println("リセット後の操作カウント: " + MathUtils.getOperationCount());
            

staticメンバーの特徴:

  • クラスがロードされるときにメモリに割り当てられる
  • すべてのインスタンス間で共有される
  • クラス名を通じて直接アクセスできる
  • staticメソッドからは非staticメンバーに直接アクセスできない
  • ユーティリティクラスや定数の定義に適している

8. thisキーワード

thisキーワードは現在のオブジェクトへの参照を表します。主に、ローカル変数とインスタンス変数の名前が同じ場合に、インスタンス変数を明示的に参照するために使用されます。

thisキーワードの使用例


public class Rectangle {
    private double width;
    private double height;
    
    // コンストラクタでのthisの使用
    public Rectangle(double width, double height) {
        // パラメータ名とフィールド名が同じ場合、thisで区別する
        this.width = width;
        this.height = height;
    }
    
    // 別のコンストラクタを呼び出すためのthisの使用
    public Rectangle() {
        // 引数付きコンストラクタを呼び出す
        this(1.0, 1.0);
    }
    
    // メソッドでのthisの使用
    public void setDimensions(double width, double height) {
        this.width = width;
        this.height = height;
    }
    
    // 現在のオブジェクトを返すメソッド(メソッドチェーンに便利)
    public Rectangle scale(double factor) {
        this.width *= factor;
        this.height *= factor;
        return this;  // 現在のオブジェクトを返す
    }
    
    public double getArea() {
        return width * height;
    }
    
    public void printInfo() {
        System.out.println("幅: " + width + ", 高さ: " + height + ", 面積: " + getArea());
    }
}
            

thisを使ったメソッドチェーンの例


// thisを使ったメソッドチェーンの例
Rectangle rect = new Rectangle(5, 3);
rect.printInfo();  // 出力: 幅: 5.0, 高さ: 3.0, 面積: 15.0

// メソッドチェーン - scaleメソッドがthisを返すため連続呼び出しが可能
rect.scale(2).scale(1.5).printInfo();  // 出力: 幅: 15.0, 高さ: 9.0, 面積: 135.0
            

thisキーワードの主な用途:

  • ローカル変数とインスタンス変数の名前が同じ場合に、インスタンス変数を参照する
  • 現在のクラスの別のコンストラクタを呼び出す
  • 現在のオブジェクトを他のメソッドに渡す
  • 現在のオブジェクトを返す(メソッドチェーンを可能にする)

9. 練習問題

問題1: 銀行口座クラスの作成

以下の要件を満たす銀行口座クラス(BankAccount)を作成してください:

  • 口座番号、口座名義、残高を表すprivateフィールドを持つ
  • コンストラクタで口座番号と口座名義を初期化し、残高は0に設定する
  • 入金メソッド(deposit)と出金メソッド(withdraw)を実装する
  • 残高照会メソッド(getBalance)を実装する
  • 口座情報を表示するメソッド(printAccountInfo)を実装する