Langsung ke konten
KamusNgoding
Menengah Java 3 menit baca

File I/O di Java: Membaca dan Menulis File dengan java.nio

#java #file #bufferedreader #bufferedwriter #files #path #nio #scanner #try-with-resources #csv

Java menawarkan dua generasi API untuk file I/O: java.io (BufferedReader/Writer, Scanner — API klasik) dan java.nio.file (Files, Path — modern, Java 7+). Keduanya bekerja dengan try-with-resources dari artikel exception handling untuk memastikan resource selalu ditutup dengan benar. Pemahaman keduanya penting karena masih banyak kode existing yang menggunakan java.io.

Files dan Path — API Modern (java.nio)

import java.nio.file.*;
import java.io.IOException;
import java.util.List;

public class FilesModern {
    public static void main(String[] args) throws IOException {
        Path path = Path.of("catatan.txt"); // Java 11+

        // Tulis semua teks (buat baru atau timpa)
        Files.writeString(path, "Halo, dunia!\nBaris kedua.");

        // Tulis list baris
        List<String> daftar = List.of("Apel", "Mangga", "Jeruk");
        Files.write(Path.of("buah.txt"), daftar);

        // Baca semua teks
        String isi = Files.readString(path); // Java 11+
        System.out.println(isi);

        // Baca sebagai list baris
        List<String> baris = Files.readAllLines(Path.of("buah.txt"));
        baris.forEach(System.out::println);
        // Output:
        // Apel
        // Mangga
        // Jeruk

        // Cek keberadaan, salin, hapus
        System.out.println(Files.exists(path));      // true
        Files.copy(path, Path.of("backup.txt"), StandardCopyOption.REPLACE_EXISTING);
        Files.delete(Path.of("backup.txt"));
    }
}

BufferedReader dan BufferedWriter — API Klasik

import java.io.*;
import java.nio.charset.StandardCharsets;

public class BufferedRW {

    // Tulis file dengan BufferedWriter
    static void tulisFile(String namaFile) throws IOException {
        // try-with-resources: writer ditutup otomatis
        try (BufferedWriter writer = new BufferedWriter(
                new FileWriter(namaFile, StandardCharsets.UTF_8))) {

            writer.write("=== Laporan Penjualan ===");
            writer.newLine(); // Newline yang sesuai OS
            writer.write("Laptop    Rp 15.000.000");
            writer.newLine();
            writer.write("Mouse     Rp 250.000");
            writer.newLine();
        }
        System.out.println("File berhasil ditulis: " + namaFile);
    }

    // Baca file dengan BufferedReader
    static void bacaFile(String namaFile) throws IOException {
        try (BufferedReader reader = new BufferedReader(
                new FileReader(namaFile, StandardCharsets.UTF_8))) {

            String baris;
            int nomor = 1;
            while ((baris = reader.readLine()) != null) {
                System.out.printf("%3d: %s%n", nomor++, baris);
            }
        }
    }

    // Append ke file yang sudah ada
    static void tambahLog(String namaFile, String pesan) throws IOException {
        // FileWriter dengan append=true
        try (BufferedWriter writer = new BufferedWriter(
                new FileWriter(namaFile, true))) {
            writer.write(pesan);
            writer.newLine();
        }
    }

    public static void main(String[] args) throws IOException {
        tulisFile("laporan.txt");
        bacaFile("laporan.txt");
        tambahLog("app.log", "[INFO] Aplikasi berjalan");
        tambahLog("app.log", "[INFO] Proses selesai");
    }
}

Scanner — Membaca Tipe Data Langsung

Scanner berguna saat file berisi data terstruktur yang perlu diparse per token:

import java.util.Scanner;
import java.io.*;

public class ScannerFile {
    public static void main(String[] args) throws IOException {
        // Buat file sample
        Files.writeString(Path.of("data.txt"),
            "Ali 25 85.5\nBudi 22 92.0\nSiti 24 78.3");

        // Scanner baca per token dengan tipe yang tepat
        try (Scanner sc = new Scanner(new File("data.txt"))) {
            while (sc.hasNextLine()) {
                String nama = sc.next();      // baca String
                int umur = sc.nextInt();      // baca int
                double nilai = sc.nextDouble(); // baca double

                System.out.printf("%-10s umur %d, nilai %.1f%n", nama, umur, nilai);
            }
        }
        // Output:
        // Ali        umur 25, nilai 85.5
        // Budi       umur 22, nilai 92.0
        // Siti       umur 24, nilai 78.3
    }
}

Praktik: Aplikasi To-Do List dengan File

import java.io.*;
import java.nio.file.*;
import java.util.*;
import java.util.stream.Collectors;

class TodoItem {
    String teks;
    boolean selesai;

    TodoItem(String teks, boolean selesai) {
        this.teks = teks;
        this.selesai = selesai;
    }

    // Format untuk disimpan ke file: "0|Belajar Java"
    String keString() {
        return (selesai ? "1" : "0") + "|" + teks;
    }

    static TodoItem dariString(String baris) {
        String[] bagian = baris.split("\\|", 2);
        if (bagian.length < 2) return null;
        return new TodoItem(bagian[1], "1".equals(bagian[0]));
    }
}

class TodoApp {
    private final Path file;
    private final List<TodoItem> todos = new ArrayList<>();

    TodoApp(String namaFile) throws IOException {
        this.file = Path.of(namaFile);
        muat();
    }

    private void muat() throws IOException {
        if (!Files.exists(file)) return;

        List<String> baris = Files.readAllLines(file);
        for (String b : baris) {
            if (b.isBlank()) continue;
            TodoItem item = TodoItem.dariString(b);
            if (item != null) todos.add(item);
        }
        System.out.println("Memuat " + todos.size() + " tugas.");
    }

    private void simpan() throws IOException {
        List<String> baris = todos.stream()
            .map(TodoItem::keString)
            .collect(Collectors.toList());
        Files.write(file, baris);
    }

    void tambah(String teks) throws IOException {
        todos.add(new TodoItem(teks, false));
        simpan();
        System.out.println("✓ Ditambahkan: " + teks);
    }

    void selesaikan(int nomor) throws IOException {
        if (nomor < 1 || nomor > todos.size()) {
            System.out.println("Nomor tidak valid!");
            return;
        }
        todos.get(nomor - 1).selesai = true;
        simpan();
        System.out.println("✓ Selesai: " + todos.get(nomor - 1).teks);
    }

    void tampilkan() {
        System.out.println("\n=== Daftar Tugas ===");
        for (int i = 0; i < todos.size(); i++) {
            TodoItem t = todos.get(i);
            System.out.printf("[%s] %d. %s%n",
                t.selesai ? "x" : " ", i + 1, t.teks);
        }
        long selesai = todos.stream().filter(t -> t.selesai).count();
        System.out.printf("Progress: %d/%d selesai%n%n", selesai, todos.size());
    }
}

public class Main {
    public static void main(String[] args) throws IOException {
        TodoApp app = new TodoApp("todo.txt");

        app.tambah("Belajar Java File I/O");
        app.tambah("Membuat aplikasi CRUD");
        app.tambah("Review kode dengan teman");
        app.selesaikan(1);
        app.tampilkan();
        // Output:
        // === Daftar Tugas ===
        // [x] 1. Belajar Java File I/O
        // [ ] 2. Membuat aplikasi CRUD
        // [ ] 3. Review kode dengan teman
        // Progress: 1/3 selesai
    }
}

Pertanyaan yang Sering Diajukan

Kapan menggunakan java.nio.file.Files vs BufferedReader?

Gunakan Files.readString() / Files.readAllLines() untuk file kecil-sedang karena lebih ringkas dan modern. Gunakan BufferedReader.readLine() untuk file besar karena membaca satu baris sekaligus tanpa memuat seluruh isi ke memori — kritis untuk file log besar atau CSV jutaan baris. Untuk menulis, Files.write() / Files.writeString() sudah menangani buffering secara internal.

Mengapa StandardCharsets.UTF_8 perlu disebutkan?

Tanpa charset eksplisit, FileReader/FileWriter menggunakan charset default sistem — bisa berbeda antar komputer (Windows sering menggunakan CP1252, Linux menggunakan UTF-8). Ini menyebabkan karakter non-ASCII (termasuk karakter Indonesia seperti é, ñ, atau karakter Jawa ꦲ) rusak saat file dibuka di sistem lain. Selalu gunakan StandardCharsets.UTF_8 untuk portabilitas.

Apa keunggulan BufferedReader atas FileReader langsung?

FileReader membaca karakter satu per satu langsung dari disk — sangat lambat. BufferedReader membaca blok besar sekaligus ke buffer internal, lalu melayani permintaan dari buffer — jauh lebih efisien. Untuk file kecil perbedaannya tidak terasa, tapi untuk file besar perbedaan performa bisa 10–100x lipat. Selalu gunakan BufferedReader untuk membaca file teks.

Bagaimana cara menangani IOException dengan benar?

Pilihan tergantung konteks: (1) propagasi dengan throws IOException di method signature — biarkan pemanggil yang handle, cocok untuk library/service layer; (2) tangkap dengan try/catch dan handle lokal — cocok untuk aplikasi yang perlu fallback atau pesan error ke pengguna. Hindari menelan exception (catch (IOException e) {}) karena menyembunyikan masalah yang nyata.

Kesimpulan

Operasijava.nio (Modern)java.io (Klasik)
Baca semuaFiles.readString(path)BufferedReader.readLine() loop
Baca barisFiles.readAllLines(path)BufferedReader.readLine()
Tulis baruFiles.writeString(path, isi)BufferedWriter.write()
AppendFiles.write(path, data, APPEND)new FileWriter(path, true)
Cek adaFiles.exists(path)new File(path).exists()
SalinFiles.copy(src, dst, ...)Manual loop baca-tulis

Artikel sebelumnya: Exception Handling di Java — checked exception dan try-with-resources.

Selamat! Kamu sudah menguasai fondasi Java dari variabel hingga file I/O. Langkah selanjutnya: pelajari Koleksi Data di Java untuk memperdalam pengelolaan data, atau mulai eksplorasi framework Spring Boot untuk pengembangan web dengan Java.

Artikel Terkait