Keluarga java.io bekerja dengan model aliran: data mengalir sekali lewat dan hilang begitu sudah terbaca. NIO memperkenalkan model yang berbeda — data berdiam dalam wadah yang bisa kita bolak-balik: membaca, memutar posisi, mengisi ulang. Wadah itu bernama Buffer, dan ByteBuffer adalah anggotanya yang paling sering dipakai karena menjadi mata uang resmi seluruh channel NIO. Memahami buffer berarti memahami tiga penanda yang mengendalikan isinya: position, limit, dan capacity. Pada Chapter ini kita membedah ketiganya sampai ke siklus put-flip-get yang menjadi denyut setiap program NIO.
Tiga Penanda: Position, Limit, Capacity #
Sebuah ByteBuffer adalah array byte berbalut kendali. capacity adalah jumlah byte yang bisa ditampung — tetap sejak buffer dibuat. position menunjuk indeks byte berikutnya yang akan dibaca atau ditulis. limit adalah pagar batas: indeks pertama yang tidak boleh disentuh. Saat menulis, kita mengisi dari position dan penanda bergerak maju; saat membaca, kita mengonsumsi dari position hingga limit. Perpindahan antara mode menulis dan mode membaca itulah tugas flip(), rewind(), dan clear() — bukan sekadar ritual, melainkan cara buffer mengingat berapa data nyata yang tersimpan.
Bentuk wadah berkapasitas 8 byte berisi tiga nilai dapat digambarkan begini:
+----+----+----+----+----+----+----+----+
| 10 | 20 | 30 | | | | | | capacity = 8
+----+----+----+----+----+----+----+----+
↑ ↑
position = 0 limit = 8
setelah flip(): position = 0, limit = 3 (mode baca)Program berikut menjalankan siklus penuh itu sambil mencatat ketiga penanda di setiap tahap:
import java.nio.ByteBuffer;
public class AnatomiBuffer {
public static void main(String[] args) {
ByteBuffer penampung = ByteBuffer.allocate(8);
System.out.println("setelah allocate : pos=" + penampung.position()
+ " lim=" + penampung.limit()
+ " cap=" + penampung.capacity());
penampung.put((byte) 10);
penampung.put((byte) 20);
penampung.put((byte) 30);
System.out.println("setelah 3 put : pos=" + penampung.position()
+ " lim=" + penampung.limit());
penampung.flip();
System.out.println("setelah flip : pos=" + penampung.position()
+ " lim=" + penampung.limit());
while (penampung.hasRemaining()) {
System.out.println("terbaca : " + penampung.get());
}
System.out.println("setelah get semua: pos=" + penampung.position()
+ " lim=" + penampung.limit());
penampung.rewind();
System.out.println("setelah rewind : pos=" + penampung.position()
+ " lim=" + penampung.limit());
System.out.println("baca ulang : " + penampung.get());
penampung.clear();
System.out.println("setelah clear : pos=" + penampung.position()
+ " lim=" + penampung.limit());
}
}Output yang Dihasilkan:
setelah allocate : pos=0 lim=8 cap=8
setelah 3 put : pos=3 lim=8
setelah flip : pos=0 lim=3
terbaca : 10
terbaca : 20
terbaca : 30
setelah get semua: pos=3 lim=3
setelah rewind : pos=0 lim=3
baca ulang : 10
setelah clear : pos=0 lim=8Jejak penanda pada keluaran mengisahkan siklus yang utuh:
allocate(8): buffer lahir kosong denganpos=0 lim=8 cap=8— mode tulis, karenalimitmenempel dicapacity.- Tiga kali
put: setiap penulisan menaruh satu byte lalu menggeserpositionmaju; setelah tiga nilai,pos=3danlimittetap8. flip(): mode berpindah dari tulis ke baca dengan dua gerakan sekaligus —limitditarik kepositionlama (angka byte nyata: 3) danpositiondipindah ke 0. Inilah momen buffer “mengunci” panjang datanya.hasRemaining()+get(): perulangan membaca selamaposition < limit; tiga nilai keluar berurutan danposberhenti tepat dilim=3.rewind(): mengembalikanpositionke 0 tanpa menyentuhlimitsehingga data yang sama bisa dibaca ulang — terbuktiget()kembali menghasilkan10.clear(): mengembalikanlimitkecapacitydanpositionke 0 — buffer siap diisi ulang. Perhatikan bahwaclear()tidak menghapus byte lama; byte10 20 30masih tersimpan, hanya saja akan tertimpa seiring penulisan baru.
Tipe Data Majemuk dan Pembungkus Array #
ByteBuffer tidak terbatas pada byte tunggal. Method putInt, putLong, putDouble menulis nilai multi-byte dalam urutan big-endian secara default (byte tertinggi duluan — dapat diubah dengan order()), dan pasangannya getInt, getLong, getDouble membaca balik dengan asumsi urutan serta posisi yang sama. Dua factory lain melengkapi cara membuat buffer: wrap(byte[]) membungkus array yang sudah ada tanpa menyalin, dan allocateDirect() meminta memori di luar heap Java.
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
public class TipeBuffer {
public static void main(String[] args) {
ByteBuffer data = ByteBuffer.allocate(16);
data.putInt(1_000_000);
data.putLong(99L);
data.put((byte) 'A');
System.out.println("posisi setelah tulis : " + data.position());
data.flip();
System.out.println("int : " + data.getInt());
System.out.println("long : " + data.getLong());
System.out.println("byte : " + (char) data.get());
byte[] salinan = "Budi".getBytes(StandardCharsets.UTF_8);
ByteBuffer bungkus = ByteBuffer.wrap(salinan);
System.out.println("wrap : pos=" + bungkus.position()
+ " lim=" + bungkus.limit()
+ " cap=" + bungkus.capacity());
byte[] hasil = new byte[bungkus.remaining()];
bungkus.get(hasil);
System.out.println("pulih : " + new String(hasil, StandardCharsets.UTF_8));
System.out.println("heap : " + ByteBuffer.allocate(8).isDirect());
System.out.println("direct : " + ByteBuffer.allocateDirect(8).isDirect());
}
}Output yang Dihasilkan:
posisi setelah tulis : 13
int : 1000000
long : 99
byte : A
wrap : pos=0 lim=4 cap=4
pulih : Budi
heap : false
direct : trueBeberapa perilaku menonjol dari program tersebut:
putInt+putLong+put:positionmelompat 4, lalu 8, lalu 1 — total 13 byte, sesuai ukuran biner tiap tipe. Pembacaan dengangetInt/getLong/getdalam urutan yang sama memulihkan nilai-nilai aslinya; urutan penulisan dan pembacaan harus cermin persis.wrap(salinan): buffer pembungkus langsung siap dibaca —limitdiatur ke panjang array (4) — karenawrapmenganggap array sudah terisi data. Buffer ini berbagi memori dengan array aslinya: mengubah array mengubah isi buffer.remaining(): selisihlimit - position, dipakai untuk mengalokasikan array tujuan yang pas sebelumget(hasil)menyalin seluruh isi.isDirect(): buffer biasa tinggal di heap Java (nilaifalse) sehingga ia ikut digerakkan garbage collector; buffer direct (nilaitrue) dialokasikan di memori natif sistem operasi. Channel dapat memindahkan direct buffer ke disk tanpa salinan tambahan melalui heap — tepat untuk I/O besar dan berulang, dengan biaya alokasi yang lebih mahal.
Buffer sudah kita kuasai sebagai wadah; waktunya menghubungkan wadah itu ke berkas. Chapter berikutnya memperkenalkan FileChannel — saluran yang membaca dan menulis isi ByteBuffer langsung ke disk, lengkap dengan trik transferTo yang memindahkan data tanpa menyentuh ruang pengguna Java.
Referensi #
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
AnatomiBuffer.java
Unduh berkasimport java.nio.ByteBuffer;
public class AnatomiBuffer {
public static void main(String[] args) {
ByteBuffer penampung = ByteBuffer.allocate(8);
System.out.println("setelah allocate : pos=" + penampung.position()
+ " lim=" + penampung.limit()
+ " cap=" + penampung.capacity());
penampung.put((byte) 10);
penampung.put((byte) 20);
penampung.put((byte) 30);
System.out.println("setelah 3 put : pos=" + penampung.position()
+ " lim=" + penampung.limit());
penampung.flip();
System.out.println("setelah flip : pos=" + penampung.position()
+ " lim=" + penampung.limit());
while (penampung.hasRemaining()) {
System.out.println("terbaca : " + penampung.get());
}
System.out.println("setelah get semua: pos=" + penampung.position()
+ " lim=" + penampung.limit());
penampung.rewind();
System.out.println("setelah rewind : pos=" + penampung.position()
+ " lim=" + penampung.limit());
System.out.println("baca ulang : " + penampung.get());
penampung.clear();
System.out.println("setelah clear : pos=" + penampung.position()
+ " lim=" + penampung.limit());
}
}
TipeBuffer.java
Unduh berkasimport java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
public class TipeBuffer {
public static void main(String[] args) {
ByteBuffer data = ByteBuffer.allocate(16);
data.putInt(1_000_000);
data.putLong(99L);
data.put((byte) 'A');
System.out.println("posisi setelah tulis : " + data.position());
data.flip();
System.out.println("int : " + data.getInt());
System.out.println("long : " + data.getLong());
System.out.println("byte : " + (char) data.get());
byte[] salinan = "Budi".getBytes(StandardCharsets.UTF_8);
ByteBuffer bungkus = ByteBuffer.wrap(salinan);
System.out.println("wrap : pos=" + bungkus.position()
+ " lim=" + bungkus.limit()
+ " cap=" + bungkus.capacity());
byte[] hasil = new byte[bungkus.remaining()];
bungkus.get(hasil);
System.out.println("pulih : " + new String(hasil, StandardCharsets.UTF_8));
System.out.println("heap : " + ByteBuffer.allocate(8).isDirect());
System.out.println("direct : " + ByteBuffer.allocateDirect(8).isDirect());
}
}