はじめに
Java研修コーディング問題集の第7回は クラスとオブジェクト です。
Javaはオブジェクト指向言語です。クラスの定義、フィールド、コンストラクタ、メソッド、カプセル化といった概念を実際にコードを書いて身につけましょう。
難易度の見方
| マーク | 難易度 | 目安 |
|---|---|---|
| ⭐ | 基本 | 研修1週目レベル |
| ⭐⭐ | 応用 | 少し考える必要あり |
| ⭐⭐⭐ | チャレンジ | 複数の知識を組み合わせる |
問題1:はじめてのクラス ⭐
問題
以下の仕様で Dog クラスを作成してください。
- フィールド:
name(String)、age(int) - メソッド:
bark()→"ワンワン!"と出力 - メソッド:
introduce()→ 名前と年齢を出力
期待する出力
名前: ポチ、年齢: 3歳
ワンワン!
名前: ハチ、年齢: 5歳
ワンワン!
模範解答
class Dog {
String name;
int age;
void bark() {
System.out.println("ワンワン!");
}
void introduce() {
System.out.println("名前: " + name + "、年齢: " + age + "歳");
}
}
public class Exercise01 {
public static void main(String[] args) {
Dog dog1 = new Dog();
dog1.name = "ポチ";
dog1.age = 3;
dog1.introduce();
dog1.bark();
Dog dog2 = new Dog();
dog2.name = "ハチ";
dog2.age = 5;
dog2.introduce();
dog2.bark();
}
}
ポイント: クラスは「設計図」、オブジェクトは「設計図から作った実体(インスタンス)」です。new でインスタンスを生成し、. でフィールドやメソッドにアクセスします。
問題2:コンストラクタ ⭐
問題
問題1の Dog クラスにコンストラクタを追加して、生成時に名前と年齢を設定できるようにしてください。
期待する出力
名前: ポチ、年齢: 3歳
名前: ハチ、年齢: 5歳
模範解答
class Dog {
String name;
int age;
Dog(String name, int age) {
this.name = name;
this.age = age;
}
void introduce() {
System.out.println("名前: " + name + "、年齢: " + age + "歳");
}
}
public class Exercise02 {
public static void main(String[] args) {
Dog dog1 = new Dog("ポチ", 3);
dog1.introduce();
Dog dog2 = new Dog("ハチ", 5);
dog2.introduce();
}
}
ポイント: コンストラクタはクラス名と同じ名前で、戻り値の型を持たない特別な構文です(メソッドとは異なります)。this.name の this は「このインスタンス自身」を指し、引数の name と区別するために使います。
問題3:カプセル化(getter / setter)⭐
問題
BankAccount(銀行口座)クラスを作成してください。
- フィールド(
private):ownerName(口座名義)、balance(残高) - コンストラクタ:口座名義と初期残高を設定
- getter:
getOwnerName(),getBalance() -
deposit(int amount):入金(0以下は無視) -
withdraw(int amount):出金(残高不足・0以下は無視)
期待する出力
口座名義: 田中太郎
残高: 10000円
--- 5000円入金 ---
残高: 15000円
--- 3000円出金 ---
残高: 12000円
--- 20000円出金(残高不足)---
残高不足です
残高: 12000円
模範解答
class BankAccount {
private String ownerName;
private int balance;
BankAccount(String ownerName, int balance) {
this.ownerName = ownerName;
this.balance = balance;
}
String getOwnerName() {
return ownerName;
}
int getBalance() {
return balance;
}
void deposit(int amount) {
if (amount > 0) {
balance += amount;
}
}
boolean withdraw(int amount) {
if (amount <= 0 || amount > balance) {
return false;
}
balance -= amount;
return true;
}
}
public class Exercise03 {
public static void main(String[] args) {
BankAccount account = new BankAccount("田中太郎", 10000);
System.out.println("口座名義: " + account.getOwnerName());
System.out.println("残高: " + account.getBalance() + "円");
System.out.println("--- 5000円入金 ---");
account.deposit(5000);
System.out.println("残高: " + account.getBalance() + "円");
System.out.println("--- 3000円出金 ---");
account.withdraw(3000);
System.out.println("残高: " + account.getBalance() + "円");
System.out.println("--- 20000円出金(残高不足)---");
if (!account.withdraw(20000)) {
System.out.println("残高不足です");
}
System.out.println("残高: " + account.getBalance() + "円");
}
}
ポイント: private でフィールドを隠蔽し、getter/setter(またはメソッド)経由でのみアクセスさせることをカプセル化といいます。残高を直接変更できないようにすることで、不正な値の設定を防げます。
問題4:toString メソッド ⭐⭐
問題
Student(学生)クラスを作成し、toString() メソッドをオーバーライドしてください。
- フィールド:
name(名前)、studentId(学籍番号)、grade(学年) -
toString():"Student{name='田中', studentId='S001', grade=2}"の形式
期待する出力
Student{name='田中', studentId='S001', grade=2}
Student{name='鈴木', studentId='S002', grade=1}
Student{name='佐藤', studentId='S003', grade=3}
模範解答
class Student {
private String name;
private String studentId;
private int grade;
Student(String name, String studentId, int grade) {
this.name = name;
this.studentId = studentId;
this.grade = grade;
}
@Override
public String toString() {
return "Student{name='" + name + "', studentId='" + studentId + "', grade=" + grade + "}";
}
}
public class Exercise04 {
public static void main(String[] args) {
Student s1 = new Student("田中", "S001", 2);
Student s2 = new Student("鈴木", "S002", 1);
Student s3 = new Student("佐藤", "S003", 3);
System.out.println(s1);
System.out.println(s2);
System.out.println(s3);
}
}
ポイント: toString() をオーバーライドすると、System.out.println() にオブジェクトを渡したとき自動的に呼ばれます。@Override アノテーションはオーバーライドであることを明示し、タイポなどのミスをコンパイル時に検出できます。
問題5:static メンバー ⭐⭐
問題
Counter クラスを作成してください。インスタンスが生成されるたびにカウントが1増え、合計で何個のインスタンスが作られたかを追跡できるようにしてください。
-
staticフィールド:totalCount(全インスタンス数) - インスタンスフィールド:
id(自分が何番目か) -
staticメソッド:getTotalCount()→ 合計数を返す
期待する出力
Counter#1 が作成されました
Counter#2 が作成されました
Counter#3 が作成されました
合計インスタンス数: 3
模範解答
class Counter {
private static int totalCount = 0;
private int id;
Counter() {
totalCount++;
this.id = totalCount;
System.out.println("Counter#" + id + " が作成されました");
}
int getId() {
return id;
}
static int getTotalCount() {
return totalCount;
}
}
public class Exercise05 {
public static void main(String[] args) {
Counter c1 = new Counter();
Counter c2 = new Counter();
Counter c3 = new Counter();
System.out.println("合計インスタンス数: " + Counter.getTotalCount());
}
}
ポイント: static フィールドはクラスに1つだけ存在し、すべてのインスタンスで共有されます。static メソッドはインスタンスなしで クラス名.メソッド名() で呼び出せます。
問題6:クラスの配列 ⭐⭐
問題
Product(商品)クラスを作成し、複数の商品を配列で管理して在庫一覧を表示してください。
- フィールド:
name(商品名)、price(価格)、stock(在庫数) - メソッド:
isAvailable()→ 在庫があればtrue
期待する出力
=== 在庫一覧 ===
商品名 価格 在庫 状態
りんご 150円 30個 ○在庫あり
バナナ 100円 0個 ×在庫なし
牛乳 200円 15個 ○在庫あり
パン 280円 0個 ×在庫なし
卵 250円 50個 ○在庫あり
---
在庫あり: 3商品 / 在庫なし: 2商品
模範解答
class Product {
private String name;
private int price;
private int stock;
Product(String name, int price, int stock) {
this.name = name;
this.price = price;
this.stock = stock;
}
String getName() { return name; }
int getPrice() { return price; }
int getStock() { return stock; }
boolean isAvailable() {
return stock > 0;
}
}
public class Exercise06 {
public static void main(String[] args) {
Product[] products = {
new Product("りんご", 150, 30),
new Product("バナナ", 100, 0),
new Product("牛乳", 200, 15),
new Product("パン", 280, 0),
new Product("卵", 250, 50)
};
System.out.println("=== 在庫一覧 ===");
System.out.printf("%-12s %6s %5s %s%n", "商品名", "価格", "在庫", "状態");
int available = 0;
int unavailable = 0;
for (Product p : products) {
String status = p.isAvailable() ? "○在庫あり" : "×在庫なし";
System.out.printf("%-12s %4d円 %3d個 %s%n",
p.getName(), p.getPrice(), p.getStock(), status);
if (p.isAvailable()) {
available++;
} else {
unavailable++;
}
}
System.out.println("---");
System.out.println("在庫あり: " + available + "商品 / 在庫なし: " + unavailable + "商品");
}
}
ポイント: オブジェクトも配列に格納できます。Product[] products は「Productオブジェクトの参照の配列」です。オブジェクトの配列とループを組み合わせることで、データの一覧表示や集計が簡潔に書けます。
問題7:クラス間の関連(has-a関係)⭐⭐
問題
Address(住所)クラスと Employee(社員)クラスを作成してください。社員は住所を持つ(has-a関係)ようにしてください。
期待する出力
=== 社員情報 ===
社員番号: E001
氏名: 田中太郎
住所: 〒100-0001 東京都千代田区1-1-1
---
社員番号: E002
氏名: 鈴木花子
住所: 〒530-0001 大阪府大阪市北区2-2-2
模範解答
class Address {
private String postalCode;
private String prefecture;
private String city;
private String street;
Address(String postalCode, String prefecture, String city, String street) {
this.postalCode = postalCode;
this.prefecture = prefecture;
this.city = city;
this.street = street;
}
@Override
public String toString() {
return "〒" + postalCode + " " + prefecture + city + street;
}
}
class Employee {
private String employeeId;
private String name;
private Address address;
Employee(String employeeId, String name, Address address) {
this.employeeId = employeeId;
this.name = name;
this.address = address;
}
void printInfo() {
System.out.println("社員番号: " + employeeId);
System.out.println("氏名: " + name);
System.out.println("住所: " + address);
}
}
public class Exercise07 {
public static void main(String[] args) {
Address addr1 = new Address("100-0001", "東京都", "千代田区", "1-1-1");
Address addr2 = new Address("530-0001", "大阪府", "大阪市北区", "2-2-2");
Employee emp1 = new Employee("E001", "田中太郎", addr1);
Employee emp2 = new Employee("E002", "鈴木花子", addr2);
System.out.println("=== 社員情報 ===");
emp1.printInfo();
System.out.println("---");
emp2.printInfo();
}
}
ポイント: クラスのフィールドに別のクラスのオブジェクトを持つことを has-a関係(コンポジション) といいます。Employee は Address を「持っている」関係です。複雑なデータを関連するクラスに分割することで、コードの整理と再利用性が向上します。
問題8:equals メソッドのオーバーライド ⭐⭐
問題
Point(座標)クラスを作成し、equals() メソッドをオーバーライドして、座標の値が同じなら等しいと判定されるようにしてください。
期待する出力
p1 = Point(3, 5)
p2 = Point(3, 5)
p3 = Point(1, 2)
p1 == p2 : false
p1.equals(p2) : true
p1.equals(p3) : false
模範解答
class Point {
private int x;
private int y;
Point(int x, int y) {
this.x = x;
this.y = y;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Point other = (Point) obj;
return this.x == other.x && this.y == other.y;
}
@Override
public int hashCode() {
return 31 * x + y;
}
@Override
public String toString() {
return "Point(" + x + ", " + y + ")";
}
}
public class Exercise08 {
public static void main(String[] args) {
Point p1 = new Point(3, 5);
Point p2 = new Point(3, 5);
Point p3 = new Point(1, 2);
System.out.println("p1 = " + p1);
System.out.println("p2 = " + p2);
System.out.println("p3 = " + p3);
System.out.println("p1 == p2 : " + (p1 == p2));
System.out.println("p1.equals(p2) : " + p1.equals(p2));
System.out.println("p1.equals(p3) : " + p1.equals(p3));
}
}
ポイント: デフォルトの equals() は == と同じく参照の比較です。値の等価性を判定するには equals() をオーバーライドする必要があります。equals() をオーバーライドする際は、null チェックと型チェックを忘れずに行いましょう。また、equals() をオーバーライドしたら hashCode() も必ずオーバーライドしてください。これは Java の契約で、HashMap や HashSet で正しく動作するために必要です。
問題9:自動販売機シミュレーション ⭐⭐⭐
問題
簡易自動販売機を複数のクラスで設計してください。
クラス構成:
-
Drink:飲み物(名前、価格、在庫数) -
VendingMachine:自動販売機(飲み物の配列、投入金額)-
insertMoney(int amount):お金を投入 -
showMenu():メニュー表示 -
buy(int index):購入 -
getChange():おつりを返却
-
期待する出力
=== 自動販売機 ===
500円投入しました(残高: 500円)
--- メニュー ---
1. コーラ 120円 [在庫:3]
2. お茶 100円 [在庫:5]
3. コーヒー 150円 [在庫:0] ※売切れ
4. 水 80円 [在庫:10]
コーラを購入しました(残高: 380円)
お茶を購入しました(残高: 280円)
コーヒーは売り切れです
水を購入しました(残高: 200円)
おつり: 200円
模範解答
class Drink {
private String name;
private int price;
private int stock;
Drink(String name, int price, int stock) {
this.name = name;
this.price = price;
this.stock = stock;
}
String getName() { return name; }
int getPrice() { return price; }
int getStock() { return stock; }
boolean isAvailable() {
return stock > 0;
}
void reduceStock() {
if (stock > 0) stock--;
}
}
class VendingMachine {
private Drink[] drinks;
private int balance;
VendingMachine(Drink[] drinks) {
this.drinks = drinks;
this.balance = 0;
}
void insertMoney(int amount) {
balance += amount;
System.out.println(amount + "円投入しました(残高: " + balance + "円)");
}
void showMenu() {
System.out.println("\n--- メニュー ---");
for (int i = 0; i < drinks.length; i++) {
Drink d = drinks[i];
String status = d.isAvailable()
? "[在庫:" + d.getStock() + "]"
: "[在庫:0] ※売切れ";
System.out.printf("%d. %-8s %3d円 %s%n", i + 1, d.getName(), d.getPrice(), status);
}
}
void buy(int index) {
if (index < 0 || index >= drinks.length) {
System.out.println("無効な番号です");
return;
}
Drink d = drinks[index];
if (!d.isAvailable()) {
System.out.println(d.getName() + "は売り切れです");
return;
}
if (balance < d.getPrice()) {
System.out.println("残高不足です(残高: " + balance + "円、価格: " + d.getPrice() + "円)");
return;
}
balance -= d.getPrice();
d.reduceStock();
System.out.println(d.getName() + "を購入しました(残高: " + balance + "円)");
}
void getChange() {
System.out.println("\nおつり: " + balance + "円");
balance = 0;
}
}
public class Exercise09 {
public static void main(String[] args) {
Drink[] drinks = {
new Drink("コーラ", 120, 3),
new Drink("お茶", 100, 5),
new Drink("コーヒー", 150, 0),
new Drink("水", 80, 10)
};
VendingMachine vm = new VendingMachine(drinks);
System.out.println("=== 自動販売機 ===");
vm.insertMoney(500);
vm.showMenu();
System.out.println();
vm.buy(0); // コーラ
vm.buy(1); // お茶
vm.buy(2); // コーヒー(売切れ)
vm.buy(3); // 水
vm.getChange();
}
}
ポイント: 複数のクラス(Drink と VendingMachine)が協調して動くプログラムです。各クラスが自分の責務だけを持ち、他のクラスの内部状態を直接操作しないようにするのがオブジェクト指向の基本です。
問題10:図書管理システム ⭐⭐⭐
問題
簡易的な図書管理システムを設計してください。
クラス構成:
-
Book:本(タイトル、著者、貸出状態) -
Library:図書館(本の配列)-
addBook(Book book):本を追加 -
searchByTitle(String keyword):タイトルで検索 -
borrowBook(String title):貸出 -
returnBook(String title):返却 -
showAllBooks():全書籍一覧表示
-
期待する出力
=== 図書管理システム ===
--- 全書籍一覧 ---
1. 「Java入門」 - 田中太郎 [貸出可]
2. 「Python入門」 - 鈴木花子 [貸出可]
3. 「データベース入門」 - 佐藤次郎 [貸出可]
4. 「Java応用」 - 田中太郎 [貸出可]
--- 「Java」で検索 ---
「Java入門」 - 田中太郎 [貸出可]
「Java応用」 - 田中太郎 [貸出可]
--- 貸出処理 ---
「Java入門」を貸出しました
「Java入門」は貸出中です
--- 返却処理 ---
「Java入門」が返却されました
--- 最終状態 ---
1. 「Java入門」 - 田中太郎 [貸出可]
2. 「Python入門」 - 鈴木花子 [貸出可]
3. 「データベース入門」 - 佐藤次郎 [貸出可]
4. 「Java応用」 - 田中太郎 [貸出可]
模範解答
class Book {
private String title;
private String author;
private boolean borrowed;
Book(String title, String author) {
this.title = title;
this.author = author;
this.borrowed = false;
}
String getTitle() { return title; }
String getAuthor() { return author; }
boolean isBorrowed() { return borrowed; }
boolean borrow() {
if (borrowed) return false;
borrowed = true;
return true;
}
boolean returnBook() {
if (!borrowed) return false;
borrowed = false;
return true;
}
@Override
public String toString() {
String status = borrowed ? "貸出中" : "貸出可";
return "「" + title + "」 - " + author + " [" + status + "]";
}
}
class Library {
private Book[] books;
private int count;
Library(int capacity) {
books = new Book[capacity];
count = 0;
}
void addBook(Book book) {
if (count < books.length) {
books[count] = book;
count++;
}
}
void showAllBooks() {
System.out.println("--- 全書籍一覧 ---");
for (int i = 0; i < count; i++) {
System.out.println((i + 1) + ". " + books[i]);
}
}
void searchByTitle(String keyword) {
System.out.println("--- 「" + keyword + "」で検索 ---");
for (int i = 0; i < count; i++) {
if (books[i].getTitle().contains(keyword)) {
System.out.println(books[i]);
}
}
}
void borrowBook(String title) {
for (int i = 0; i < count; i++) {
if (books[i].getTitle().equals(title)) {
if (books[i].borrow()) {
System.out.println("「" + title + "」を貸出しました");
} else {
System.out.println("「" + title + "」は貸出中です");
}
return;
}
}
System.out.println("「" + title + "」は見つかりませんでした");
}
void returnBook(String title) {
for (int i = 0; i < count; i++) {
if (books[i].getTitle().equals(title)) {
if (books[i].returnBook()) {
System.out.println("「" + title + "」が返却されました");
} else {
System.out.println("「" + title + "」は貸出されていません");
}
return;
}
}
System.out.println("「" + title + "」は見つかりませんでした");
}
}
public class Exercise10 {
public static void main(String[] args) {
Library library = new Library(10);
library.addBook(new Book("Java入門", "田中太郎"));
library.addBook(new Book("Python入門", "鈴木花子"));
library.addBook(new Book("データベース入門", "佐藤次郎"));
library.addBook(new Book("Java応用", "田中太郎"));
System.out.println("=== 図書管理システム ===\n");
library.showAllBooks();
System.out.println();
library.searchByTitle("Java");
System.out.println("\n--- 貸出処理 ---");
library.borrowBook("Java入門");
library.borrowBook("Java入門"); // 2回目は失敗
System.out.println("\n--- 返却処理 ---");
library.returnBook("Java入門");
System.out.println("\n--- 最終状態 ---");
library.showAllBooks();
}
}
ポイント: このプログラムはCRUD操作(Create=追加、Read=検索/一覧、Update=貸出/返却、Delete=未実装)の基本形です。実務のWebアプリケーションでも同じ構造が使われます。各クラスが自分の状態を管理し、メソッドを通じて操作する設計がオブジェクト指向の基本です。
まとめ
| 問題 | テーマ | 難易度 |
|---|---|---|
| 問題1 | クラスの基本 | ⭐ |
| 問題2 | コンストラクタ | ⭐ |
| 問題3 | カプセル化(getter/setter) | ⭐ |
| 問題4 | toString オーバーライド | ⭐⭐ |
| 問題5 | static メンバー | ⭐⭐ |
| 問題6 | クラスの配列 | ⭐⭐ |
| 問題7 | has-a関係(コンポジション) | ⭐⭐ |
| 問題8 | equals オーバーライド | ⭐⭐ |
| 問題9 | 自動販売機シミュレーション | ⭐⭐⭐ |
| 問題10 | 図書管理システム | ⭐⭐⭐ |
次回は 継承とインターフェース のコーディング問題です!
シリーズ一覧:Java研修コーディング問題集
- 変数・データ型・演算子 編
- 条件分岐(if / switch)編
- 繰り返し処理(for / while)編
- 配列 編
- メソッド 編
- 文字列操作(String)編
- 👉 クラスとオブジェクト 編(本記事)
- 継承とインターフェース 編
- 例外処理 編
- コレクションと Stream API 編
著者: @kotaro_ai_lab
AI駆動開発やテック情報を毎日発信しています。フォローお気軽にどうぞ!