Javaでプログラミングを学ぶ際、最初の一歩として「画面に文字を出力すること」を覚えますが、その次に行うべき重要なステップが「ユーザーからの入力を受け取ること」です。

Javaにおいて、キーボードなどからの入力を受け取る処理は「標準入力」と呼ばれます。

標準入力をマスターすることで、動的に値を変更できるインタラクティブなプログラムを作成できるようになります。

Javaには標準入力を実現するための方法がいくつか存在しますが、主に利用されるのは「Scannerクラス」「BufferedReaderクラス」の2つです。

本記事では、これら2つのツールの使い方から、それぞれのメリット・デメリット、そして実務や競技プログラミングで役立つ使い分けのポイントまで、徹底的に解説していきます。

Javaの標準入力とは

Javaにおける標準入力の基本は、System.inというオブジェクトにあります。

これは、Javaのシステムが標準的に用意している入力ストリームを指します。

しかし、System.inはバイト単位の低レベルなストリームであるため、そのままでは人間が読み取れる「文字列」や「数値」として扱うには非常に不便です。

そこで、このSystem.inをラップ(包み込む)して、より扱いやすくするためのクラスが用意されています。

それがScannerBufferedReaderです。

これらのクラスを利用することで、1行単位でデータを読み込んだり、特定の型に自動で変換したりすることが可能になります。

Scannerクラスによる入力処理

Scannerクラスは、java.utilパッケージに含まれるクラスで、Java 5から導入されました。

最大の特徴は、入力された文字列を数値や特定の型に簡単に変換できる点にあります。

初心者にとって最も扱いやすく、直感的な操作が可能です。

Scannerの基本的な使い方

Scannerを使用するには、まずインスタンスを生成する必要があります。

コンストラクタの引数にSystem.inを渡すことで、標準入力をスキャン対象に指定します。

Java
import java.util.Scanner;

public class ScannerExample {
    public static void main(String[] args) {
        // Scannerのインスタンスを作成
        Scanner sc = new Scanner(System.in);

        System.out.print("名前を入力してください: ");
        // 文字列を入力
        String name = sc.nextLine();

        System.out.print("年齢を入力してください: ");
        // 数値を入力
        int age = sc.nextInt();

        System.out.println("こんにちは、" + name + "さん!");
        System.out.println("あなたは" + age + "歳ですね。");

        // 使用後はクローズする(try-with-resourcesの使用が推奨される)
        sc.close();
    }
}
実行結果
名前を入力してください: 山田太郎
年齢を入力してください: 25
こんにちは、山田太郎さん!
あなたは25歳ですね。

Scannerの主なメソッド

Scannerクラスには、データの種類に合わせて使い分けられる便利なメソッドが多数用意されています。

next()

次のトークン(空白や改行で区切られた文字列)を読み込みます。

nextLine()

現在の行全体を読み込みます。

nextInt()

入力を int 型として読み込みます。

nextDouble()

入力を double 型として読み込みます。

nextBoolean()

入力を boolean 型として読み込みます。

Scanner利用時の注意点:改行文字の残留

Scannerを使う上で、初心者が必ずと言っていいほど遭遇する落とし穴があります。

それが「nextInt() などの後に nextLine() を呼び出した際の挙動」です。

nextInt() は数値を読み取りますが、入力の最後にある「改行(Enterキー)」を読み飛ばさずにバッファに残してしまいます。

その直後に nextLine() を呼ぶと、バッファに残っていた改行文字だけを読み取ってしまい、入力を受け付けずに空の文字列が返ってくる現象が発生します。

これを防ぐには、数値を読み取った直後に一度空の sc.nextLine() を実行して、バッファをクリアする必要があります。

BufferedReaderクラスによる入力処理

BufferedReaderクラスは、java.ioパッケージに含まれる古くからあるクラスです。

その名の通り「バッファリング」を行いながらデータを読み込むため、大量のデータを効率よく読み込む場合に非常に高いパフォーマンスを発揮します。

BufferedReaderの基本的な使い方

BufferedReaderは単独では標準入力を扱えません。

まず InputStreamReader でバイトストリームを文字ストリームに変換し、それを BufferedReader で包むという手順を踏みます。

Java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class BufferedReaderExample {
    public static void main(String[] args) {
        // 標準入力のための準備
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

        try {
            System.out.print("何かメッセージを入力してください: ");
            // 1行読み込み
            String input = reader.readLine();
            System.out.println("入力された内容: " + input);

            System.out.print("数値を入力してください: ");
            // 数値として扱う場合は自分で変換が必要
            String numStr = reader.readLine();
            int number = Integer.parseInt(numStr);
            System.out.println("入力された数値の2倍: " + (number * 2));

        } catch (IOException e) {
            // 入出力エラーのハンドリング
            e.printStackTrace();
        } catch (NumberFormatException e) {
            // 数値変換エラーのハンドリング
            System.out.println("数値以外の文字が入力されました。");
        }
    }
}
実行結果
何かメッセージを入力してください: Hello Java
入力された内容: Hello Java
数値を入力してください: 100
入力された数値の2倍: 200

BufferedReaderの特徴と注意点

BufferedReaderを使用する際には、以下の2点に注意が必要です。

例外処理が必要

readLine() メソッドは IOException を投げる可能性があるため、try-catch ブロックで囲むか、メソッドに throws を記述する必要があります。

型変換が手動

Scannerのように nextInt() といった便利なメソッドはありません。

すべて一度文字列として読み込み、Integer.parseInt() などを使って自分で型変換を行う必要があります。

ScannerとBufferedReaderの比較

どちらのクラスを使うべきか判断するために、主要な項目の違いを表にまとめました。

項目ScannerBufferedReader
パッケージjava.utiljava.io
読み込み速度比較的遅い(小規模な入力向き)非常に速い(大規模な入力向き)
型変換自動(nextInt()など)手動(Stringからの変換が必要)
例外処理不要(非チェック例外)必須(IOException)
バッファサイズ小さい(1KB)大きい(8KB)
主な用途学習・小規模なコンソールアプリ競技プログラミング・大量のデータ処理

結論として、一般的な学習や簡単なツール作成であればScannerが適しており、実行速度が重視される競技プログラミングや、何万行ものテキストデータを処理する場合はBufferedReaderが適しています。

Java 7以降の推奨:try-with-resourcesの活用

標準入力やファイル入力などの「リソース」を扱う場合、使い終わった後に確実にクローズ(close())することがメモリリークを防ぐために重要です。

Java 7で導入されたtry-with-resources文を使用すると、明示的に close() を記述しなくても、ブロックを抜ける際に自動的にリソースが解放されます。

try-with-resourcesの記述例

Java
import java.util.Scanner;

public class ResourceManagement {
    public static void main(String[] args) {
        // tryの後に括弧でリソースを宣言する
        try (Scanner sc = new Scanner(System.in)) {
            System.out.print("入力をどうぞ: ");
            if (sc.hasNext()) {
                System.out.println("読み込み完了: " + sc.next());
            }
        } // ここで自動的に sc.close() が呼ばれる
    }
}

この書き方をすることで、予期せぬエラー(例外)が発生した場合でも確実にリソースが解放されるため、現代のJava開発においては必須のテクニックと言えます。

特殊な入力:System.console()

パスワードなどの機密情報を入力する場合、入力した文字が画面に表示されてしまうのはセキュリティ上好ましくありません。

その場合、System.console() を利用する方法があります。

Java
import java.io.Console;

public class ConsoleExample {
    public static void main(String[] args) {
        Console console = System.console();

        if (console != null) {
            String username = console.readLine("ユーザー名: ");
            // パスワードを画面に表示せずに入力
            char[] passwordChars = console.readPassword("パスワード: ");
            
            System.out.println("ログイン試行: " + username);
            // パスワードはセキュリティのため、使い終わったらすぐクリアすることが推奨される
        } else {
            System.out.println("コンソールが利用できません。");
        }
    }
}

ただし、System.console() はIDE(EclipseやIntelliJ IDEAなど)の実行コンソールでは null を返すことがあるため、主にコマンドプロンプトやターミナルで直接実行するプログラムで使用されます。

競技プログラミングにおける標準入力のテクニック

競技プログラミング(AtCoderなど)では、実行時間の制約が厳しいため、標準入力の速度が合否を分けることがあります。

Scannerは非常に便利ですが、内部での正規表現による解析処理が重いため、大量の数値を読み込む際にタイムアウト(TLE)の原因になることがあります。

StringTokenizerとの組み合わせ

BufferedReaderで読み込んだ1行の文字列を、空白区切りで高速に分割するには java.util.StringTokenizer クラスを組み合わせるのが一般的です。

Java
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class FastInput {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        
        // 1行に複数の数値がある場合: "10 20 30"
        String line = br.readLine();
        StringTokenizer st = new StringTokenizer(line);
        
        int a = Integer.parseInt(st.nextToken());
        int b = Integer.parseInt(st.nextToken());
        int c = Integer.parseInt(st.nextToken());
        
        System.out.println("合計: " + (a + b + c));
    }
}

このように、BufferedReaderで読み込み、StringTokenizerで切り分けるという手法は、Javaで高速な入出力を行うための王道パターンです。

入力値のバリデーション(検証)

実務的なアプリケーションでは、ユーザーが常に期待通りの値を入力するとは限りません。

数値入力を求めているのに文字列が入力された場合、プログラムは例外を投げて異常終了してしまいます。

これを防ぐためには、Scannerの hasNextInt() などのメソッドを使用して、事前に入力値が適切かどうかを確認する習慣をつけましょう。

Java
Scanner sc = new Scanner(System.in);
System.out.print("数値を入力してください: ");

if (sc.hasNextInt()) {
    int val = sc.nextInt();
    System.out.println("数値を確認しました: " + val);
} else {
    System.out.println("エラー: 数値を入力してください。");
}

このように、「入力値が期待する型であるか」を確認してから処理に移ることで、堅牢なプログラムを構築することができます。

まとめ

Javaで標準入力を扱う方法は、用途に応じて使い分けることが重要です。

Scanner

直感的に使え、数値や文字列の切り分けが自動で行えるため、学習や小規模なプログラムに最適です。

BufferedReader

構造はやや複雑ですが、圧倒的な読み込み速度を誇ります。

大規模データ処理や競技プログラミングにはこちらを選びましょう。

try-with-resources

どちらのクラスを使う場合も、リソースの解放を自動化するためにこの構文を使用することが現代的なJavaの書き方です。

バリデーション

入力ミスによるエラーを防ぐため、常に「予期せぬ入力」への対策を講じる必要があります。

標準入力は、ユーザーと対話するプログラムを作るための第一歩です。

まずはScannerから使い始め、徐々にパフォーマンスやエラーハンドリングを意識したBufferedReaderへとステップアップしていくことで、より実践的なJavaスキルの習得に繋がります。

今回紹介したコード例を実際に自分の環境で動かし、その挙動の違いを体感してみてください。