☕
JAVA言語のサンプルコード集
文法解説で学んだ内容を組み合わせた、難易度が段階的に上がる3本の実践的なサンプルプログラムです。①クラス設計とポリモーフィズムを使った蔵書管理、②整列・探索アルゴリズムの実装と計算量の比較、③例外処理とコレクションを使った銀行口座管理、の順に紹介します。
サンプル1:クラス設計を使う蔵書管理システム(カプセル化・継承・ポリモーフィズム)
抽象クラスItemを継承したBook・Magazineクラスを用意し、共通のリストList<Item>で管理します。
フィールドはすべてprivateにしてカプセル化し、それぞれのクラスでdescribe()を独自に実装することで、ポリモーフィズムにより1つのループから種類ごとに異なる出力を得られます。
SAMPLE PROGRAM
import java.util.ArrayList;
import java.util.List;
// 抽象クラス:蔵書共通の状態(カプセル化されたフィールド)と振る舞いの雛形を定義
abstract class Item {
private final String title;
private boolean borrowed = false;
Item(String title) {
this.title = title;
}
public String getTitle() {
return title;
}
public boolean isBorrowed() {
return borrowed;
}
public void borrow() {
if (borrowed) {
throw new IllegalStateException(title + " は既に貸出中です");
}
borrowed = true;
}
public void returnItem() {
borrowed = false;
}
// サブクラスごとに異なる説明文を返す(ポリモーフィズムの要)
public abstract String describe();
}
class Book extends Item {
private final String author;
Book(String title, String author) {
super(title);
this.author = author;
}
@Override
public String describe() {
return "[書籍] " + getTitle() + "(著: " + author + ")"
+ (isBorrowed() ? " ※貸出中" : "");
}
}
class Magazine extends Item {
private final int issueNumber;
Magazine(String title, int issueNumber) {
super(title);
this.issueNumber = issueNumber;
}
@Override
public String describe() {
return "[雑誌] " + getTitle() + " 第" + issueNumber + "号"
+ (isBorrowed() ? " ※貸出中" : "");
}
}
class Library {
private final List- items = new ArrayList<>();
public void add(Item item) {
items.add(item);
}
public void printCatalog() {
for (Item item : items) {
System.out.println(item.describe()); // 実際の型のdescribe()が呼ばれる
}
}
public long countBorrowed() {
return items.stream().filter(Item::isBorrowed).count();
}
}
public class Main {
public static void main(String[] args) {
Library library = new Library();
Book book = new Book("Javaの基礎", "山田太郎");
Magazine magazine = new Magazine("技術評論月刊誌", 42);
library.add(book);
library.add(magazine);
book.borrow();
library.printCatalog();
System.out.println("貸出中の点数: " + library.countBorrowed());
}
}
サンプル2:整列・探索アルゴリズムの実装と計算量の比較
バブルソート(計算量O(n²))とクイックソート(平均O(n log n))で同じ配列を整列し、比較回数を数えることで計算量の違いを体感します。整列後は二分探索(O(log n))で値を探索します。 データ件数を増やすほどO(n²)とO(n log n)の差が大きくなる点が、基本情報技術者試験でも問われる重要な考え方です。
SAMPLE PROGRAM
import java.util.Arrays;
public class SortSearchDemo {
// バブルソート:隣接する要素を比較して交換する。計算量は平均・最悪ともにO(n^2)
static int bubbleSort(int[] arr) {
int comparisons = 0;
int n = arr.length;
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - 1 - i; j++) {
comparisons++;
if (arr[j] > arr[j + 1]) {
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
return comparisons;
}
// クイックソート:基準値(pivot)より小さい/大きいで分割する分割統治法
// 計算量は平均O(n log n)、ピボットの選び方が悪いと最悪O(n^2)
static int quickSort(int[] arr, int low, int high) {
if (low >= high) return 0;
int comparisons = 0;
int pivot = arr[high];
int i = low - 1;
for (int j = low; j < high; j++) {
comparisons++;
if (arr[j] < pivot) {
i++;
int tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp;
}
}
int tmp = arr[i + 1]; arr[i + 1] = arr[high]; arr[high] = tmp;
int pivotIndex = i + 1;
comparisons += quickSort(arr, low, pivotIndex - 1);
comparisons += quickSort(arr, pivotIndex + 1, high);
return comparisons;
}
// 二分探索:ソート済み配列が前提。計算量はO(log n)
static int binarySearch(int[] arr, int target) {
int low = 0, high = arr.length - 1;
while (low <= high) {
int mid = (low + high) / 2;
if (arr[mid] == target) return mid;
else if (arr[mid] < target) low = mid + 1;
else high = mid - 1;
}
return -1;
}
public static void main(String[] args) {
int[] dataForBubble = {64, 34, 25, 12, 22, 11, 90, 5, 77, 1};
int[] dataForQuick = dataForBubble.clone();
int bubbleComparisons = bubbleSort(dataForBubble);
int quickComparisons = quickSort(dataForQuick, 0, dataForQuick.length - 1);
System.out.println("バブルソート結果: " + Arrays.toString(dataForBubble)
+ "(比較回数: " + bubbleComparisons + ")");
System.out.println("クイックソート結果: " + Arrays.toString(dataForQuick)
+ "(比較回数: " + quickComparisons + ")");
int index = binarySearch(dataForQuick, 22);
System.out.println("22 はインデックス " + index + " に見つかりました");
}
}
サンプル3:例外処理とコレクションを使う銀行口座管理システム
口座をMap<String, Account>で管理し、残高不足を表す独自の検査例外(InsufficientBalanceException)を定義します。
検査例外なので呼び出し側は必ずtry-catchまたはthrowsで対応する必要があり、コンパイル時にエラー処理漏れを防げます。あわせてtry-with-resourcesで取引ログの書き込みリソースを安全に解放する例も示します。
SAMPLE PROGRAM
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
// 独自の検査例外:Exceptionを継承(RuntimeExceptionではないのでchecked exceptionになる)
class InsufficientBalanceException extends Exception {
InsufficientBalanceException(String message) {
super(message);
}
}
class Account {
private final String owner;
private long balance;
Account(String owner, long balance) {
this.owner = owner;
this.balance = balance;
}
public long getBalance() {
return balance;
}
public void withdraw(long amount) throws InsufficientBalanceException {
if (amount > balance) {
// 残高不足という業務エラーは検査例外として呼び出し元に対応を強制する
throw new InsufficientBalanceException(
owner + "さんの残高が不足しています(残高: " + balance + "円, 引出額: " + amount + "円)");
}
balance -= amount;
}
public void deposit(long amount) {
balance += amount;
}
}
class Bank {
// Mapで口座番号をキーに口座を管理する
private final Map accounts = new HashMap<>();
public void openAccount(String accountId, String owner, long initialBalance) {
accounts.put(accountId, new Account(owner, initialBalance));
}
public void transfer(String fromId, String toId, long amount) throws InsufficientBalanceException {
Account from = accounts.get(fromId);
Account to = accounts.get(toId);
if (from == null || to == null) {
throw new java.util.NoSuchElementException("指定された口座が見つかりません");
}
from.withdraw(amount); // ここでInsufficientBalanceExceptionがthrowされる可能性がある
to.deposit(amount);
// try-with-resources:FileWriterはAutoCloseableを実装しているため自動でclose()される
try (FileWriter log = new FileWriter("transfer.log", true)) {
log.write(fromId + " -> " + toId + " : " + amount + "円\n");
} catch (IOException e) {
System.out.println("ログの書き込みに失敗しました: " + e.getMessage());
}
}
public long balanceOf(String accountId) {
return accounts.get(accountId).getBalance();
}
}
public class Main {
public static void main(String[] args) {
Bank bank = new Bank();
bank.openAccount("A001", "田中", 5000);
bank.openAccount("A002", "佐藤", 1000);
try {
bank.transfer("A001", "A002", 3000);
System.out.println("A001の残高: " + bank.balanceOf("A001"));
System.out.println("A002の残高: " + bank.balanceOf("A002"));
bank.transfer("A001", "A002", 100_000); // 残高不足で例外が発生する
} catch (InsufficientBalanceException e) {
// 検査例外なのでcatch(またはthrows宣言)が必須。忘れるとコンパイルエラーになる
System.out.println("振込エラー: " + e.getMessage());
}
}
}