Di Chapter sebelumnya kita melihat bahwa java.io.File membingkai path sekadar sebagai string dengan sedikit operasi. Kebutuhan nyata jauh lebih kaya: menggabungkan folder dengan nama berkas, membersihkan segmen .., menghitung jarak relatif antara dua path, dan membedakan path mutlak dari path relatif. API NIO.2 yang hadir di Java 7 menjawab kebutuhan itu melalui interface java.nio.file.Path — tipe immutable yang menjadi bahasa bersama seluruh I/O modern Java. Pada Chapter ini kita belajar membangun, mengurai, dan memanipulasi Path sebelum menyentuh isi berkasnya.
Membuat dan Mengurai Objek Path #
Objek Path dibuat lewat factory Path.of(...), bukan new. Method ini menerima satu string utuh atau rangkaian segmen yang digabungkan otomatis dengan pemisah milik platform; di Linux pemisahnya /, dan kita tidak pernah menuliskannya secara manual sehingga kode tetap portabel. Berbeda dari File, Path bersifat immutable: setiap operasi mengembalikan objek Path baru dan objek lama tidak berubah. Seperti File, membuat Path tidak menyentuh disk sama sekali — string hanya diurai dan divalidasi bentuknya.
Program berikut membangun path tiga segmen lalu mengurai balik setiap komponennya, ditutup konversi dua arah dengan API lama:
import java.io.File;
import java.nio.file.Path;
public class MembuatPath {
public static void main(String[] args) {
Path p = Path.of("data", "arsip", "daftar.txt");
System.out.println("toString : " + p);
System.out.println("getFileName : " + p.getFileName());
System.out.println("getNameCount : " + p.getNameCount());
System.out.println("getName(0) : " + p.getName(0));
System.out.println("getName(2) : " + p.getName(2));
System.out.println("subpath(0, 2) : " + p.subpath(0, 2));
System.out.println("isAbsolute : " + p.isAbsolute());
System.out.println("startsWith : " + p.startsWith("data"));
System.out.println("endsWith : " + p.endsWith("daftar.txt"));
Path kosong = Path.of("");
System.out.println("path kosong : '" + kosong + "'");
for (int i = 0; i < p.getNameCount(); i++) {
System.out.println("elemen ke-" + i + " : " + p.getName(i));
}
File berkasLama = new File("daftar.txt");
Path dariFile = berkasLama.toPath();
Path sama = Path.of("daftar.txt");
System.out.println("toPath equals : " + dariFile.equals(sama));
System.out.println("toFile : " + sama.toFile().getName());
}
}Output yang Dihasilkan:
toString : data/arsip/daftar.txt
getFileName : daftar.txt
getNameCount : 3
getName(0) : data
getName(2) : daftar.txt
subpath(0, 2) : data/arsip
isAbsolute : false
startsWith : true
endsWith : true
path kosong : ''
elemen ke-0 : data
elemen ke-1 : arsip
elemen ke-2 : daftar.txt
toPath equals : true
toFile : daftar.txtMari kita telaah bagian-bagian pentingnya:
- Baris 6 (
Path.of("data", "arsip", "daftar.txt")): menggabungkan tiga segmen menjadi satuPath;toString()menampilkan bentuk gabungannya dengan pemisah platform. - Baris 8-13 (
getFileName,getNameCount,getName,subpath): mengurai path menjadi elemen bernomor mulai nol.subpath(0, 2)mengambil jangkauan elemen 0 sampai sebelum elemen 2 — aturan batas atas eksklusif sama sepertiString.substring. - Baris 14-16 (
isAbsolute,startsWith,endsWith):startsWithdanendsWithbekerja per elemen path, bukan per karakter teks, sehinggadaftar.txtcocok sebagai nama terakhir meskipun panjang path berbeda. - Baris 18-19 (
Path.of("")): satu-satunyaPathtanpa elemen — berguna sebagai identitas pada operasinormalize()yang kita lihat nanti. - Baris 21-23 (perulangan
getName):PathmengimplementasikanIterable<Path>, sehingga perulangan for-each juga bisa dipakai tanpa indeks. - Baris 25-30 (
toPath/toFile): jembatan dua arah denganjava.io.File. Dua path hasil konversi dibandingkan denganequals()dan terbukti sama.
Resolve, Normalize, dan Relativize #
Tiga operasi membentuk mayoritas manipulasi path sehari-hari. resolve() menggabungkan path dasar dengan tambahan; normalize() membersihkan segmen redundan . dan ..; relativize() menghitung path yang ditempuh dari satu path ke path lain. Ketiganya bekerja murni pada string — tanpa memeriksa keberadaan berkas di disk.
import java.nio.file.Path;
public class MemanipulasiPath {
public static void main(String[] args) {
Path dasar = Path.of("proyek", "src");
System.out.println("resolve relatif : " + dasar.resolve("Aplikasi.java"));
System.out.println("resolve absolut : " + dasar.resolve(Path.of("/etc")));
System.out.println("resolveSibling : " + dasar.resolveSibling("test"));
Path berantakan = Path.of("proyek", "src", "..", "doc", "readme.md");
System.out.println("sebelum : " + berantakan);
System.out.println("normalize : " + berantakan.normalize());
Path target = Path.of("proyek", "doc", "readme.md");
System.out.println("relativize : " + dasar.relativize(target));
Path relatif = Path.of("data", "daftar.txt");
Path absolut = relatif.toAbsolutePath();
System.out.println("toAbsolutePath isAbsolute : " + absolut.isAbsolute());
System.out.println("absolut endsWith daftar.txt: " + absolut.endsWith("daftar.txt"));
Path denganTitik = Path.of("a", ".", "b");
System.out.println("normalize titik : '" + denganTitik.normalize() + "'");
}
}Output yang Dihasilkan:
resolve relatif : proyek/src/Aplikasi.java
resolve absolut : /etc
resolveSibling : proyek/test
sebelum : proyek/src/../doc/readme.md
normalize : proyek/doc/readme.md
relativize : ../doc/readme.md
toAbsolutePath isAbsolute : true
absolut endsWith daftar.txt: true
normalize titik : 'a/b'Perilaku tiap operasi terbaca langsung dari keluarannya:
resolve("Aplikasi.java")menempelkan segmen relatif ke belakang path dasar. Ketika argumennya path mutlak (/etc),resolvejustru mengembalikan argumen itu apa adanya — path mutlak dianggap sudah komplet dan tidak perlu landasan.resolveSibling("test")mengganti elemen terakhir, persis seperti mengubah nama berkas pada folder yang sama:proyek/srcmenjadiproyek/test.normalize()meringkasproyek/src/../doc/readme.mdmenjadiproyek/doc/readme.mdkarenasrc/..saling meniadakan; segmen.juga dibuang hinggaa/./bmenjadia/b. Operasi ini string-only —..tidak diperiksa terhadap struktur nyata di disk.relativize(target)menjawab pertanyaan “dariproyek/src, bagaimana mencapaiproyek/doc/readme.md?” dengan jawaban../doc/readme.md.toAbsolutePath()mengubah path relatif menjadi mutlak dengan menambahkan direktori kerja di depannya. Nilai hasilnya berbeda di setiap mesin, sehingga program ini hanya mencetak sifatnya (isAbsolutedan kecocokan akhiran) — bukan string absolutnya.
Path Relatif, Path Mutlak, dan Pembandingnya #
Pemahaman isAbsolute() penting sebelum beroperasi pada berkas. Path relatif seperti data/daftar.txt ditafsirkan terhadap direktori kerja proses Java — folder dari mana java dijalankan — sedangkan path mutlak seperti /etc/hosts berdiri sendiri tanpa konteks. Method toAbsolutePath() memperjelas path relatif menjadi mutlak, sedangkan relativize() berjalan arah sebaliknya untuk dua path yang berangkat dari titik sama.
Sebagai pembanding ringkas dua generasi API:
| Aspek | java.io.File | java.nio.file.Path |
|---|---|---|
| Diperkenalkan | JDK 1.0 (1996) | Java 7 (NIO.2) |
| Sifat objek | mutable, method ubah isi | immutable, operasi kembalikan objek baru |
| Penggabungan segmen | konstruktor File(File, String) | of(...), resolve, resolveSibling |
Pembersihan .. | manual | normalize() |
| Jarak antar path | tidak tersedia | relativize() |
| Operasi disk | ada di class yang sama | dipisah ke class Files |
Pemisahan tanggung jawab di baris terakhir adalah perubahan desain terbesar: Path hanya merepresentasikan nama, sementara seluruh operasi sistem berkas tinggal di utility class Files. Chapter berikutnya membuka kotak peralatan Files itu — dari exists sampai readString — yang membuat tugas I/O sehari-hari selesai dengan beberapa baris kode.
Referensi #
- Oracle: Interface Path (Java SE 25 API)
- Oracle: Package java.nio.file (Java SE 25 API)
- dev.java: Java I/O
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
MemanipulasiPath.java
Unduh berkasimport java.nio.file.Path;
public class MemanipulasiPath {
public static void main(String[] args) {
Path dasar = Path.of("proyek", "src");
System.out.println("resolve relatif : " + dasar.resolve("Aplikasi.java"));
System.out.println("resolve absolut : " + dasar.resolve(Path.of("/etc")));
System.out.println("resolveSibling : " + dasar.resolveSibling("test"));
Path berantakan = Path.of("proyek", "src", "..", "doc", "readme.md");
System.out.println("sebelum : " + berantakan);
System.out.println("normalize : " + berantakan.normalize());
Path target = Path.of("proyek", "doc", "readme.md");
System.out.println("relativize : " + dasar.relativize(target));
Path relatif = Path.of("data", "daftar.txt");
Path absolut = relatif.toAbsolutePath();
System.out.println("toAbsolutePath isAbsolute : " + absolut.isAbsolute());
System.out.println("absolut endsWith daftar.txt: " + absolut.endsWith("daftar.txt"));
Path denganTitik = Path.of("a", ".", "b");
System.out.println("normalize titik : '" + denganTitik.normalize() + "'");
}
}
MembuatPath.java
Unduh berkasimport java.io.File;
import java.nio.file.Path;
public class MembuatPath {
public static void main(String[] args) {
Path p = Path.of("data", "arsip", "daftar.txt");
System.out.println("toString : " + p);
System.out.println("getFileName : " + p.getFileName());
System.out.println("getNameCount : " + p.getNameCount());
System.out.println("getName(0) : " + p.getName(0));
System.out.println("getName(2) : " + p.getName(2));
System.out.println("subpath(0, 2) : " + p.subpath(0, 2));
System.out.println("isAbsolute : " + p.isAbsolute());
System.out.println("startsWith : " + p.startsWith("data"));
System.out.println("endsWith : " + p.endsWith("daftar.txt"));
Path kosong = Path.of("");
System.out.println("path kosong : '" + kosong + "'");
for (int i = 0; i < p.getNameCount(); i++) {
System.out.println("elemen ke-" + i + " : " + p.getName(i));
}
File berkasLama = new File("daftar.txt");
Path dariFile = berkasLama.toPath();
Path sama = Path.of("daftar.txt");
System.out.println("toPath equals : " + dariFile.equals(sama));
System.out.println("toFile : " + sama.toFile().getName());
}
}