← JAVA言語ハブページに戻る

JAVA言語のサンプルコード集

文法解説で学んだ内容を組み合わせた、難易度が段階的に上がる3本の実践的なサンプルプログラムです。①クラス設計とポリモーフィズムを使った蔵書管理、②整列・探索アルゴリズムの実装と計算量の比較、③例外処理とコレクションを使った銀行口座管理、の順に紹介します。

サンプル1:クラス設計を使う蔵書管理システム(カプセル化・継承・ポリモーフィズム)

抽象クラスItemを継承したBookMagazineクラスを用意し、共通のリスト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());
        }
    }
}
理解度チェッククイズへ進む →