Learn Java Io Modern Io Resource Boundaries Part 001 Kaufman Skill Map
title: Learn Java IO, Modern IO, Streams, Buffers, Resources, Serialization & Data Boundaries - Part 001 description: Skill map, scope, practice strategy, and production-grade learning path for mastering Java IO using Josh Kaufman's rapid skill acquisition framework. series: learn-java-io-modern-io-resource-boundaries seriesTitle: Learn Java IO, Modern IO, Streams, Buffers, Resources, Serialization & Data Boundaries order: 1 partTitle: Kaufman Skill Map tags:
- java
- io
- nio
- streams
- buffers
- resources
- serialization
- kaufman
- series date: 2026-06-30
Part 001 — Kaufman Skill Map for Java IO Mastery
1. Tujuan Part Ini
Part ini adalah peta kendali untuk seluruh seri. Kita belum masuk terlalu dalam ke API satu per satu. Kita akan menetapkan:
- apa sebenarnya yang dimaksud dengan “menguasai Java IO”;
- sub-skill apa saja yang harus dikuasai;
- bagaimana menerapkan framework The First 20 Hours dari Josh Kaufman;
- apa saja invariants yang harus selalu dicek saat menulis IO code;
- bagaimana membedakan engineer yang sekadar tahu API dari engineer yang mampu merancang boundary IO yang aman, cepat, dan bisa dipertanggungjawabkan.
Targetnya bukan hafal semua class di java.io dan java.nio. Targetnya adalah mampu menjawab pertanyaan seperti ini dengan percaya diri:
“Data dari sistem eksternal masuk melalui file/stream/socket/process/archive/resource. Bagaimana kita membaca, memvalidasi, mentransfer, menyimpan, memulihkan, menguji, dan men-debug boundary tersebut tanpa leak, corrupt, deadlock, silent truncation, encoding bug, atau durability illusion?”
Itulah inti seri ini.
2. Skill Akhir yang Ingin Dicapai
Kita akan mendefinisikan target performa secara eksplisit. Dalam konteks Kaufman, skill harus didefinisikan sebagai kemampuan konkret, bukan topik abstrak.
Setelah menyelesaikan seri ini, kamu seharusnya mampu:
- memilih primitive IO yang tepat:
Path,Files,InputStream,OutputStream,Reader,Writer,ByteBuffer,Channel,MappedByteBuffer,MemorySegment, atau callback-based boundary; - mendesain API boundary yang jelas soal ownership, close responsibility, replayability, encoding, framing, dan partial failure;
- membaca dan menulis data besar tanpa membebani heap secara tidak perlu;
- membedakan byte stream, character stream, record stream, message stream, dan object graph serialization;
- menulis file update yang aman terhadap crash menggunakan temporary file, flush/force, dan atomic move saat tersedia;
- memahami konsekuensi OS/filesystem terhadap Java code: page cache, file descriptor, symbolic link, permission, atomicity, visibility, durability, network filesystem;
- mengelola resource lifecycle dengan disiplin: ownership, close ordering, suppressed exception, leak prevention, cancellation;
- menggunakan NIO buffer dan channel secara benar:
position,limit,capacity,flip,compact, direct buffer, scatter/gather, transfer; - menangani serialization boundary dengan sadar risiko: versioning, compatibility, filtering, object graph hazards;
- menguji IO code dengan deterministic temporary resources, fake streams, fault injection, partial read/write, dan crash-simulation mindset;
- mendiagnosis performa IO: syscall, buffering, page cache, allocation, native memory, GC, throughput vs latency;
- membangun ingestion/transfer pipeline production-grade yang bisa di-retry, di-resume, diobservasi, dan dibatalkan dengan aman.
Kalimat paling pendeknya:
Menguasai Java IO berarti mampu mengendalikan perpindahan data melintasi boundary yang lambat, stateful, partial, dan gagal dengan cara yang benar.
3. Mengapa IO Sulit Padahal API-nya Terlihat Sederhana
Banyak API IO terlihat mudah:
String content = Files.readString(path);
Files.writeString(path, content);
Atau:
try (InputStream in = Files.newInputStream(path)) {
return in.readAllBytes();
}
Untuk file kecil, trusted, lokal, dan non-critical, ini valid. Tetapi production system sering tidak seperti itu.
IO menjadi sulit karena beberapa hal berikut.
| Dimensi | Kenyataan Production | Bug yang Sering Muncul |
|---|---|---|
| Size | Data bisa jauh lebih besar dari asumsi | OOM, GC pressure, stuck processing |
| Time | Source/sink bisa lambat atau berhenti | thread starvation, timeout, backpressure collapse |
| Partiality | Read/write bisa parsial | truncation, corrupted output, incomplete transfer |
| Encoding | Bytes belum tentu valid text | mojibake, replacement char, data loss |
| Boundary | Stream tidak selalu punya message boundary | salah parsing, over-read, under-read |
| Resource | File descriptor/socket/native memory terbatas | leak, “too many open files” |
| Filesystem | Atomicity dan durability bergantung provider/OS | lost update, crash corruption |
| Concurrency | File bisa berubah saat dibaca | race condition, TOCTOU, inconsistent snapshot |
| Trust | Input bisa berasal dari pihak eksternal | zip-slip, deserialization hazard, resource bomb |
| Diagnostics | IO lambat sering terlihat seperti CPU idle | salah tuning, salah bottleneck |
Engineer yang kuat di IO tidak hanya tahu “cara membaca file”. Ia tahu bahwa IO selalu membawa hidden contract.
4. Framework Kaufman untuk Seri Ini
Josh Kaufman mengusulkan cara belajar skill dengan cepat melalui prinsip seperti:
- deconstruct the skill;
- learn enough to self-correct;
- remove barriers to practice;
- practice deliberately for at least 20 hours.
Kita adaptasikan ke Java IO.
Kita tidak akan belajar dengan urutan “semua class A-Z”. Itu buruk untuk skill acquisition. Kita belajar berdasarkan risiko dan leverage.
5. Deconstruct the Skill: Java IO Bukan Satu Skill
Java IO adalah bundle dari banyak sub-skill. Jika semua dicampur, belajar menjadi kabur. Maka kita pecah.
5.1 Sub-Skill 1 — Byte Flow
Ini pondasi paling bawah.
Kamu harus paham:
- byte adalah unit fisik data IO;
- stream tidak selalu menyampaikan semua byte sekaligus;
- EOF bukan error normal, tetapi unexpected EOF bisa error;
- read/write bisa blocking;
- transfer besar harus chunked.
Contoh pertanyaan self-check:
Kalau
InputStream.read(byte[])mengembalikan 173 padahal buffer 8192, apakah itu error?
Jawaban: tidak. Itu normal. Caller harus loop sampai EOF atau sampai jumlah byte yang diperlukan terpenuhi.
5.2 Sub-Skill 2 — Text Boundary
Text bukan byte. Text adalah hasil decoding byte menggunakan charset.
Kamu harus paham:
- UTF-8, UTF-16, ISO-8859-1, dan default charset;
- malformed input;
- replacement policy vs strict decoding;
- newline format:
\n,\r\n,\r; - BOM;
- read-ahead behavior pada decoder.
Pertanyaan self-check:
Apakah
new FileReader(path.toFile())selalu aman untuk file UTF-8?
Jawaban: jangan asumsikan. Di Java modern default charset memang lebih konsisten daripada era lama, tetapi API boundary yang serius tetap harus menyebut charset secara eksplisit.
5.3 Sub-Skill 3 — Resource Lifecycle
IO resource biasanya memegang sesuatu di luar heap: file descriptor, socket descriptor, native buffer, memory mapping, process pipe, directory handle.
Kamu harus paham:
- siapa pemilik resource;
- siapa yang wajib menutup;
- kapan close harus terjadi;
- apa efek close terhadap wrapper/decorator;
- bagaimana suppressed exception bekerja;
- bagaimana resource leak muncul walaupun object Java akhirnya di-GC.
Pertanyaan self-check:
Jika method menerima
InputStream, apakah method itu boleh menutup stream tersebut?
Jawaban: tergantung contract. Default yang aman: jangan menutup resource yang tidak kamu buka, kecuali API menyatakan ownership berpindah.
5.4 Sub-Skill 4 — Filesystem Semantics
Filesystem bukan Map<String, byte[]> sederhana.
Kamu harus paham:
- path bisa relative, absolute, normalized, real path;
- symbolic link bisa mengubah target operasi;
- metadata bisa berubah saat operasi berjalan;
- permission model berbeda antar OS;
- atomic move bergantung filesystem/provider;
- visibility antar process tidak selalu instant;
- durability bukan sama dengan “write method sudah return”.
Pertanyaan self-check:
Setelah
Files.write(path, bytes)return, apakah data pasti selamat dari power loss?
Jawaban: tidak otomatis. Untuk durability, kamu perlu memahami flush, fsync/force, opsi SYNC/DSYNC, filesystem, storage, dan rename discipline.
5.5 Sub-Skill 5 — Buffering
Buffer adalah trade-off.
Kamu harus paham:
- buffer mengurangi syscall overhead;
- buffer menambah memory footprint;
- buffer bisa menunda visibility output;
- terlalu kecil buruk, terlalu besar belum tentu lebih baik;
- double-buffering bisa sia-sia;
- flush bukan durability.
Pertanyaan self-check:
Apakah semua
OutputStreamperlu dibungkusBufferedOutputStream?
Jawaban: tidak selalu. Beberapa stream sudah buffered atau target-nya bukan bottleneck. Pahami source/sink dan access pattern.
5.6 Sub-Skill 6 — NIO Buffer and Channel
NIO memperkenalkan model explicit buffer.
Kamu harus paham:
ByteBufferpunyaposition,limit,capacity;- mode write-to-buffer dan read-from-buffer harus dipisah dengan
flip; compactdipakai saat partial consumption;- channel bisa positional, scattering, gathering, selectable, asynchronous;
- direct buffer menggunakan native memory dan punya lifecycle berbeda dari heap array biasa.
Pertanyaan self-check:
Mengapa
buffer.flip()sering hilang di bug NIO pemula?
Jawaban: karena developer menganggap buffer seperti array. Padahal buffer punya state machine eksplisit.
5.7 Sub-Skill 7 — Boundary Framing
Stream hanya rangkaian data. Ia tidak selalu tahu “pesan” dimulai dan berakhir di mana.
Kamu harus paham:
- delimiter framing;
- length-prefix framing;
- fixed-width record;
- self-describing format;
- streaming parser;
- partial message;
- over-read dan under-read.
Pertanyaan self-check:
Jika socket menerima
read()1024 bytes, apakah itu berarti satu message lengkap?
Jawaban: tidak. Stream transport tidak menjamin message boundary kecuali protocol di atasnya mendefinisikan boundary.
5.8 Sub-Skill 8 — Serialization Boundary
Serialization bukan hanya “object jadi bytes”.
Kamu harus paham:
- object graph traversal;
- identity/reference handling;
- class descriptor;
- versioning;
serialVersionUID;- compatibility rules;
- trusted vs untrusted data;
- filtering;
- long-lived data migration.
Pertanyaan self-check:
Apakah aman menerima Java serialized object dari client eksternal?
Jawaban: secara umum jangan. Jika terpaksa, boundary harus sangat dibatasi, difilter, dan diperlakukan sebagai data tidak tepercaya.
5.9 Sub-Skill 9 — Process IO
External process punya stdin/stdout/stderr. Kalau salah ditangani, process bisa deadlock karena pipe penuh.
Kamu harus paham:
- pipe buffer finite;
- stdout dan stderr harus dikuras;
- process bisa menghasilkan output besar;
- redirect bisa lebih aman daripada capture in-memory;
- timeout dan cancellation perlu membunuh process tree jika perlu.
Pertanyaan self-check:
Mengapa
process.waitFor()bisa hang walau program child sebenarnya hanya menulis log?
Jawaban: karena child bisa blocked menulis stdout/stderr ketika parent tidak menguras pipe.
5.10 Sub-Skill 10 — Testing and Diagnostics
IO bug sering tidak muncul di happy path.
Kamu harus paham:
- temporary directory testing;
- fake stream yang return partial read;
- stream yang throw setelah N bytes;
- permission failure;
- corrupted/truncated file;
- large file simulation;
- checksum verification;
- benchmarking IO tanpa tertipu page cache.
Pertanyaan self-check:
Apakah test yang hanya memakai
ByteArrayInputStreamcukup untuk membuktikan file ingestion aman?
Jawaban: tidak. Itu hanya membuktikan parsing happy path in-memory. Belum membuktikan filesystem, partial transfer, permission, cleanup, durability, atau cancellation.
6. Peta Seri 32 Part
Seri ini dibangun seperti progression skill, bukan katalog class.
Part 001 sampai 012 membangun correctness. Part 013 sampai 023 membangun modern IO machinery. Part 024 sampai 032 membangun production boundary engineering.
7. The 20-Hour Practice Plan
Kaufman tidak berarti kamu akan menjadi world-class dalam 20 jam. Maksudnya: kamu bisa mencapai level kompetensi praktis jika skill dipecah, hambatan dikurangi, dan latihan dilakukan sengaja.
Untuk Java IO, 20 jam pertama harus diarahkan ke usable production intuition.
7.1 Hour 0–2 — Build the IO Map
Tujuan:
- memahami byte vs char vs record;
- memahami resource ownership;
- memahami partial read/write;
- memahami kapan API simple cukup dan kapan berbahaya.
Latihan:
- baca file kecil dengan
Files.readString; - baca file besar secara streaming;
- bandingkan memory behavior secara kasar;
- tulis catatan: kapan memakai high-level API, kapan tidak.
Output:
Decision note: File size, trust level, encoding, retry needs, durability needs, and ownership decide the IO primitive.
7.2 Hour 3–5 — Classic Stream Correctness
Tujuan:
- menguasai loop
readyang benar; - memahami EOF;
- memahami wrapper stream;
- memahami close propagation.
Latihan:
- implementasi copy stream manual;
- injeksi stream yang hanya return 1–3 byte per read;
- pastikan code tetap benar;
- uji stream yang throw di tengah transfer.
Output:
static long copy(InputStream in, OutputStream out) throws IOException {
byte[] buffer = new byte[8192];
long total = 0;
int n;
while ((n = in.read(buffer)) != -1) {
out.write(buffer, 0, n);
total += n;
}
return total;
}
Yang dipelajari bukan syntax-nya, tetapi invariant-nya:
- jangan asumsikan buffer selalu penuh;
- jangan abaikan return value;
- jangan load semua data tanpa alasan;
- jangan close resource yang bukan milikmu tanpa contract.
7.3 Hour 6–8 — Text and Binary Boundary
Tujuan:
- selalu menyebut charset;
- memahami malformed input;
- memahami delimiter vs length-prefix;
- memahami endian dan primitive binary layout.
Latihan:
- baca file UTF-8 strict;
- baca file dengan byte invalid;
- buat parser length-prefixed frame sederhana;
- uji truncated frame.
Output:
Text is bytes plus charset plus error policy. Records are bytes plus framing contract.
7.4 Hour 9–11 — Modern Files API
Tujuan:
- menguasai
Path,Files, danFileSystem; - memahami symbolic link;
- memahami temp file;
- memahami atomic create/move/delete.
Latihan:
- tulis file ke temp path;
- validasi content;
- pindahkan ke final path dengan atomic move jika tersedia;
- handle fallback jika atomic move tidak tersedia.
Output:
Never update important files by blindly overwriting the final target first.
7.5 Hour 12–14 — NIO Buffer and Channel
Tujuan:
- memahami state machine
ByteBuffer; - membaca file memakai
FileChannel; - menguasai
flip,clear,compact; - memahami positional read/write.
Latihan:
- implementasi small file scanner dengan
FileChannel; - sengaja hilangkan
flip, lihat bug-nya; - buat diagram state buffer;
- uji partial record across buffer boundary.
Output:
ByteBuffer is not an array. It is a mutable cursor object with explicit mode transitions.
7.6 Hour 15–17 — Transfer, Compression, Process Boundary
Tujuan:
- memahami transfer besar;
- memahami compression stream chaining;
- memahami archive traversal risk;
- memahami process stdout/stderr deadlock.
Latihan:
- copy file besar chunked;
- gzip stream tanpa load full file;
- extract zip dengan path validation;
- jalankan process yang menulis stdout/stderr besar dan drain keduanya.
Output:
Every boundary has a pressure point: memory, descriptor, pipe, disk, CPU, or trust.
7.7 Hour 18–20 — Capstone Mini Project
Bangun mini ingestion pipeline:
- menerima file dari staging directory;
- membaca metadata;
- validasi size dan extension;
- streaming checksum;
- parse record boundary;
- tulis hasil ke output temp file;
- atomic move ke final;
- simpan manifest;
- cleanup resource;
- uji partial failure.
Output minimal:
Input file -> staging -> validated stream -> processed output temp -> forced write -> atomic publish -> manifest
Diagram:
8. Lima Pertanyaan Wajib untuk Semua IO Boundary
Setiap kali kamu melihat atau menulis IO code, tanyakan lima hal ini.
8.1 Siapa Pemilik Resource?
Contoh:
void process(InputStream input) throws IOException {
try (input) { // dangerous unless ownership transfer is documented
// process
}
}
Masalahnya bukan syntax. Masalahnya contract.
Jika caller masih membutuhkan input, method ini merusak caller. Jika method memang dimaksudkan mengambil ownership, documentasikan dengan jelas.
Contract yang lebih jelas:
/**
* Consumes and closes the given input stream.
*/
void consumeAndClose(InputStream input) throws IOException {
try (input) {
// process
}
}
Atau:
/**
* Reads from the given input stream but does not close it.
*/
void readWithoutClosing(InputStream input) throws IOException {
// process only
}
8.2 Apa Unit Data-nya?
Unit data bisa berbeda-beda:
| Unit | Contoh | API Cocok |
|---|---|---|
| Byte | image, compressed payload | InputStream, ByteBuffer, Channel |
| Character | text config | Reader, Files.readString |
| Line | CSV-ish line file | BufferedReader, Files.lines |
| Record | length-prefixed binary message | custom parser over stream/channel |
| Object graph | Java serialization | ObjectInputStream |
| File tree | archive/extract/copy directory | Files.walk, visitor API |
Bug sering muncul ketika unit mental tidak cocok dengan API.
Contoh: memakai line-based reader untuk format yang sebenarnya binary. Atau menganggap read(byte[]) menghasilkan satu record utuh.
8.3 Apakah Operasi Bisa Partial?
Jawaban default: ya.
- read bisa return kurang dari buffer length;
- write ke channel bisa menulis sebagian;
- transfer bisa berhenti di tengah;
- file copy bisa menghasilkan target parsial;
- process output bisa terpotong karena timeout;
- archive extraction bisa berhenti setelah beberapa entry.
IO yang benar punya recovery atau cleanup strategy.
8.4 Bagaimana EOF, Error, Timeout, dan Cancellation Disinyalkan?
Boundary harus membedakan:
| Signal | Makna |
|---|---|
| EOF normal | stream selesai sesuai protocol |
| Unexpected EOF | data terpotong |
| IOException | source/sink gagal |
| Timeout | progress tidak cukup cepat |
| Cancellation | caller membatalkan |
| Closed resource | lifecycle sudah berakhir |
Semua ini tidak boleh diperlakukan sama.
8.5 Apa Guarantee Setelah Failure?
Setelah failure, state harus jelas.
Pertanyaan:
- apakah output file final mungkin parsial?
- apakah temp file tertinggal?
- apakah input sudah dikonsumsi sebagian dan tidak bisa diulang?
- apakah checksum masih valid?
- apakah operation idempotent untuk retry?
- apakah caller tahu berapa byte/record yang berhasil?
Production code harus punya jawaban.
9. IO Invariants untuk Engineer Senior
Invariant adalah aturan yang harus selalu benar. Ini lebih penting dari hafalan API.
9.1 Resource Invariants
- Resource yang dibuka harus ditutup.
- Resource yang tidak kamu buka tidak boleh kamu tutup kecuali contract mengatakan ownership berpindah.
- Wrapper stream biasanya menutup underlying stream saat wrapper ditutup.
- Close bisa throw exception.
- Exception saat close tidak boleh menghapus failure utama tanpa disadari.
- Native memory dan file descriptor harus dipikirkan seperti resource terbatas.
9.2 Stream Invariants
- Stream adalah data sequence, bukan message sequence.
readtidak wajib memenuhi buffer.- EOF bukan byte; EOF adalah signal.
available()bukan ukuran total stream.skip(n)tidak selalu melewati tepatnbyte.flush()bukan jaminan data tahan crash.
9.3 Text Invariants
- Bytes harus didecode memakai charset.
- Charset harus eksplisit di boundary serius.
- Decoder bisa membaca byte lebih banyak daripada karakter yang diminta.
- Newline bukan universal.
- File text bisa memiliki BOM.
- Invalid byte sequence harus punya policy: fail, replace, atau ignore.
9.4 File Invariants
- Path string bukan file identity yang stabil.
- File bisa berubah antara check dan use.
- Symbolic link bisa mengarahkan operasi ke target lain.
- Atomicity bergantung operation, filesystem, dan provider.
- Visibility bukan durability.
- Delete/move behavior berbeda antar OS ketika file masih terbuka.
9.5 Buffer Invariants
capacityadalah ukuran storage.positionadalah cursor.limitadalah batas operasi saat ini.flipmengubah buffer dari writing mode ke reading mode.cleartidak menghapus data, hanya reset cursor.- Direct buffer bukan heap array dan bukan gratis.
9.6 Transfer Invariants
- Transfer bisa partial.
- Large transfer harus bounded memory.
- Destination parsial harus dibersihkan atau diberi status eksplisit.
- Retry butuh idempotency atau resume token.
- Checksum harus dihitung terhadap bytes aktual yang diterima/ditulis.
- Compression/encryption/framing mengubah boundary dan error model.
10. Decision Matrix: Primitive Mana yang Dipilih?
| Kebutuhan | Primitive Awal | Catatan |
|---|---|---|
| Baca file text kecil | Files.readString(path, charset) | jelas, sederhana, tidak untuk file besar |
| Tulis file text kecil | Files.writeString(path, text, charset) | hati-hati overwrite dan durability |
| Baca file besar | BufferedInputStream atau Files.newInputStream | proses chunked |
| Baca line-by-line | BufferedReader / Files.newBufferedReader | explicit charset |
| Copy stream umum | InputStream.transferTo atau manual copy | perhatikan ownership dan close |
| Random access file | FileChannel | positional read/write |
| Binary protocol | ByteBuffer + framing parser | define endian dan frame length |
| Non-blocking network | SocketChannel + Selector | readiness is not completion |
| Large file transfer | FileChannel.transferTo/transferFrom | perhatikan limitations dan fallback |
| Huge file random scan | memory mapping | perhatikan unmap/lifecycle/page fault |
| File tree traversal | Files.walkFileTree | cleanup stream dan symlink policy |
| Classpath config | ClassLoader.getResourceAsStream | bukan File biasa saat packaged dalam JAR |
| External process | ProcessBuilder + drain stdout/stderr | hindari pipe deadlock |
| Object graph persistence | hindari native serialization untuk long-term external boundary | gunakan hanya untuk controlled internal cases |
Matrix ini bukan dogma. Ini starting point.
11. Anti-Goals Seri Ini
Agar belajar efisien, kita sengaja tidak mengulang materi yang sudah ada di seri lain.
11.1 Tidak Mengulang JSON/XML Mapping
Jackson, MapStruct, JAXB, Bean Validation, dan XML processing tidak akan diulang. Seri ini hanya menyentuh data boundary di level stream, encoding, framing, dan serialization lifecycle.
11.2 Tidak Mengulang Concurrency Umum
Thread, lock, executor, reactive programming, dan memory model tidak dibahas ulang. Kita hanya membahas concurrency ketika berdampak langsung pada IO: blocking, cancellation, selector, async channel, pipe draining, resource close race.
11.3 Tidak Mengulang Security Umum
Cryptography, TLS, secret handling, authn/authz, dan platform hardening tidak diulang. Kita hanya membahas IO-specific hazard seperti zip-slip, deserialization filter, path traversal, trusted/untrusted stream, dan resource bomb.
11.4 Tidak Mengulang Observability Umum
Logging, metrics, tracing, dan SLO tidak diulang. Kita hanya memakai diagnostics untuk IO: bytes transferred, duration, throughput, retry, partial failure, file descriptor pressure, native memory.
11.5 Tidak Mengulang Persistence/JDBC
Database IO tidak menjadi fokus. Namun mental model boundary, durability, partial failure, dan batching akan relevan untuk database engineer juga.
12. Cara Membaca Seri Ini
Jangan membaca seri ini seperti dokumentasi API. Baca seperti training lapangan.
Untuk setiap part:
- baca mental model;
- catat invariants;
- jalankan snippet kecil;
- buat test yang mematahkan asumsi;
- ubah snippet menjadi utility kecil;
- tulis failure note: “apa yang bisa salah?”;
- ulangi dengan data lebih besar atau kondisi lebih buruk.
Template catatan per part:
## Part N Practice Notes
### New Concept
### Contract I Must Remember
### Failure Mode
### Small Experiment
### Production Rule
### Question I Still Have
13. Practice Harness yang Disarankan
Gunakan project kecil, bukan enterprise skeleton penuh.
Struktur:
java-io-lab/
pom.xml or build.gradle
src/main/java/lab/io/
src/test/java/lab/io/
src/test/resources/
data/
input/
output/
tmp/
Dependencies minimal:
- JUnit 5;
- AssertJ atau assertion bawaan;
- Jimfs opsional untuk fake filesystem pada bagian testing;
- JMH opsional untuk performance part.
Hindari framework besar di awal. Tujuan seri ini adalah menguasai IO contract, bukan membuat aplikasi Spring.
14. Lab Pertama: Stream Copy yang Bisa Dipercaya
Walaupun detail classic stream akan dibahas di Part 003, lab awal ini berguna untuk menguji mindset.
14.1 Implementasi Minimal
package lab.io;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public final class StreamCopies {
private StreamCopies() {
}
public static long copy(InputStream input, OutputStream output) throws IOException {
byte[] buffer = new byte[8192];
long total = 0L;
while (true) {
int read = input.read(buffer);
if (read == -1) {
return total;
}
output.write(buffer, 0, read);
total += read;
}
}
}
14.2 Pertanyaan yang Harus Kamu Jawab
- Siapa yang menutup
inputdanoutput? - Apakah method ini safe untuk stream sangat besar?
- Apa yang terjadi jika
output.writegagal di tengah? - Apakah
outputperlu di-flush? - Apakah
totalbisa overflow? - Apakah method ini tahu data sudah durable?
- Apakah method ini bisa dibatalkan?
- Apakah method ini cocok untuk network stream?
Jawaban terbaik bukan selalu “ya” atau “tidak”. Jawaban terbaik adalah contract yang jelas.
Contoh contract:
/**
* Copies all bytes from input to output without closing either stream.
*
* This method is streaming and does not retain the full payload in memory.
* It returns after EOF from input or throws IOException if either stream fails.
* It does not guarantee durability of the output sink.
*/
public static long copy(InputStream input, OutputStream output) throws IOException
Ini gaya berpikir yang akan kita bawa sepanjang seri.
15. IO Design Review Checklist
Gunakan checklist ini setiap kali review PR yang menyentuh IO.
15.1 Boundary Checklist
- Apakah input trusted atau untrusted?
- Apakah size maksimum dibatasi?
- Apakah charset eksplisit?
- Apakah framing jelas?
- Apakah EOF normal dibedakan dari unexpected EOF?
- Apakah partial read/write ditangani?
- Apakah resource ownership didokumentasikan?
- Apakah close terjadi di semua path?
- Apakah output parsial bisa tertinggal?
- Apakah retry aman?
- Apakah durability benar-benar dibutuhkan?
- Apakah path traversal/symlink policy jelas?
- Apakah test mencakup failure path?
15.2 Performance Checklist
- Apakah data besar diproses streaming?
- Apakah buffer size masuk akal?
- Apakah ada
readAllBytes/readStringpada boundary tidak terbatas? - Apakah ada double buffering yang tidak perlu?
- Apakah native memory/direct buffer terkontrol?
- Apakah process stdout/stderr dikuras?
- Apakah measurement mempertimbangkan page cache?
15.3 Correctness Checklist
- Apakah operasi file publish atomic?
- Apakah temp file dibersihkan?
- Apakah metadata/checksum dihitung setelah bytes final tersedia?
- Apakah file bisa berubah saat diproses?
- Apakah symbolic link harus diikuti atau ditolak?
- Apakah permission error diperlakukan sebagai expected operational failure?
- Apakah exception message cukup untuk investigasi?
16. Mental Model: IO sebagai Boundary State Machine
IO operation jarang benar-benar satu langkah. Hampir selalu state machine.
Contoh file ingestion:
Pertanyaan penting:
- state mana yang persistent?
- state mana yang bisa diulang?
- state mana yang meninggalkan artifact?
- state mana yang butuh cleanup?
- state mana yang aman untuk retry?
IO engineering yang matang biasanya terlihat seperti lifecycle/state design, bukan sekadar utility function.
17. Common Misleading Simplicities
17.1 “File Itu Selalu Ada atau Tidak Ada”
Tidak sesederhana itu.
File bisa:
- ada saat dicek, hilang saat dibuka;
- permission berubah;
- path adalah symlink;
- file sedang ditulis proses lain;
- file berada di network filesystem;
- metadata cache stale;
- path terlalu panjang pada platform tertentu;
- nama mengandung karakter yang valid di OS tertentu tapi tidak di OS lain.
17.2 “Stream Itu Seperti Array”
Stream bukan array.
Array:
- size diketahui;
- random access;
- berada di memory;
- bisa dibaca ulang;
- tidak blocking.
Stream:
- size bisa tidak diketahui;
- sequential;
- source bisa lambat;
- bisa hanya sekali baca;
- bisa blocking;
- bisa gagal di tengah.
17.3 “Flush Berarti Aman”
Flush biasanya berarti data didorong dari buffer Java/wrapper ke layer bawah. Itu belum tentu berarti data sudah persistent ke physical storage.
Durability membutuhkan pembahasan terpisah di Part 012.
17.4 “NIO Selalu Lebih Cepat”
NIO adalah model, bukan magic. NIO memberi kontrol: buffer eksplisit, channel, selector, memory mapping. Untuk banyak kasus sederhana, classic stream yang dibuffer dengan baik sudah cukup dan lebih mudah benar.
17.5 “Serialization Itu Format Data”
Java native serialization lebih tepat dipikirkan sebagai object graph protocol dengan coupling kuat ke class Java. Untuk external long-term data boundary, ini sering bukan pilihan ideal.
18. Top 1% IO Engineer: Apa yang Dilihat Berbeda?
Engineer biasa melihat:
Files.copy(source, target);
Engineer kuat bertanya:
- Apakah
sourcetrusted? - Apakah
targetboleh overwrite? - Apakah copy harus atomic?
- Apakah target parsial boleh terlihat?
- Apakah source dan target di filesystem yang sama?
- Apakah symlink diikuti?
- Apakah permission dipertahankan?
- Apakah metadata perlu disalin?
- Apakah checksum perlu diverifikasi?
- Apa yang terjadi jika disk penuh di tengah?
- Apa yang terjadi jika JVM mati setelah 80% copy?
- Apakah perlu fsync directory setelah rename?
- Apakah operasi ini retryable?
- Apakah path ini berasal dari user input?
- Apakah ada size limit?
Itulah perbedaan utamanya: bukan API berbeda, melainkan model risiko berbeda.
19. Vocabulary yang Harus Kamu Kuasai
| Istilah | Arti Praktis |
|---|---|
| Source | Tempat data dibaca |
| Sink | Tempat data ditulis |
| Stream | Sequence data, biasanya sequential |
| Buffer | Area sementara untuk mengurangi overhead dan mengatur flow |
| Channel | Koneksi IO NIO yang bekerja dengan buffer |
| Descriptor | Handle OS untuk file/socket/pipe |
| EOF | Signal bahwa input selesai |
| Framing | Aturan pembagian stream menjadi message/record |
| Charset | Mapping bytes ke characters dan sebaliknya |
| Decoder | Komponen yang menerjemahkan bytes ke chars |
| Atomicity | Operasi terlihat terjadi seluruhnya atau tidak sama sekali |
| Durability | Data bertahan setelah crash/power loss |
| Visibility | Perubahan terlihat oleh reader lain |
| Idempotency | Aman diulang tanpa efek ganda yang salah |
| Replayability | Input bisa dibaca ulang |
| Seekability | Bisa random access ke posisi tertentu |
| Backpressure | Mekanisme mencegah producer mengalahkan consumer |
| Partial failure | Failure setelah sebagian efek terjadi |
| TOCTOU | Time-of-check to time-of-use race |
| Mmap | File region dipetakan ke memory address space |
| Direct buffer | Buffer off-heap untuk potensi native IO lebih efisien |
20. Exit Criteria Part 001
Kamu dianggap selesai dengan Part 001 jika bisa menjawab tanpa melihat catatan:
- Mengapa IO bukan sekadar baca/tulis file?
- Apa lima pertanyaan wajib untuk semua IO boundary?
- Apa bedanya byte, char, record, dan object graph?
- Mengapa resource ownership harus menjadi bagian dari API contract?
- Mengapa partial read/write adalah asumsi default?
- Mengapa flush bukan durability?
- Mengapa
ByteBufferbukan array biasa? - Mengapa
Files.readAllBytestidak cocok untuk boundary tidak terbatas? - Apa output dari 20-hour practice plan?
- Apa risiko terbesar jika IO code tidak punya failure model?
21. Rangkuman
Java IO mastery adalah skill boundary engineering. Fokus utamanya bukan menghafal class, tetapi mengendalikan aliran data yang:
- bisa besar;
- bisa lambat;
- bisa parsial;
- bisa gagal di tengah;
- bisa berasal dari sumber tidak tepercaya;
- bisa bergantung pada filesystem/OS;
- bisa memegang resource di luar heap;
- bisa meninggalkan state parsial setelah failure.
Framework Kaufman membantu kita belajar cepat dengan cara yang tepat: pecah skill, pelajari cukup untuk self-correct, hilangkan hambatan latihan, lalu praktik sengaja.
Part berikutnya masuk ke fondasi paling penting: IO Mental Model: Bytes, Characters, Records, and Boundaries.
22. Referensi
- Oracle Java SE 25 API —
java.iopackage summary: https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/io/package-summary.html - Oracle Java SE 25 API —
java.niopackage summary: https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/nio/package-summary.html - Oracle Java SE 25 API —
java.nio.filepackage summary: https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/nio/file/package-summary.html - Oracle Java SE 25 API —
java.nio.channelspackage summary: https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/nio/channels/package-summary.html - Josh Kaufman — The First 20 Hours: https://joshkaufman.net/books/the-first-20-hours/
You just completed lesson 01 in start here. Use the series map if you want to review the broader track, or continue directly into the next lesson while the context is still warm.
Keep the momentum while the lesson is still fresh. Move backward for review or continue forward into the next concept.