JavaJavaコンソールアプリ

【Java】コンソールで操作するメモ帳アプリを作成(2/2)

Javaメモ帳アプリ解説ページ2のアイキャッチ画像 Java

Javaを使用してコンソールで操作するクイズアプリケーションを作成する方法を解説します。この記事では、オブジェクト指向プログラミングの原則を活用し、ファイル操作ユーザー入力処理を含む実践的なアプリケーション開発のプロセスを紹介します。

はじめに

この記事のコードをコピペしてEclipseで出力結果を確認してみよう

コンソールで操作するメモ帳アプリ作成

以下のメモ帳アプリを作成します。

=== メモ帳アプリ ===
現在のメモ: なし
------------------------
1. 新規メモ作成
2. 既存のメモを開く
3. 現在のメモを保存
4. 現在のメモを編集
5. 終了
選択してください: 

メモ帳アプリ操作ガイド

アプリケーションの起動:

  1. Javaがインストールされている環境で、メモ帳アプリケーションを実行します。
  2. コンソールにメニューが表示されます。
操作選択肢操作手順結果
1. 新規メモ作成1・1を入力してEnterキーを押す
・新しいメモの内容をコンソールに入力
空行を入力してEnterキーを押す(Enterキーを2回押す)
新しいメモが作成される
2. 既存のメモを開く2・2を入力してEnterキーを押す
・ファイル選択ダイアログからファイルを選択
選択したファイルの内容が読み込まれ、コンソールに表示される
3. 現在のメモを保存3・3を入力してEnterキーを押す
・ファイル保存ダイアログで保存先とファイル名を指定
現在のメモが指定した場所に保存される
4. 現在のメモを編集4・4を入力してEnterキーを押す
・表示された内容を編集
編集終了後、空行を入力してEnterキーを押す(Enterキーを2回押す)
※ 最初の行に’cancel’と入力でキャンセル可能
メモが更新される
(キャンセル時は変更が反映されない)
5. アプリケーションの終了5・5を入力してEnterキーを押すアプリケーションが終了する

このガイドに従って、簡単にメモ帳アプリケーションをご利用いただけます。必要に応じて新しいメモを作成したり、既存のメモを開いて編集・保存できます。

コード例

  • クリックするとコードが表示されます。

Note.javaメモデータの構造を実装

Java
/**
 * メモを表すクラス
 */
public record Note(String content) {
	/**
	 * コンストラクタ
	 * @param content メモの内容
	 */
	public Note {
		if (content == null) {
			throw new IllegalArgumentException("Content cannot be null");
		}
	}
}

NotePadApp.javaメインアプリケーションロジックユーザーインターフェースを実装

Java
import java.io.File;
import java.util.Scanner;

import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.filechooser.FileNameExtensionFilter;

/**
 * メモ帳アプリケーションのメインクラス
 */
public class NotePadApp {
	private final NoteManager noteManager;
	private final Scanner scanner;
	private final JFileChooser fileChooser;

	/**
	 * NotePadAppのコンストラクタ
	 * 必要なオブジェクトの初期化を行う
	 */
	public NotePadApp() {
		this.noteManager = new NoteManager();
		this.scanner = new Scanner(System.in);
		this.fileChooser = createCustomJFileChooser();
	}

	/**
	 * カスタマイズされたJFileChooserを作成する
	 */
	private JFileChooser createCustomJFileChooser() {
		JFileChooser chooser = new JFileChooser() {
			@Override
			public void approveSelection() {
				File selectedFile = getSelectedFile();
				if (selectedFile.exists()) {
					int response = JOptionPane.showConfirmDialog(this,
							"ファイル '" + selectedFile.getName() + "' はすでに存在します。\n上書きしますか?",
							"ファイルの上書き確認",
							JOptionPane.YES_NO_OPTION,
							JOptionPane.WARNING_MESSAGE);
					if (response != JOptionPane.YES_OPTION) {
						return;
					}
				}
				super.approveSelection();
			}
		};
		FileNameExtensionFilter filter = new FileNameExtensionFilter("テキストファイル", "txt");
		chooser.setFileFilter(filter);
		return chooser;
	}

	/**
	 * アプリケーションのメインループを実行する
	 */
	public void run() {
		while (true) {
			displayMenu();
			int choice = scanner.nextInt();
			scanner.nextLine(); // 改行文字を消費

			switch (choice) {
			case 1 -> createNewNote();
			case 2 -> openExistingNote();
			case 3 -> saveCurrentNote();
			case 4 -> editCurrentNote();
			case 5 -> {
				System.out.println("アプリケーションを終了します。");
				return;
			}
			default -> System.out.println("無効な選択です。もう一度お試しください。");
			}
		}
	}

	/**
	 * メインメニューを表示する
	 */
	private void displayMenu() {
		System.out.println("\n=== メモ帳アプリ ===");
		displayCurrentNoteInfo();
		System.out.println("1. 新規メモ作成");
		System.out.println("2. 既存のメモを開く");
		System.out.println("3. 現在のメモを保存");
		System.out.println("4. 現在のメモを編集");
		System.out.println("5. 終了");
		System.out.print("選択してください: ");
	}

	/**
	 * 現在のメモの情報を表示する
	 */
	private void displayCurrentNoteInfo() {
		Note currentNote = noteManager.getCurrentNote();
		String fileName = noteManager.getCurrentFileName();
		if (currentNote != null) {
			System.out.println("現在のメモ:");
			System.out.println("ファイル名: " + (fileName != null ? fileName : "未保存"));
			System.out.println("内容:");
			System.out.println(currentNote.content());
			System.out.println("------------------------");
		} else {
			System.out.println("現在のメモ: なし");
			System.out.println("------------------------");
		}
	}

	/**
	 * 新規メモを作成する
	 */
	private void createNewNote() {
		System.out.println("新しいメモの内容を入力してください。入力が終わったら空行を入力してEnterキーを押してください:");
		StringBuilder content = new StringBuilder();
		String line;
		while (!(line = scanner.nextLine()).isEmpty()) {
			content.append(line).append("\n");
		}
		noteManager.setCurrentNote(new Note(content.toString().trim()));
		noteManager.setCurrentFileName(null);
		System.out.println("新しいメモが作成されました。");
	}

	/**
	 * 既存のメモを開く
	 */
	private void openExistingNote() {
		int result = fileChooser.showOpenDialog(null);
		if (result == JFileChooser.APPROVE_OPTION) {
			File selectedFile = fileChooser.getSelectedFile();
			try {
				noteManager.loadNote(selectedFile.getAbsolutePath());
				noteManager.setCurrentFileName(selectedFile.getName());
				System.out.println("メモを開きました: " + selectedFile.getName());
			} catch (Exception e) {
				System.out.println("エラー: " + e.getMessage());
			}
		}
	}

	/**
	 * 現在のメモを保存する
	 */
	private void saveCurrentNote() {
		if (noteManager.getCurrentNote() == null) {
			System.out.println("保存するメモがありません。");
			return;
		}
		int result = fileChooser.showSaveDialog(null);
		if (result == JFileChooser.APPROVE_OPTION) {
			File selectedFile = fileChooser.getSelectedFile();
			// ファイル名に .txt 拡張子を追加(必要な場合)
			if (!selectedFile.getName().toLowerCase().endsWith(".txt")) {
				selectedFile = new File(selectedFile.getParentFile(), selectedFile.getName() + ".txt");
			}
			try {
				noteManager.saveNote(selectedFile.getAbsolutePath());
				noteManager.setCurrentFileName(selectedFile.getName());
				System.out.println("メモを保存しました: " + selectedFile.getName());
			} catch (Exception e) {
				System.out.println("エラー: " + e.getMessage());
			}
		}
	}

	/**
	 * 現在のメモを編集する
	 */
	private void editCurrentNote() {
		if (noteManager.getCurrentNote() == null) {
			System.out.println("編集するメモがありません。新規メモを作成するか、既存のメモを開いてください。");
			return;
		}
		System.out.println("現在のメモ内容を編集します。編集が終わったら空行を入力してください。");
		System.out.println("編集をキャンセルする場合は、最初の行に 'cancel' と入力してください。");
		System.out.println("現在の内容:");
		System.out.println(noteManager.getCurrentNote().content());

		StringBuilder newContent = new StringBuilder();
		String line = scanner.nextLine();
		if ("cancel".equalsIgnoreCase(line.trim())) {
			System.out.println("編集をキャンセルしました。");
			return;
		}
		newContent.append(line).append("\n");

		while (!(line = scanner.nextLine()).isEmpty()) {
			newContent.append(line).append("\n");
		}
		noteManager.setCurrentNote(new Note(newContent.toString().trim()));
		System.out.println("メモが更新されました。");
	}
}

NoteManager.javaメモの管理機能を実装し、ファイル操作の基本

Java
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

/**
 * メモの管理を行うクラス
 */
public class NoteManager {
	private Note currentNote;
	private String currentFileName;

	/**
	 * 現在のメモを取得
	 * @return 現在のメモ
	 */
	public Note getCurrentNote() {
		return currentNote;
	}

	/**
	 * 現在のメモを設定
	 * @param note 設定するメモ
	 */
	public void setCurrentNote(Note note) {
		this.currentNote = note;
	}

	/**
	 * 現在のファイル名を取得
	 * @return 現在のファイル名
	 */
	public String getCurrentFileName() {
		return currentFileName;
	}

	/**
	 * 現在のファイル名を設定
	 * @param fileName 設定するファイル名
	 */
	public void setCurrentFileName(String fileName) {
		this.currentFileName = fileName;
	}

	/**
	 * メモをファイルから読み込む
	 * @param fileName 読み込むファイル名
	 * @throws IOException ファイル読み込みエラー時
	 */
	public void loadNote(String fileName) throws IOException {
		Path path = Paths.get(fileName);
		String content = Files.readString(path);
		this.currentNote = new Note(content);
		this.currentFileName = path.getFileName().toString();
	}

	/**
	 * 現在のメモをファイルに保存
	 * @param fileName 保存するファイル名
	 * @throws IOException ファイル書き込みエラー時
	 */
	public void saveNote(String fileName) throws IOException {
		if (currentNote == null) {
			throw new IllegalStateException("No note to save");
		}
		Path path = Paths.get(fileName);
		Files.writeString(path, currentNote.content());
		this.currentFileName = path.getFileName().toString();
	}
}

Main.javaで開始地点を実装

Java
/**
 * メモ帳アプリケーションのメインクラス
 */
public class Main {
	/**
	 * アプリケーションのエントリーポイント
	 * @param args コマンドライン引数(使用しない)
	 */
	public static void main(String[] args) {
		NotePadApp app = new NotePadApp();
		app.run();
	}
}

NoteManager.javaの解説

インポート文

Java
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

これらは必要なクラスをインポートしています:

  • IOException: 入出力操作に関連する例外を処理するために使用
  • Files, Path, Paths: ファイル操作を行うためのモダンなJava NIO API

クラス定義

Java
/**
 * メモの管理を行うクラス
 */
public class NoteManager {
    // ...
}

これはNoteManagerクラスの定義です。JavaDocコメントでクラスの目的を説明しています。

フィールド

Java
private Note currentNote;
private String currentFileName;

これらはクラスのメンバー変数(フィールド)です:

  • currentNote: 現在編集中のメモを保持
  • currentFileName: 現在のファイル名を保持

ゲッターとセッター

Java
	/**
	 * 現在のメモを取得
	 * @return 現在のメモ
	 */
	public Note getCurrentNote() {
		return currentNote;
	}

	/**
	 * 現在のメモを設定
	 * @param note 設定するメモ
	 */
	public void setCurrentNote(Note note) {
		this.currentNote = note;
	}

	/**
	 * 現在のファイル名を取得
	 * @return 現在のファイル名
	 */
	public String getCurrentFileName() {
		return currentFileName;
	}
	
	/**
	 * 現在のファイル名を設定
	 * @param fileName 設定するファイル名
	 */
	public void setCurrentFileName(String fileName) {
		this.currentFileName = fileName;
	}

これらはゲッターとセッターメソッドです:

  • currentNotecurrentFileNameのアクセスと変更を可能にします
  • カプセル化の原則に従っています

メモの読み込み

メソッド定義

Java
/**
 * メモをファイルから読み込む
 * @param fileName 読み込むファイル名
 * @throws IOException ファイル読み込みエラー時
 */
public void loadNote(String fileName) throws IOException {
    // ...
}
  • これはloadNoteというパブリックメソッドです。
  • ファイルからメモを読み込む機能を提供します。
  • JavaDocコメントでメソッドの目的、パラメータ、例外を説明しています。
  • throws IOExceptionは、このメソッドがIOExceptionを発生させる可能性があることを示しています。

ファイルパスの作成

Java
Path path = Paths.get(fileName);
  • Paths.get()メソッドを使用して、ファイル名からPathオブジェクトを作成します。
  • Pathは、ファイルシステム内のファイルやディレクトリの場所を表します。

ファイルの内容を読み込む

Java
String content = Files.readString(path);
  • Files.readString()メソッドを使用して、ファイルの内容を一度に全て読み込みます。
  • 読み込んだ内容はStringとしてcontent変数に格納されます。

新しいNoteオブジェクトの作成

Java
this.currentNote = new Note(content);
  • 読み込んだ内容を使用して、新しいNoteオブジェクトを作成します。
  • この新しいオブジェクトをcurrentNoteフィールドに代入します。

現在のファイル名の設定

Java
this.currentFileName = path.getFileName().toString();
  • path.getFileName()ファイル名部分のみを取得し、それを文字列に変換します。
  • 取得したファイル名をcurrentFileNameフィールドに設定します。

重要なポイント:

  1. 例外処理: このメソッドはIOExceptionをスローすることを宣言しています。呼び出し元でこの例外を適切に処理する必要があります。
  2. Java NIO API: PathPathsFilesクラスを使用して、モダンなファイル操作を行っています。
  3. ファイル読み込み: Files.readString()を使用して、ファイルの内容を効率的に読み込んでいます。
  4. 状態管理: 読み込んだ内容を新しいNoteオブジェクトとして保存し、現在のファイル名も更新しています。
  5. メソッドの単一責任: このメソッドは「ファイルからメモを読み込む」という単一の責任を持っています。

このコードは、Java NIOを使用したファイル操作の良い例です。効率的なファイル読み込み、適切な例外処理、そしてオブジェクト指向プログラミングの原則(新しいNoteオブジェクトの作成)を示しています。また、メソッドの責任が明確で、再利用性が高いコードとなっています。

メモの保存

メソッド定義

Java
/**
 * 現在のメモをファイルに保存
 * @param fileName 保存するファイル名
 * @throws IOException ファイル書き込みエラー時
 */
public void saveNote(String fileName) throws IOException {
    // ...
}
  • これはsaveNoteというパブリックメソッドです。
  • 現在のメモをファイルに保存する機能を提供します。
  • JavaDocコメントでメソッドの目的、パラメータ、例外を説明しています。
  • throws IOExceptionは、このメソッドがIOExceptionを発生させる可能性があることを示しています。

保存するメモの存在確認

Java
if (currentNote == null) {
    throw new IllegalStateException("No note to save");
}
  • 現在のメモが存在するかどうかをチェックします。
  • メモがない場合(currentNotenullの場合)、IllegalStateExceptionをスローします。
  • これは、メソッドの前提条件を確認する重要なステップです。

ファイルパスの作成

Java
Path path = Paths.get(fileName);
  • Paths.get()メソッドを使用して、ファイル名からPathオブジェクトを作成します。
  • Pathは、ファイルシステム内のファイルやディレクトリの場所を表します。

ファイルへの書き込み

Java
Files.writeString(path, currentNote.content());
  • Files.writeString()メソッドを使用して、メモの内容をファイルに書き込みます。
  • currentNote.content()で現在のメモの内容を取得し、それをファイルに書き込みます。

現在のファイル名の更新

Java
this.currentFileName = path.getFileName().toString();
  • path.getFileName()ファイル名部分のみを取得し、それを文字列に変換します。
  • 取得したファイル名をcurrentFileNameフィールドに設定します。

重要なポイント:

  1. 例外処理:
    • このメソッドはIOExceptionをスローすることを宣言しています。
    • また、不適切な状態(メモがない)の場合にIllegalStateExceptionをスローします。
  2. Java NIO API: PathPathsFilesクラスを使用して、モダンなファイル操作を行っています。
  3. ファイル書き込み: Files.writeString()を使用して、ファイルへの書き込みを効率的に行っています。
  4. 状態チェック: メソッドの開始時に現在のメモの存在を確認し、不正な操作を防いでいます。
  5. 状態管理: 保存後に現在のファイル名を更新しています。
  6. メソッドの単一責任: このメソッドは「現在のメモをファイルに保存する」という単一の責任を持っています。

このコードは、Java NIOを使用したファイル操作、適切な例外処理、そして堅牢なプログラミング(前提条件のチェック)の良い例です。また、メソッドの責任が明確で、再利用性が高いコードとなっています。

Main.javaの解説

クラス定義

Java
/**
 * メモ帳アプリケーションのメインクラス
 */
public class Main {
    // ...
}
  • これはMainクラスの定義です。
  • public キーワードは、このクラスが他のクラスからアクセス可能であることを示します。
  • クラス名の上にある部分はJavaDocコメントで、クラスの目的を説明しています。

main メソッド

Java
/**
 * アプリケーションのエントリーポイント
 * @param args コマンドライン引数(使用しない)
 */
public static void main(String[] args) {
    // ...
}
  • これはmainメソッドで、Javaアプリケーションのエントリーポイント(開始点)です。
  • public: このメソッドが外部からアクセス可能であることを示します。
  • static: このメソッドがクラスに属し、インスタンス化せずに呼び出せることを示します。
  • void: このメソッドが値を返さないことを示します。
  • String[] args: コマンドライン引数を受け取るための配列です(この場合は使用していません)。

アプリケーションの実行

Java
NotePadApp app = new NotePadApp();
app.run();
  • NotePadAppクラスの新しいインスタンスを作成しています。
  • app.run()を呼び出して、アプリケーションを開始しています。

重要なポイント:

  1. メインクラス: このクラスは、アプリケーションの起動点として機能します。
  2. main メソッド: Javaアプリケーションを実行する際に、JVMが最初に呼び出すメソッドです。
  3. インスタンス化: NotePadAppクラスのインスタンスを作成することで、アプリケーションのメイン機能を初期化しています。
  4. メソッド呼び出し: run()メソッドを呼び出すことで、アプリケーションの主要な処理を開始しています。
  5. シンプルな構造: このクラスは非常にシンプルで、アプリケーションの開始のみを担当しています。実際の機能はNotePadAppクラスに実装されています。

このコードは、Javaアプリケーションの標準的な開始方法を示しています。Mainクラスは、アプリケーションの他の部分(この場合はNotePadApp)を初期化し、実行するための橋渡し役として機能します。これにより、アプリケーションの構造が整理され、メイン処理を別のクラス(NotePadApp)に集中させることができます。

まとめ

  • NoteManager.javaメモの管理機能を実装し、ファイル操作の基本
  • Main.javaメインアプリケーションロジックユーザーインターフェースを実装
  • ファイル読み書きの実装方法、データの永続化について
  • 例外処理を通じて、堅牢なエラーハンドリングの実装方法
  • オブジェクト指向設計の原則を実践し、モジュール化されたコードの書き方
  • ユーザー入力の処理コンソールベースのインターフェース設計

NoteManagerクラスの実装を通じて、ファイル操作データ管理の基本的な概念を学ぶことができます。これは多くのアプリケーション開発で必要とされる重要なスキルです。

また、追加の機能(例:メモの検索、タグ付け、複数ユーザー対応など)を実装することで、さらに複雑なアプリケーションへと発展させることができます。

未経験でもWEBスキルを取得したい方
  • Javaコース
    Javaプログラミングを基礎から応用まで、体系的に学べる実践的なカリキュラムが特徴です。「マインクラフト」を通じてJavaの基本文法とフレームワークの概念を学びます。また、実際のプロジェクト開発を通じて、データベース連携やWebアプリケーション開発のスキルを身につけることができます。

コメント

タイトルとURLをコピーしました