Pola pembersihan dengan finally pada Chapter sebelumnya bekerja, tetapi menuntut disiplin manual: deklarasi di luar try, penutupan di finally, dan close() yang sendiri melempar exception. Sejak Java 7, bahasa menyediakan bentuk yang jauh lebih ringkas: try-with-resources — resource dideklarasikan langsung di kurung setelah kata kunci try, dan compiler menyisipkan penutupan otomatis pada semua jalur keluar. Pada Chapter ini kita pelajari syarat AutoCloseable, urutan penutupan, dan perilakunya saat badan try melempar exception.
Bentuk dan Syarat AutoCloseable #
Bentuk umumnya: try (Deklarasi resource) { ... }. Setiap variabel di dalam kurung disebut resource dan tipenya harus mengimplementasikan interface AutoCloseable — interface dengan satu method, close(). Saat blok try berakhir, JVM memanggil close() tiap resource otomatis, dalam urutan kebalikan dari urutan deklarasinya. Mari kita buktikan dengan class buatan sendiri:
class SumberData implements AutoCloseable {
private final String nama;
SumberData(String nama) {
this.nama = nama;
System.out.println("membuka " + nama);
}
@Override
public void close() {
System.out.println("menutup " + nama);
}
}
public class ResourceSederhana {
public static void main(String[] args) {
try (SumberData basisdata = new SumberData("basisdata");
SumberData jaringan = new SumberData("jaringan")) {
System.out.println("badan try berjalan");
}
System.out.println("setelah blok");
}
}Output yang Dihasilkan:
membuka basisdata
membuka jaringan
badan try berjalan
menutup jaringan
menutup basisdata
setelah blokMari kita bedah urutan kejadiannya:
- Baris 1 (
class SumberData implements AutoCloseable): class ini memenuhi syarat resource karena mengimplementasikanAutoCloseabledan menyediakanclose(). - Baris 20-21 (
try (SumberData basisdata = ...; SumberData jaringan = ...) {}): kedua resource dibuat saat deklarasi —basisdatalebih dulu, lalujaringan. Urutan deklarasi ini yang menentukan urutan penutupan. - Keluaran
menutup jaringanlalumenutup basisdata: penutupan berjalan terbalik dari urutan pembukaan. Aturan ini disengaja agar resource yang dibuka paling akhir — biasanya yang paling bergantung pada resource lain — dilepas lebih dulu sebelum dependensinya. - Tidak ada
finallysama sekali: penutupan terjadi tanpa satu baris kode pembersihan dari kita, dan tetap berlaku baik saat badantrysukses maupun saat exception terbang keluar.
Membaca dengan Resource Nyata #
Pustaka standar penuh dengan class yang sudah mengimplementasikan AutoCloseable: BufferedReader, Scanner, FileInputStream, hingga koneksi JDBC. Contoh berikut membaca nama dari sebuah BufferedReader — penutupan otomatis menggantikan finally dari Chapter sebelumnya:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
public class BacaBaris {
public static void main(String[] args) {
String data = "Budi\nSiti\nDewi";
try (BufferedReader pembaca = new BufferedReader(new StringReader(data))) {
String baris;
while ((baris = pembaca.readLine()) != null) {
System.out.println("nama: " + baris);
}
} catch (IOException e) {
System.out.println("gagal membaca: " + e.getMessage());
}
}
}Output yang Dihasilkan:
nama: Budi
nama: Siti
nama: DewiTiga nama tercetak berurutan, dan setelah loop selesai pembaca.close() dipanggil tanpa kita menuliskannya. Perhatikan juga blok catch (IOException e) di baris setelah kurung resource — readLine() mendeklarasikan IOException sehingga compiler meminta penanganan, dan pada try-with-resources posisi catch tetap sama seperti biasa. Untuk demo ini sumber datanya StringReader di memori sehingga pembacaan tidak mungkin gagal; pada berkas sungguhan di penyimpanan, kegagalan baca adalah skenario nyata yang harus ditangani.
Penutupan Tetap Terjadi Saat Kegagalan #
Pertanyaan penting: apa yang terjadi dengan penutupan saat badan try melempar exception? Jawabannya: close() tetap dipanggil sebelum exception sampai ke blok catch:
class Sambungan implements AutoCloseable {
private final String nama;
Sambungan(String nama) {
this.nama = nama;
}
@Override
public void close() {
System.out.println("menutup " + nama);
}
}
public class ResourceGagal {
public static void main(String[] args) {
try (Sambungan s = new Sambungan("kirim")) {
System.out.println("mengirim data...");
throw new IllegalStateException("jaringan terputus");
} catch (IllegalStateException e) {
System.out.println("ditangani: " + e.getMessage());
}
System.out.println("program berlanjut");
}
}Output yang Dihasilkan:
mengirim data...
menutup kirim
ditangani: jaringan terputus
program berlanjutUrutan keluarannya membuktikan mekanismenya: mengirim data... tercetak, IllegalStateException dilempar, lalu menutup kirim berjalan, dan baru setelah itu blok catch menangani exception. Compiler menyusun ulang konstruksi ini menjadi pola finally-biasa di belakang layar — penutupan berada di jalur keluar mana pun — hanya saja beban menuliskannya diambil alih sepenuhnya.
Kita juga baru saja menyaksikan sesuatu yang menarik pada contoh BacaBaris: compiler menolak program jika IOException tidak ditangani, tetapi tidak pernah memaksa apa pun untuk IllegalStateException. Dari mana perbedaan perlakuan itu berasal? Chapter berikutnya membedah perbedaan mendasar antara checked dan unchecked exceptions — filosofi di baliknya dan dampaknya pada desain method kita.
Referensi #
- Interface AutoCloseable pada API java.lang
- Class BufferedReader pada API java.io
- Oracle: try-with-resources di dev.java
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
BacaBaris.java
Unduh berkasimport java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
public class BacaBaris {
public static void main(String[] args) {
String data = "Budi\nSiti\nDewi";
try (BufferedReader pembaca = new BufferedReader(new StringReader(data))) {
String baris;
while ((baris = pembaca.readLine()) != null) {
System.out.println("nama: " + baris);
}
} catch (IOException e) {
System.out.println("gagal membaca: " + e.getMessage());
}
}
}
ResourceGagal.java
Unduh berkasclass Sambungan implements AutoCloseable {
private final String nama;
Sambungan(String nama) {
this.nama = nama;
}
@Override
public void close() {
System.out.println("menutup " + nama);
}
}
public class ResourceGagal {
public static void main(String[] args) {
try (Sambungan s = new Sambungan("kirim")) {
System.out.println("mengirim data...");
throw new IllegalStateException("jaringan terputus");
} catch (IllegalStateException e) {
System.out.println("ditangani: " + e.getMessage());
}
System.out.println("program berlanjut");
}
}
ResourceSederhana.java
Unduh berkasclass SumberData implements AutoCloseable {
private final String nama;
SumberData(String nama) {
this.nama = nama;
System.out.println("membuka " + nama);
}
@Override
public void close() {
System.out.println("menutup " + nama);
}
}
public class ResourceSederhana {
public static void main(String[] args) {
try (SumberData basisdata = new SumberData("basisdata");
SumberData jaringan = new SumberData("jaringan")) {
System.out.println("badan try berjalan");
}
System.out.println("setelah blok");
}
}