Javaのクラス基礎

2025-08-01

はじめに

Javaプログラミングにおいて、クラスの理解はオブジェクト指向プログラミング(OOP)への重要な第一歩です。この記事では、すでにJavaの基本文法を理解している方を対象に、クラスの定義方法や利用方法について詳しく解説します。

クラスを学ぶことで、プログラムの再利用性や保守性が大きく向上します。コードを整理して構造化できるため、後から修正や拡張を行いやすくなります。また、複雑な問題を小さな単位に分けて設計できるようになることで、効率的に開発を進められます。クラスの理解は、より高度で柔軟なプログラム設計を実現するための基礎となります。

クラスの基礎

Javaのクラスは、ざっくり言うと「関連するデータと処理をひとまとめにするための設計図」です。オブジェクト指向の中心にある考え方で、これを理解しておくと後の継承・ポリモーフィズム・カプセル化が全部つながってきます。順番にいきますね。

1. クラスって何?

クラスは「こういう性質を持って、こういうことができるものですよ」という型(設計図)を定義したものです。たとえば「生徒」「商品」「注文」「車」といった実世界のものをプログラム上で扱いたいときに、それをコードで表現するためにクラスを作ります。クラスをもとにして実際のデータを持つ実体を作ることを「インスタンス化」と言い、その実体を「オブジェクト」と呼びます。

2. なぜ必要なのか?

手続き的に書いていくだけだと、変数や処理がバラバラに散らばって次第に管理が難しくなります。クラスを使うと「このデータはこの種類のものに属している」とまとまりを持たせられるので、コードの見通しが一気によくなります。現実世界に近い形でモデリングできるので、仕様書や設計とも対応づけやすく、「何を作っているのか」が人間にとって理解しやすくなります。

3. クラスを使うメリット

① コードの再利用性が上がる
一度クラスとして「生徒」や「ユーザー」を定義しておけば、同じ型のオブジェクトをいくつでも作れます。毎回同じような変数セットを書かなくていいのでミスも減ります。

② 保守しやすくなる
仕様変更があったときに、関連するコードが一か所にまとまっているので、クラス内だけを直せばよい場面が増えます。散らばった処理を全部探す…という地獄を減らせます。

③ データを安全に扱える(カプセル化)
フィールドをprivateにして、外からはメソッド経由でしか触れないようにすると、意図しない値の変更を防げます。これもクラスがあるからできることです。

④ 大きなプログラムを分割できる
画面、ユーザー、注文、商品…といった単位でクラスを分けておけば、複数人開発でも役割を分けやすくなります。「この機能はこのクラスたちが担当する」と言えるからです。

4. どんなときにクラスを意識するか

プログラムの規模が大きくなり、データや処理が増えて整理が難しくなってきたと感じる段階では、それらを目的ごとに分類してまとめることが重要です。こうした整理を行うことで、コード全体の見通しが良くなり、修正や拡張を行う際の手間を大幅に減らせます。分類を意識して構造化することで、後々の機能追加や保守が格段にしやすくなり、開発効率も向上します。

クラスの定義構文

クラスは、データ(フィールド)と処理(メソッド)を1つにまとめた設計図のようなものです。これにより、オブジェクトの性質や振る舞いを体系的に表現できます。

[アクセス修飾子] class クラス名 {
    // フィールド(メンバ変数)
    // コンストラクタ
    // メソッド
}

アクセス修飾子:クラスやメンバの利用範囲を制御する仕組みです。publicprivateprotectedなどがあり、外部からのアクセスを制限して安全性を高めます。

クラス名:オブジェクトの設計図を表す識別名です。大文字で始めるのが一般的で、クラスを区別しやすくする役割を持ちます。クラスの名前を定義し、波括弧 {} の中にクラスの中身を書きます。

フィールド:クラスの中で定義される変数で、オブジェクトが持つデータを保存します。各インスタンスごとに異なる値を保持できます。

メソッド:特定の処理や動作をまとめた命令ブロックです。引数を受け取り、処理結果を返すこともでき、コードの再利用性を高めます。

コンストラクタ

Javaのコンストラクタは、オブジェクトを生成したときにその初期状態を設定するための特別なメソッドです。クラスと同じ名前を持ち、戻り値の型を指定しない点が特徴です。たとえば、newキーワードを使ってオブジェクトを作成すると、自動的にコンストラクタが呼び出されます。これにより、オブジェクトのフィールドに初期値を代入したり、生成時に必要な処理を行ったりできます。

もしコンストラクタを定義しない場合、Javaは自動的に引数なしの「デフォルトコンストラクタ」を用意しますが、引数付きのコンストラクタを作成するとデフォルトは生成されません。そのため、必要に応じて自分で複数のコンストラクタを定義し、オブジェクトを柔軟に初期化できるようにします。コンストラクタは、オブジェクトを正しい状態で使い始めるための重要な仕組みです。

簡単なクラスの例

BankAccountクラス

クラスは、**データ(フィールド)処理(メソッド)**をひとまとめにした構造で、現実世界の「口座」という概念をプログラムで表現しています。

private String owner;private double balance;は口座の所有者名と残高を保持するフィールドで、外部から直接アクセスできないようにprivateで保護されています。

public BankAccount(String owner, double initialBalance)コンストラクタで、新しい口座を作るときに初期状態を設定します。this.ownerthis.balanceによって、引数で受け取った値をクラスのフィールドに代入します。

depositメソッドは入金処理を、withdrawメソッドは出金処理を担当しており、どちらも金額が正しい場合にのみ残高を更新します。displayBalanceメソッドは現在の残高を表示する機能を持ちます。

このように、BankAccountクラスは「データを安全に管理し、その操作を明確に定義する」というクラス設計の基本を示す代表的な例です。

public class BankAccount {
    // フィールド
    private String owner;
    private double balance;

    // コンストラクタ
    public BankAccount(String owner, double initialBalance) {
        this.owner = owner;
        this.balance = initialBalance;
    }

    // メソッド
    public void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
            System.out.println(amount + "円預けました。残高: " + balance);
        }
    }

    public void withdraw(double amount) {
        if (amount > 0 && amount <= balance) {
            balance -= amount;
            System.out.println(amount + "円引き出しました。残高: " + balance);
        } else {
            System.out.println("引き出しできません。");
        }
    }

    public void displayBalance() {
        System.out.println(owner + "さんの残高: " + balance + "円");
    }
}

クラスの使用例

Mainクラスのmainメソッドは、Javaアプリケーションの実行開始点(エントリーポイント)となります。

まず、new BankAccount("山田太郎", 10000)で口座所有者「山田太郎」、初期残高10,000円のBankAccountオブジェクトを生成しています。ここで呼び出されるのは、BankAccountクラスのコンストラクタです。生成されたオブジェクトはaccountという変数に格納されます。

次に、account.displayBalance()で現在の残高を表示し、account.deposit(5000)で5,000円を入金、account.withdraw(3000)で3,000円を出金します。残高を超える金額であるaccount.withdraw(15000)を実行すると、BankAccountクラス内の条件分岐によって「引き出しできません。」というメッセージが表示されます。

このように、Mainクラスではオブジェクトの生成とメソッド呼び出しを通して、BankAccountクラスに定義された機能を実際に動作させています。クラスの仕組みとメソッド呼び出しの関係を理解する上で、とても基本的かつ重要な例です。

public class Main {
    public static void main(String[] args) {
        // BankAccountオブジェクトの作成
        BankAccount account = new BankAccount("山田太郎", 10000);

        // メソッドの呼び出し
        account.displayBalance();  // 山田太郎さんの残高: 10000.0円
        account.deposit(5000);    // 5000.0円預けました。残高: 15000.0
        account.withdraw(3000);   // 3000.0円引き出しました。残高: 12000.0
        account.withdraw(15000);   // 引き出しできません。
    }
}

クラス内のメソッド詳細

インスタンスメソッド vs スタティックメソッド

インスタンスメソッドは、生成されたオブジェクト(インスタンス)ごとに異なるデータを扱うためのメソッドで、オブジェクトの状態(フィールド)を参照・変更できます。一方、スタティックメソッドはクラス全体で共有される機能を提供し、インスタンスを作らずに呼び出せます。個々のデータを操作する処理はインスタンスメソッドに、共通の計算やユーティリティ的な処理はスタティックメソッドに分けるのが適切です。

特徴インスタンスメソッドスタティックメソッド
呼び出し方法オブジェクトを通して呼び出すクラス名で直接呼び出す
thisの有無あり(現在のオブジェクトを参照)なし
フィールドアクセスインスタンスフィールドにアクセス可能staticフィールドのみアクセス可能
使用例オブジェクトの状態を操作・取得するユーティリティ関数など

スタティックメソッドの例

MathUtilsクラスにはpublic static int square(int num)というメソッドが定義されており、引数として受け取った整数numを自乗して結果を返します。staticが付いているため、MathUtilsクラスのインスタンスを作成せずに、MathUtils.square(5)のようにクラス名から直接呼び出すことができます。このようなメソッドは、インスタンスの状態に依存しない共通的な処理(数値計算や文字列操作など)に適しています。

public class MathUtils {
    public static int square(int num) {
        return num * num;
    }
}

// 呼び出し例
int result = MathUtils.square(5);  // 25

ゲッターとセッター

次のコードは、Personというクラスを使って人の情報を表現する基本的な例です。name(名前)とage(年齢)はフィールドとして定義されており、private修飾子によって外部から直接アクセスできないように保護されています。

外部からこれらの値を安全に取得・変更するために、**ゲッター(getter)セッター(setter)**が用意されています。getName()getAge()で現在の値を取得でき、setName()setAge()では値を設定する際に妥当性チェック(nullやマイナス値の防止)を行っています。

このように、フィールドを直接公開せず、メソッドを介してアクセスを制御することで、データの整合性を保ちつつ安全な設計を実現しています。これが**カプセル化(encapsulation)**と呼ばれるオブジェクト指向の基本的な考え方です。

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

    // コンストラクタ
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // ゲッター
    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    // セッター
    public void setName(String name) {
        if (name != null && !name.isEmpty()) {
            this.name = name;
        }
    }

    public void setAge(int age) {
        if (age >= 0) {
            this.age = age;
        }
    }
}

メソッドチェーン

次のコードは、StringBuilderを活用した文字列連結の例であり、メソッドチェーンの仕組みを示しています。StringBuilderExampleクラスは内部にStringBuilderオブジェクトを持ち、appendメソッドで文字列を追加します。

appendメソッドの戻り値としてthis(自分自身のインスタンス)を返している点がポイントです。これにより、.append("Hello").append(" ")...のように連続してメソッドを呼び出すことができ、可読性の高いコードになります。

最後にtoString()メソッドを呼ぶことで、これまでに追加した文字列を結合した結果を取得します。このような設計は、ビルダー(Builder)パターンや流れるようなAPI設計(Fluent API)によく使われ、コードの簡潔さと直感的な操作性を両立します。

public class StringBuilderExample {
    private StringBuilder builder = new StringBuilder();

    public StringBuilderExample append(String str) {
        builder.append(str);
        return this;
    }

    public String toString() {
        return builder.toString();
    }
}

// 使用例
String result = new StringBuilderExample()
    .append("Hello")
    .append(" ")
    .append("World!")
    .toString();  // "Hello World!"

実践的なクラス設計例

図形クラスの例

次のコードは、長方形を表すRectangleクラスの例です。クラスには、長方形の幅(width)と高さ(height)という2つのフィールドがあり、それぞれprivateで保護されています。

コンストラクタでは、オブジェクト生成時に幅と高さを初期化します。getArea()メソッドは面積(幅×高さ)を計算し、getPerimeter()は周囲の長さ(2×(幅+高さ))を求めます。isSquare()は幅と高さが等しいかを判定し、正方形であればtrueを返します。

scale(double factor)メソッドは拡大・縮小処理を行い、与えられた倍率に応じて幅と高さを変更します。さらに、getWidthsetHeightなどのゲッター・セッターを備えており、外部から安全に値を取得・変更できます。

このクラスは、図形の性質を明確に定義し、データと処理を一体化して管理するオブジェクト指向設計の基本的な実例です。

public class Rectangle {
    private double width;
    private double height;

    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }

    public double getArea() {
        return width * height;
    }

    public double getPerimeter() {
        return 2 * (width + height);
    }

    public boolean isSquare() {
        return width == height;
    }

    public void scale(double factor) {
        width *= factor;
        height *= factor;
    }

    // ゲッターとセッター
    public double getWidth() { return width; }
    public double getHeight() { return height; }
    public void setWidth(double width) { this.width = width; }
    public void setHeight(double height) { this.height = height; }
}

学生成績管理システム

次のコードは、学生の成績情報を管理するStudentクラスの例です。クラスには学生の名前(name)と、テストの点数を格納するリスト(grades)がフィールドとして定義されています。

コンストラクタStudent(String name)で学生の名前を初期化し、addGrade(int grade)メソッドでは0〜100の範囲内の点数だけを追加できるように条件チェックを行っています。

getAverage()メソッドは、登録された点数の平均値を計算して返します。点数がまだ追加されていない場合は0を返すようにしています。getGradeReport()は平均点に応じてA〜Fの成績評価を判定し、文字で返します。

また、getGrades()メソッドでは内部のgradesリストのコピーを返すことで、外部から直接変更されるのを防いでいます。これはカプセル化の考え方を反映した安全な設計です。

このように、Studentクラスは「データ(点数)」と「処理(計算・評価)」を一体化して管理する、オブジェクト指向設計の実践的な例となっています。

import java.util.ArrayList;
import java.util.List;

public class Student {
    private String name;
    private List grades = new ArrayList<>();

    public Student(String name) {
        this.name = name;
    }

    public void addGrade(int grade) {
        if (grade >= 0 && grade <= 100) {
            grades.add(grade);
        }
    }

    public double getAverage() {
        if (grades.isEmpty()) return 0;

        int sum = 0;
        for (int grade : grades) {
            sum += grade;
        }
        return (double)sum / grades.size();
    }

    public String getGradeReport() {
        double avg = getAverage();
        if (avg >= 90) return "A";
        else if (avg >= 80) return "B";
        else if (avg >= 70) return "C";
        else if (avg >= 60) return "D";
        else return "F";
    }

    // ゲッター
    public String getName() { return name; }
    public List getGrades() { return new ArrayList<>(grades); }
}

よくある間違い

  1. メソッドの戻り値の型を忘れる

addメソッドの定義には戻り値の型が記述されていません。Javaでは、すべてのメソッドに対して戻り値の型を明示する必要があります。戻り値がある場合はその型(例:intdoubleなど)を、戻り値がない場合はvoidを指定します。

   public add(int a, int b) {  // 戻り値の型がない
       return a + b;
   }

修正する場合は戻り値にintを付けることで、「このメソッドは整数値を返す」ということをコンパイラに明示できます。戻り値の型を省略するとコンパイルエラーとなるため注意が必要です。

  1. staticとインスタンスの混同

このコードは、スタティックメソッドとインスタンス変数の関係に関する典型的なエラー例です。printValue()メソッドはstaticとして定義されていますが、その中でインスタンス変数valueへ直接アクセスしようとしているため、コンパイルエラーが発生します。

staticメソッドはクラス全体に属し、特定のインスタンス(オブジェクト)に依存しません。一方、valueは各オブジェクトごとに異なる値を持つインスタンス変数であり、クラス全体から直接参照することはできません。

   public class Example {
       private int value;

       public static void printValue() {
           System.out.println(value);  // エラー: staticメソッドからインスタンス変数にアクセスできない
       }
   }
  1. 不適切なスコープのメソッド

このコードは、アクセス修飾子privateの使い方に関するエラー例です。criticalOperation()メソッドがprivateで定義されているため、このメソッドは同じクラスの内部からしか呼び出すことができません。外部のクラスや他のオブジェクトから呼び出そうとすると、コンパイルエラーになります。

privateは、クラス内部でのみ利用できる機能やデータを隠すために使われるアクセス修飾子です。外部から直接操作されると困る処理や、安全性を保ちたい内部ロジックに適しています。

   private void criticalOperation() {  // privateなのに外部から呼び出そうとする
       // 重要な処理
   }

privateを使うことでクラスの内部構造を隠し、外部との不要な依存を防ぐことができます。これを「情報隠蔽(encapsulation)」と呼び、堅牢な設計の基本となります。

まとめ

Javaにおけるクラスは、データと処理をひとまとまりに管理する仕組みであり、オブジェクト指向の基礎を形成する重要な概念です。クラスを使うことで、関連する情報を整理し、再利用性・保守性の高い構造を実現できます。フィールドはオブジェクトの状態を保持し、メソッドはその動作を定義します。さらに、コンストラクタによって生成時の初期化が行われ、ゲッターやセッターを通じて安全なデータアクセスが可能になります。クラスを正しく設計することで、現実の問題を効率的にモデル化でき、拡張性の高いプログラム開発を行う土台が築かれます。

演習問題

初級レベル(3問)

問題1:基本的なクラスの定義

// Studentクラスを作成してください
// フィールド: name(String型), grade(int型)
// メソッド: introduce() - 「私は[name]です。[grade]年生です」と表示
// MainクラスでStudentのインスタンスを作成し、introduce()を呼び出してください

問題2:ゲッターとセッター

// Bookクラスを作成してください
// フィールド: title(String型), price(int型) - どちらもprivate
// メソッド: 各フィールドのゲッターとセッター
// MainクラスでBookのインスタンスを作成し、セッターで値を設定、ゲッターで値を取得して表示

問題3:コンストラクタ

// Carクラスを作成してください
// フィールド: brand(String型), speed(int型)
// コンストラクタ: 引数でbrandとspeedを受け取る
// メソッド: displayInfo() - ブランドと速度を表示
// MainクラスでCarのインスタンスを2つ作成し、情報を表示

中級レベル(6問)

問題4:複数のメソッドを持つクラス

// Calculatorクラスを作成してください
// フィールド: result(double型) - 計算結果を保持
// メソッド:
//   - add(double x): resultにxを加算
//   - subtract(double x): resultからxを減算
//   - multiply(double x): resultにxを乗算
//   - divide(double x): resultをxで除算(0除算チェック)
//   - getResult(): 現在のresultを返す
//   - clear(): resultを0にリセット
// Mainクラスで計算の流れをテスト

問題5:メソッドチェーンの実装

// StringBuilderクラスのように、メソッドチェーンができるPersonクラスを作成
// フィールド: name, age, city(すべてString型)
// メソッド(すべて戻り値でthisを返す):
//   - setName(String name)
//   - setAge(int age)
//   - setCity(String city)
//   - introduce(): すべての情報を表示
// Mainクラスでメソッドチェーンを使用

問題6:配列をフィールドに持つクラス

// ScoreManagerクラスを作成してください
// フィールド: scores(int配列), count(現在の要素数)
// メソッド:
//   - addScore(int score): 配列に点数を追加
//   - getAverage(): 平均点を計算して返す
//   - getMax(): 最高点を返す
//   - getMin(): 最低点を返す
//   - showAllScores(): すべての点数を表示
// Mainクラスで機能をテスト

問題7:バリデーション付きセッター

// BankAccountクラスを作成してください
// フィールド: accountNumber(String), balance(double), ownerName(String)
// メソッド:
//   - コンストラクタ: アカウント番号と所有者名を受け取る、残高は0で初期化
//   - deposit(double amount): 預金(負の数は拒否)
//   - withdraw(double amount): 引き出し(残高不足や負の数は拒否)
//   - getBalance(): 残高を返す
// Mainクラスで入出金操作をテスト

問題8:staticフィールドの使用

// Employeeクラスを作成してください
// フィールド:
//   - instanceフィールド: name, id
//   - staticフィールド: employeeCount(従業員数)
// メソッド:
//   - コンストラクタ: 名前とIDを受け取り、employeeCountを増加
//   - staticメソッド getEmployeeCount(): 従業員数を返す
//   - displayInfo(): 従業員情報を表示
// Mainクラスで複数の従業員を作成し、従業員数を確認

問題9:オブジェクトの配列

// Productクラスを作成してください
// フィールド: name, price, quantity
// メソッド: コンストラクタ、ゲッター、セッター、getTotalValue()(price * quantity)
// MainクラスでProductの配列を作成し、以下の操作を行ってください:
//   1. すべての商品情報を表示
//   2. 在庫総額(すべての商品のtotalValueの合計)を計算
//   3. 最高額の商品を見つける

上級レベル(3問)

問題10:より複雑なクラス設計

// LibraryクラスとBookクラスを作成してください
// Bookクラス:
//   - フィールド: title, author, isbn, isBorrowed
//   - メソッド: コンストラクタ、ゲッター、セッター、borrow(), returnBook()
// Libraryクラス:
//   - フィールド: books(Bookの配列), bookCount
//   - メソッド: 
//       addBook(Book book)
//       findBookByTitle(String title)
//       borrowBook(String title)
//       returnBook(String title)
//       showAvailableBooks()
// Mainクラスで図書館システムをテスト

問題11:メソッドチェーンとバリデーションの組み合わせ

// Orderクラスを作成してください
// フィールド: orderId, customerName, items(String配列), totalAmount, itemCount
// メソッド(メソッドチェーン対応):
//   - setCustomerName(String name): 空文字チェック
//   - addItem(String item, double price): 商品追加、合計金額更新
//   - applyDiscount(double percent): 割引適用(0-100%の範囲チェック)
//   - displayOrder(): 注文詳細を表示
//   - 各セッターメソッドはバリデーション後、thisを返す
// Mainクラスでメソッドチェーンを使用した注文作成をテスト

問題12:状態管理を持つクラス

// TrafficLightクラスを作成してください
// フィールド: currentColor, timer
// 定数: RED, YELLOW, GREEN
// メソッド:
//   - コンストラクタ: REDで初期化
//   - changeColor(): 次の色に変更(RED→GREEN→YELLOW→RED)
//   - getCurrentColor(): 現在の色を返す
//   - setTimer(int seconds): タイマー設定
//   - isSafeToCross(): GREENならtrue
//   - simulate(int steps): 指定回数だけ色変更をシミュレート
// Mainクラスで信号機の動作をテスト

初級レベル(3問)解答例

問題1 解答例

// Studentクラスを作成してください
class Student {
    String name;
    int grade;

    public void introduce() {
        System.out.println("私は" + name + "です。" + grade + "年生です");
    }
}

// Mainクラス
public class Main {
    public static void main(String[] args) {
        Student student1 = new Student();
        student1.name = "山田太郎";
        student1.grade = 2;
        student1.introduce();

        Student student2 = new Student();
        student2.name = "佐藤花子";
        student2.grade = 3;
        student2.introduce();
    }
}

問題2 解答例

// Bookクラスを作成してください
class Book {
    private String title;
    private int price;

    // ゲッターとセッター
    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }
}

// Mainクラス
public class Main {
    public static void main(String[] args) {
        Book book = new Book();
        book.setTitle("Java入門");
        book.setPrice(2500);

        System.out.println("タイトル: " + book.getTitle());
        System.out.println("価格: " + book.getPrice() + "円");
    }
}

問題3 解答例

// Carクラスを作成してください
class Car {
    String brand;
    int speed;

    // コンストラクタ
    public Car(String brand, int speed) {
        this.brand = brand;
        this.speed = speed;
    }

    public void displayInfo() {
        System.out.println("ブランド: " + brand + ", 速度: " + speed + "km/h");
    }
}

// Mainクラス
public class Main {
    public static void main(String[] args) {
        Car car1 = new Car("Toyota", 60);
        Car car2 = new Car("Honda", 80);

        car1.displayInfo();
        car2.displayInfo();
    }
}

中級レベル(6問)解答例

問題4 解答例

// Calculatorクラスを作成してください
class Calculator {
    private double result;

    public Calculator() {
        this.result = 0;
    }

    public void add(double x) {
        result += x;
    }

    public void subtract(double x) {
        result -= x;
    }

    public void multiply(double x) {
        result *= x;
    }

    public void divide(double x) {
        if (x == 0) {
            System.out.println("エラー: 0で除算できません");
            return;
        }
        result /= x;
    }

    public double getResult() {
        return result;
    }

    public void clear() {
        result = 0;
    }
}

// Mainクラス
public class Main {
    public static void main(String[] args) {
        Calculator calc = new Calculator();

        calc.add(10);
        calc.multiply(3);
        calc.subtract(5);
        System.out.println("結果: " + calc.getResult()); // 25.0

        calc.divide(5);
        System.out.println("結果: " + calc.getResult()); // 5.0

        calc.clear();
        System.out.println("クリア後: " + calc.getResult()); // 0.0
    }
}

問題5 解答例

// メソッドチェーンができるPersonクラス
class Person {
    private String name;
    private int age;
    private String city;

    public Person setName(String name) {
        this.name = name;
        return this;
    }

    public Person setAge(int age) {
        this.age = age;
        return this;
    }

    public Person setCity(String city) {
        this.city = city;
        return this;
    }

    public void introduce() {
        System.out.println("名前: " + name + ", 年齢: " + age + ", 都市: " + city);
    }
}

// Mainクラス
public class Main {
    public static void main(String[] args) {
        Person person = new Person();

        // メソッドチェーンを使用
        person.setName("鈴木一郎")
              .setAge(25)
              .setCity("東京")
              .introduce();
    }
}

問題6 解答例

// ScoreManagerクラスを作成してください
class ScoreManager {
    private int[] scores;
    private int count;
    private static final int MAX_SIZE = 100;

    public ScoreManager() {
        scores = new int[MAX_SIZE];
        count = 0;
    }

    public void addScore(int score) {
        if (count < MAX_SIZE) {
            scores[count] = score;
            count++;
        } else {
            System.out.println("エラー: これ以上点数を追加できません");
        }
    }

    public double getAverage() {
        if (count == 0) return 0;

        int sum = 0;
        for (int i = 0; i < count; i++) {
            sum += scores[i];
        }
        return (double) sum / count;
    }

    public int getMax() {
        if (count == 0) return 0;

        int max = scores[0];
        for (int i = 1; i < count; i++) {
            if (scores[i] > max) {
                max = scores[i];
            }
        }
        return max;
    }

    public int getMin() {
        if (count == 0) return 0;

        int min = scores[0];
        for (int i = 1; i < count; i++) {
            if (scores[i] < min) {
                min = scores[i];
            }
        }
        return min;
    }

    public void showAllScores() {
        System.out.print("点数一覧: ");
        for (int i = 0; i < count; i++) {
            System.out.print(scores[i] + " ");
        }
        System.out.println();
    }
}

// Mainクラス
public class Main {
    public static void main(String[] args) {
        ScoreManager manager = new ScoreManager();

        manager.addScore(85);
        manager.addScore(92);
        manager.addScore(78);
        manager.addScore(65);
        manager.addScore(90);

        manager.showAllScores();
        System.out.println("平均点: " + manager.getAverage());
        System.out.println("最高点: " + manager.getMax());
        System.out.println("最低点: " + manager.getMin());
    }
}

問題7 解答例

// BankAccountクラスを作成してください
class BankAccount {
    private String accountNumber;
    private double balance;
    private String ownerName;

    public BankAccount(String accountNumber, String ownerName) {
        this.accountNumber = accountNumber;
        this.ownerName = ownerName;
        this.balance = 0;
    }

    public void deposit(double amount) {
        if (amount <= 0) {
            System.out.println("エラー: 預金額は正の数でなければなりません");
            return;
        }
        balance += amount;
        System.out.println(amount + "円預金しました");
    }

    public boolean withdraw(double amount) {
        if (amount <= 0) {
            System.out.println("エラー: 引き出し額は正の数でなければなりません");
            return false;
        }
        if (amount > balance) {
            System.out.println("エラー: 残高不足です");
            return false;
        }
        balance -= amount;
        System.out.println(amount + "円引き出しました");
        return true;
    }

    public double getBalance() {
        return balance;
    }

    public void displayInfo() {
        System.out.println("口座番号: " + accountNumber);
        System.out.println("名義人: " + ownerName);
        System.out.println("残高: " + balance + "円");
    }
}

// Mainクラス
public class Main {
    public static void main(String[] args) {
        BankAccount account = new BankAccount("123456789", "山田太郎");

        account.deposit(50000);
        account.withdraw(20000);
        account.withdraw(40000); // 残高不足
        account.deposit(15000);

        account.displayInfo();
    }
}

問題8 解答例

// Employeeクラスを作成してください
class Employee {
    private String name;
    private int id;
    private static int employeeCount = 0;

    public Employee(String name, int id) {
        this.name = name;
        this.id = id;
        employeeCount++;
    }

    public static int getEmployeeCount() {
        return employeeCount;
    }

    public void displayInfo() {
        System.out.println("ID: " + id + ", 名前: " + name);
    }
}

// Mainクラス
public class Main {
    public static void main(String[] args) {
        System.out.println("初期従業員数: " + Employee.getEmployeeCount());

        Employee emp1 = new Employee("田中一郎", 1001);
        Employee emp2 = new Employee("佐藤美香", 1002);
        Employee emp3 = new Employee("鈴木健太", 1003);

        emp1.displayInfo();
        emp2.displayInfo();
        emp3.displayInfo();

        System.out.println("現在の従業員数: " + Employee.getEmployeeCount());
    }
}

問題9 解答例

// Productクラスを作成してください
class Product {
    private String name;
    private double price;
    private int quantity;

    public Product(String name, double price, int quantity) {
        this.name = name;
        this.price = price;
        this.quantity = quantity;
    }

    public String getName() { return name; }
    public double getPrice() { return price; }
    public int getQuantity() { return quantity; }

    public double getTotalValue() {
        return price * quantity;
    }

    public void displayInfo() {
        System.out.println("商品名: " + name + ", 単価: " + price + 
                         ", 数量: " + quantity + ", 総額: " + getTotalValue());
    }
}

// Mainクラス
public class Main {
    public static void main(String[] args) {
        Product[] products = {
            new Product("ノート", 120, 50),
            new Product("ペン", 80, 100),
            new Product("消しゴム", 50, 80),
            new Product("定規", 150, 30)
        };

        // 1. すべての商品情報を表示
        System.out.println("=== すべての商品情報 ===");
        for (Product product : products) {
            product.displayInfo();
        }

        // 2. 在庫総額を計算
        double totalInventoryValue = 0;
        for (Product product : products) {
            totalInventoryValue += product.getTotalValue();
        }
        System.out.println("\n在庫総額: " + totalInventoryValue + "円");

        // 3. 最高額の商品を見つける
        Product mostValuable = products[0];
        for (int i = 1; i < products.length; i++) {
            if (products[i].getTotalValue() > mostValuable.getTotalValue()) {
                mostValuable = products[i];
            }
        }
        System.out.println("最高額商品: " + mostValuable.getName() + 
                         " (" + mostValuable.getTotalValue() + "円)");
    }
}

上級レベル(3問)解答例

問題10 解答例

// Bookクラス
class Book {
    private String title;
    private String author;
    private String isbn;
    private boolean isBorrowed;

    public Book(String title, String author, String isbn) {
        this.title = title;
        this.author = author;
        this.isbn = isbn;
        this.isBorrowed = false;
    }

    public String getTitle() { return title; }
    public String getAuthor() { return author; }
    public String getIsbn() { return isbn; }
    public boolean isBorrowed() { return isBorrowed; }

    public void borrow() {
        if (!isBorrowed) {
            isBorrowed = true;
            System.out.println(title + "を貸し出しました");
        } else {
            System.out.println(title + "は既に貸出中です");
        }
    }

    public void returnBook() {
        if (isBorrowed) {
            isBorrowed = false;
            System.out.println(title + "を返却しました");
        } else {
            System.out.println(title + "は貸出中ではありません");
        }
    }

    public void displayInfo() {
        String status = isBorrowed ? "貸出中" : "利用可";
        System.out.println("タイトル: " + title + ", 著者: " + author + 
                         ", ISBN: " + isbn + ", 状態: " + status);
    }
}

// Libraryクラス
class Library {
    private Book[] books;
    private int bookCount;
    private static final int MAX_BOOKS = 100;

    public Library() {
        books = new Book[MAX_BOOKS];
        bookCount = 0;
    }

    public void addBook(Book book) {
        if (bookCount < MAX_BOOKS) {
            books[bookCount] = book;
            bookCount++;
            System.out.println(book.getTitle() + "を追加しました");
        } else {
            System.out.println("エラー: これ以上本を追加できません");
        }
    }

    public Book findBookByTitle(String title) {
        for (int i = 0; i < bookCount; i++) {
            if (books[i].getTitle().equals(title)) {
                return books[i];
            }
        }
        return null;
    }

    public void borrowBook(String title) {
        Book book = findBookByTitle(title);
        if (book != null) {
            book.borrow();
        } else {
            System.out.println("エラー: 「" + title + "」は見つかりませんでした");
        }
    }

    public void returnBook(String title) {
        Book book = findBookByTitle(title);
        if (book != null) {
            book.returnBook();
        } else {
            System.out.println("エラー: 「" + title + "」は見つかりませんでした");
        }
    }

    public void showAvailableBooks() {
        System.out.println("=== 利用可能な書籍 ===");
        boolean found = false;
        for (int i = 0; i < bookCount; i++) {
            if (!books[i].isBorrowed()) {
                books[i].displayInfo();
                found = true;
            }
        }
        if (!found) {
            System.out.println("利用可能な書籍はありません");
        }
    }
}

// Mainクラス
public class Main {
    public static void main(String[] args) {
        Library library = new Library();

        // 本を追加
        library.addBook(new Book("Java入門", "山田太郎", "978-1234567890"));
        library.addBook(new Book("オブジェクト指向設計", "佐藤花子", "978-0987654321"));
        library.addBook(new Book("データベース基礎", "鈴木健太", "978-1122334455"));

        // 操作テスト
        library.borrowBook("Java入門");
        library.borrowBook("Java入門"); // 重複貸出
        library.showAvailableBooks();

        library.returnBook("Java入門");
        library.showAvailableBooks();
    }
}

問題11 解答例

// Orderクラスを作成してください
class Order {
    private String orderId;
    private String customerName;
    private String[] items;
    private double[] prices;
    private double totalAmount;
    private int itemCount;
    private static final int MAX_ITEMS = 50;

    public Order(String orderId) {
        this.orderId = orderId;
        this.items = new String[MAX_ITEMS];
        this.prices = new double[MAX_ITEMS];
        this.itemCount = 0;
        this.totalAmount = 0;
    }

    public Order setCustomerName(String name) {
        if (name == null || name.trim().isEmpty()) {
            System.out.println("エラー: 顧客名を入力してください");
            return this;
        }
        this.customerName = name;
        return this;
    }

    public Order addItem(String item, double price) {
        if (itemCount >= MAX_ITEMS) {
            System.out.println("エラー: これ以上商品を追加できません");
            return this;
        }
        if (price < 0) {
            System.out.println("エラー: 価格は0以上でなければなりません");
            return this;
        }

        items[itemCount] = item;
        prices[itemCount] = price;
        itemCount++;
        totalAmount += price;

        System.out.println(item + "を追加しました (" + price + "円)");
        return this;
    }

    public Order applyDiscount(double percent) {
        if (percent < 0 || percent > 100) {
            System.out.println("エラー: 割引率は0〜100の間で指定してください");
            return this;
        }

        double discount = totalAmount * (percent / 100);
        totalAmount -= discount;
        System.out.println(percent + "%の割引を適用しました (" + discount + "円引き)");
        return this;
    }

    public void displayOrder() {
        System.out.println("\n=== 注文詳細 ===");
        System.out.println("注文ID: " + orderId);
        System.out.println("顧客名: " + customerName);
        System.out.println("商品一覧:");

        for (int i = 0; i < itemCount; i++) {
            System.out.println("  - " + items[i] + ": " + prices[i] + "円");
        }

        System.out.println("合計金額: " + totalAmount + "円");
    }
}

// Mainクラス
public class Main {
    public static void main(String[] args) {
        // メソッドチェーンを使用した注文作成
        Order order = new Order("ORD001")
            .setCustomerName("田中一郎")
            .addItem("ノートPC", 120000)
            .addItem("マウス", 2500)
            .addItem("キーボード", 5000)
            .applyDiscount(10)
            .displayOrder();
    }
}

問題12 解答例

// TrafficLightクラスを作成してください
class TrafficLight {
    public static final String RED = "赤";
    public static final String YELLOW = "黄";
    public static final String GREEN = "青";

    private String currentColor;
    private int timer;

    public TrafficLight() {
        this.currentColor = RED;
        this.timer = 0;
    }

    public void changeColor() {
        switch (currentColor) {
            case RED:
                currentColor = GREEN;
                break;
            case GREEN:
                currentColor = YELLOW;
                break;
            case YELLOW:
                currentColor = RED;
                break;
        }
        System.out.println("信号が" + currentColor + "に変わりました");
    }

    public String getCurrentColor() {
        return currentColor;
    }

    public void setTimer(int seconds) {
        if (seconds < 0) {
            System.out.println("エラー: タイマーは0以上でなければなりません");
            return;
        }
        this.timer = seconds;
        System.out.println("タイマーを" + seconds + "秒に設定しました");
    }

    public boolean isSafeToCross() {
        return currentColor.equals(GREEN);
    }

    public void simulate(int steps) {
        System.out.println("\n=== 信号機シミュレーション (" + steps + "回) ===");
        for (int i = 0; i < steps; i++) {
            System.out.print("ステップ " + (i + 1) + ": ");
            changeColor();
            System.out.println("  横断可能: " + (isSafeToCross() ? "はい" : "いいえ"));
        }
    }

    public void displayStatus() {
        System.out.println("現在の信号: " + currentColor + 
                         ", 横断可能: " + (isSafeToCross() ? "はい" : "いいえ"));
    }
}

// Mainクラス
public class Main {
    public static void main(String[] args) {
        TrafficLight light = new TrafficLight();

        light.displayStatus();

        light.changeColor();
        light.displayStatus();

        light.changeColor();
        light.displayStatus();

        light.changeColor();
        light.displayStatus();

        // シミュレーション実行
        light.simulate(6);
    }
}