Program yang kita tulis sejauh ini berjalan satu arah: semua nilai sudah tertanam di dalam kode. Interaksi baru benar-benar terasa ketika program dapat membaca ketikan pengguna selagi program berjalan. Java menyediakan beberapa jalur untuk membaca input, dan jalur yang paling ramah bagi pemula adalah class Scanner dari package java.util. Chapter ini membahas cara kerja Scanner, perbedaan antara method berbasis token dan berbasis baris, jebakan baris baru yang paling sering mengejutkan pemula, serta cara menangani input yang tidak sesuai tipe.

Class Scanner dan Model Token #

System.in adalah stream byte mentah yang datang dari keyboard. Class Scanner bertugas menerjemahkan stream tersebut menjadi teks, lalu memecah teks itu menjadi token — potongan kata yang dipisahkan oleh delimiter. Delimiter bawaan Scanner adalah whitespace: spasi, tab, dan baris baru. Dari model token inilah lahir berbagai method pembacaan: next() mengambil satu token sebagai String, nextInt() mengambil token sekaligus mengonversinya menjadi int, nextDouble() menjadi double, dan nextLine() membaca satu baris utuh tanpa memedulikan delimiter.

Mari kita mulai dengan program biodata sederhana yang membaca tiga nilai bertipe berbeda:

java BacaBiodata.java
import java.util.Scanner;

public class BacaBiodata {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        System.out.print("Nama lengkap: ");
        String nama = sc.nextLine();

        System.out.print("Umur: ");
        int umur = sc.nextInt();

        System.out.print("Berat badan (kg): ");
        double berat = sc.nextDouble();

        System.out.println();
        System.out.println("Biodata " + nama);
        System.out.println("Umur  : " + umur + " tahun");
        System.out.println("Berat : " + berat + " kg");
    }
}

Output yang Dihasilkan:

text
Nama lengkap: Umur: Berat badan (kg): 
Biodata Budi Santoso
Umur  : 17 tahun
Berat : 62.5 kg

Mari kita telaah baris-baris kode di atas:

  1. Baris 1 (import java.util.Scanner;): mengimpor class Scanner dari package java.util supaya dapat dipakai tanpa menulis nama package lengkap.
  2. Baris 5 (Scanner sc = new Scanner(System.in);): membuat object Scanner yang membaca dari System.in. Scanner menyangga data yang masuk, sehingga satu ketikan pengguna dapat melayani beberapa panggilan next berikutnya.
  3. Baris 7-8 (System.out.print(...) dan String nama = sc.nextLine();): prompt dicetak tanpa baris baru, lalu nextLine() membaca seluruh baris ketikan — termasuk spasi di tengah nama “Budi Santoso” — sampai tanda Enter ditekan.
  4. Baris 10-11 (int umur = sc.nextInt();): nextInt() mengambil satu token dan mengonversinya menjadi int secara langsung, tanpa perlu Integer.parseInt manual.
  5. Baris 13-14 (double berat = sc.nextDouble();): pola yang sama berlaku untuk bilangan desimal; titik dipakai sebagai pemisah desimal sesuai Locale default sistem.

Membedakan next dan nextLine #

Perbedaan antara method token dan method baris sering menjadi sumber kebingungan, jadi mari kita petakan secara eksplisit:

MethodMengembalikanBerhenti diMemakan baris baru?
next()satu token (String)delimiter apa puntidak
nextInt()int dari satu tokendelimiter apa puntidak
nextDouble()double dari satu tokendelimiter apa puntidak
nextLine()satu baris utuh (String)baris baruya

Method token hanya mengambil potongan yang diperlukan lalu berhenti tepat sebelum delimiter. Sebaliknya, nextLine() membaca semua karakter hingga tanda baris baru, lalu mengonsumsi tanda baris baru itu sebagai penutup baris. Perilaku “memakan atau tidak memakan baris baru” inilah yang memunculkan jebakan pada bagian berikutnya.

Jebakan Baris Baru setelah nextInt #

Bayangkan urutan input yang wajar: pengguna mengetik umur lebih dulu, lalu nama. Kode yang mengikuti urutan tersebut secara harfiah akan menghasilkan kejutan — nextLine() yang dipanggil tepat setelah nextInt() mengembalikan string kosong, bukan nama yang diketik. Mekanismenya begini: ketika pengguna menekan Enter setelah mengetik 17, buffer input berisi 17 diikuti tanda baris baru. Method nextInt() membaca angka 17 lalu berhenti tepat sebelum tanda baris baru tanpa mengonsumsinya. Panggilan nextLine() berikutnya melihat sisa tanda baris baru itu, menganggap baris telah berakhir, dan langsung mengembalikan string kosong.

Program berikut membuktikan kejadian tersebut dengan input 17 lalu Budi:

import java.util.Scanner;

public class JebakanNewline {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        int umur = sc.nextInt();      // berhenti tepat sebelum baris baru
        String nama = sc.nextLine();  // langsung mendapat string kosong

        System.out.println("Umur        : " + umur);
        System.out.println("Nama        : \"" + nama + "\"");
        System.out.println("Panjang nama: " + nama.length());

        String namaBaru = sc.nextLine(); // baris berikutnya akhirnya terbaca
        System.out.println("Nama kedua  : \"" + namaBaru + "\"");
    }
}

Output yang Dihasilkan:

text
Umur        : 17
Nama        : ""
Panjang nama: 0
Nama kedua  : "Budi"

Baris Nama bernilai string kosong dengan panjang 0, sementara Budi baru terbaca pada pemanggilan nextLine() kedua — persis seperti analisis buffer di atas.

Ada dua perbaikan standar untuk jebakan ini. Pertama, buang sisa baris dengan satu panggilan sc.nextLine() tambahan yang hasilnya diabaikan, baru kemudian baca baris yang sebenarnya. Kedua, baca semua input sebagai baris melalui nextLine(), lalu konversi angka secara manual — pola ini sekaligus menjadi fondasi Chapter Membaca Input dengan BufferedReader:

java
int umur = Integer.parseInt(sc.nextLine()); // baca baris utuh, lalu konversi
String nama = sc.nextLine();

Menangani Input yang Tidak Sesuai Tipe #

Scanner percaya diri mengonversi token, tetapi kepercayaan itu runtuh ketika pengguna mengetik teks pada tempat angka. Method nextInt() akan melempar InputMismatchException — sebuah unchecked exception dari package java.util — dan yang tidak kalah penting: token yang bermasalah itu tidak dikonsumsi sehingga masih menunggu di buffer. Program berikut membungkus pembacaan dalam blok try-catch, membuang token yang salah dengan sc.next(), lalu meminta ulang sampai input valid:

import java.util.InputMismatchException;
import java.util.Scanner;

public class BacaUmurAman {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int umur = -1;

        while (umur < 0) {
            System.out.print("Masukkan umur (bilangan bulat): ");
            try {
                umur = sc.nextInt();
            } catch (InputMismatchException e) {
                String token = sc.next(); // buang token yang tidak valid
                System.out.println("Token \"" + token + "\" bukan bilangan bulat.");
            }
        }
        System.out.println("Umur tercatat: " + umur);
    }
}

Output yang Dihasilkan:

text
Masukkan umur (bilangan bulat): Token "duapuluh" bukan bilangan bulat.
Masukkan umur (bilangan bulat): Umur tercatat: 17

Mari kita telaah alurnya:

  1. Baris 7 (int umur = -1;): nilai penanda yang membuat loop while masuk pada putaran pertama.
  2. Baris 11-12 (try { umur = sc.nextInt(); }): pembacaan angka dijaga blok try; jika token bukan bilangan bulat, JVM melempar InputMismatchException dan eksekusi melompat ke blok catch.
  3. Baris 13-14 (catch dan String token = sc.next();): blok catch membaca ulang token bermasalah dengan sc.next() — langkah wajib, sebab token gagal tidak terkonsumsi dan tanpa langkah ini loop akan membaca token yang sama tanpa henti.
  4. Baris 17-18: setelah umur bernilai valid, loop berakhir dan hasilnya dicetak.

Kapan Memakai Scanner #

Scanner unggul ketika kita butuh pemecahan token yang fleksibel: membaca angka dan kata bercampur dalam satu baris, mengganti delimiter dengan useDelimiter(), atau membaca dari berkas selain keyboard. Untuk program interaktif biasa, kenyamanan nextInt dan nextDouble lebih terasa daripada perbedaan performa. Namun ketika program harus memproses baris demi baris dalam jumlah besar — misalnya membaca banyak data dari pipa atau berkas — pendekatan berbasis baris dengan BufferedReader lebih tepat. Chapter berikutnya membahas BufferedReader: membaca baris utuh secara efisien dan mengonversinya sendiri dengan Integer.parseInt.

Referensi #

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

BacaBiodata.java

Unduh berkas
java
import java.util.Scanner;

public class BacaBiodata {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        System.out.print("Nama lengkap: ");
        String nama = sc.nextLine();

        System.out.print("Umur: ");
        int umur = sc.nextInt();

        System.out.print("Berat badan (kg): ");
        double berat = sc.nextDouble();

        System.out.println();
        System.out.println("Biodata " + nama);
        System.out.println("Umur  : " + umur + " tahun");
        System.out.println("Berat : " + berat + " kg");
    }
}

BacaUmurAman.java

Unduh berkas
java
import java.util.InputMismatchException;
import java.util.Scanner;

public class BacaUmurAman {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int umur = -1;

        while (umur < 0) {
            System.out.print("Masukkan umur (bilangan bulat): ");
            try {
                umur = sc.nextInt();
            } catch (InputMismatchException e) {
                String token = sc.next(); // buang token yang tidak valid
                System.out.println("Token \"" + token + "\" bukan bilangan bulat.");
            }
        }
        System.out.println("Umur tercatat: " + umur);
    }
}

JebakanNewline.java

Unduh berkas
java
import java.util.Scanner;

public class JebakanNewline {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        int umur = sc.nextInt();      // berhenti tepat sebelum baris baru
        String nama = sc.nextLine();  // langsung mendapat string kosong

        System.out.println("Umur        : " + umur);
        System.out.println("Nama        : \"" + nama + "\"");
        System.out.println("Panjang nama: " + nama.length());

        String namaBaru = sc.nextLine(); // baris berikutnya akhirnya terbaca
        System.out.println("Nama kedua  : \"" + namaBaru + "\"");
    }
}