1. Sumber daya eksternal

Saat program Java berjalan, terkadang ia berinteraksi dengan entitas di luar mesin Java. Misalnya, dengan file di disk. Entitas ini biasanya disebut sumber daya eksternal. Sumber daya internal adalah objek yang dibuat di dalam mesin Java.

Biasanya, interaksi mengikuti skema ini:

Pernyataan coba-dengan-sumber daya

Sumber daya pelacakan

Sistem operasi dengan ketat melacak sumber daya yang tersedia, dan juga mengontrol akses bersama ke sumber daya tersebut dari berbagai program. Misalnya, jika satu program mengubah file, maka program lain tidak dapat mengubah (atau menghapus) file tersebut. Prinsip ini tidak terbatas pada file, tetapi memberikan contoh yang paling mudah dimengerti.

Sistem operasi memiliki fungsi (API) yang memungkinkan program memperoleh dan/atau melepaskan sumber daya. Jika sumber daya sibuk, maka hanya program yang memperolehnya yang dapat bekerja dengannya. Jika sumber daya gratis, maka program apa pun dapat memperolehnya.

Bayangkan kantor Anda telah berbagi cangkir kopi. Jika ada yang mengambil mug, maka orang lain tidak bisa lagi mengambilnya. Namun setelah mug tersebut digunakan, dicuci, dan diletakkan kembali pada tempatnya, maka siapapun dapat mengambilnya kembali. Situasi dengan tempat duduk di bus atau kereta bawah tanah adalah sama. Jika kursi gratis, maka siapa pun dapat mengambilnya. Jika kursi diduduki, maka itu dikendalikan oleh orang yang mengambilnya.

Memperoleh sumber daya eksternal .

Setiap kali program Java Anda mulai bekerja dengan file di disk, mesin Java meminta sistem operasi untuk akses eksklusif ke sana. Jika sumber dayanya gratis, maka mesin Java akan memperolehnya.

Tetapi setelah Anda selesai bekerja dengan file tersebut, sumber daya (file) ini harus dirilis, yaitu Anda perlu memberi tahu sistem operasi bahwa Anda tidak lagi membutuhkannya. Jika Anda tidak melakukan ini, sumber daya akan terus disimpan oleh program Anda.

Sistem operasi menyimpan daftar sumber daya yang ditempati oleh setiap program yang sedang berjalan. Jika program Anda melebihi batas sumber daya yang ditetapkan, sistem operasi tidak akan lagi memberi Anda sumber daya baru.

Kabar baiknya adalah jika program Anda dihentikan, semua sumber daya dilepaskan secara otomatis (sistem operasi itu sendiri yang melakukannya).

Kabar buruknya adalah jika Anda sedang menulis aplikasi server (dan banyak aplikasi server yang ditulis dalam Java), server Anda harus dapat berjalan selama berhari-hari, berminggu-minggu, dan berbulan-bulan tanpa henti. Dan jika Anda membuka 100 file sehari dan tidak menutupnya, maka dalam beberapa minggu aplikasi Anda akan mencapai batas sumber daya dan macet. Itu jauh dari pekerjaan stabil selama berbulan-bulan.


2. close()metode

Kelas yang menggunakan sumber daya eksternal memiliki metode khusus untuk melepaskannya: close().

Di bawah ini kami memberikan contoh program yang menulis sesuatu ke file dan kemudian menutup file setelah selesai, yaitu membebaskan sumber daya sistem operasi. Ini terlihat seperti ini:

Kode Catatan
String path = "c:\\projects\\log.txt";
FileOutputStream output = new FileOutputStream(path);
output.write(1);
output.close();
Jalur ke file.
Dapatkan objek file: dapatkan sumber daya.
Tulis ke file
Tutup file - lepaskan sumber daya

Setelah bekerja dengan file (atau sumber daya eksternal lainnya), Anda harus memanggil close()metode pada objek yang ditautkan ke sumber daya eksternal.

Pengecualian

Semuanya tampak sederhana. Namun pengecualian dapat terjadi saat program berjalan, dan sumber daya eksternal tidak akan dirilis. Dan itu sangat buruk.

Untuk memastikan bahwa close()metode selalu dipanggil, kita perlu membungkus kode kita dalam blok try- catch- finallydan menambahkan close()metode ke finallyblok tersebut. Ini akan terlihat seperti ini:

try
{
   FileOutputStream output = new FileOutputStream(path);
   output.write(1);
   output.close();
}
catch (IOException e)
{
   e.printStackTrace();
}
finally
{
   output.close();
}

Kode ini tidak dapat dikompilasi, karena outputvariabel dideklarasikan di dalam try {}blok, dan karenanya tidak terlihat di finallyblok.

Mari kita perbaiki:

FileOutputStream output = new FileOutputStream(path);

try
{
   output.write(1);
   output.close();
}
catch (IOException e)
{
   e.printStackTrace();
}
finally
{
   output.close();
}

Tidak apa-apa, tetapi tidak akan berfungsi jika terjadi kesalahan saat kita membuat objek FileOutputStream, dan ini bisa terjadi dengan mudah.

Mari kita perbaiki:

FileOutputStream output = null;

try
{
   output = new FileOutputStream(path);
   output.write(1);
   output.close();
}
catch (IOException e)
{
   e.printStackTrace();
}
finally
{
   output.close();
}

Masih ada beberapa kritik. Pertama, jika terjadi kesalahan saat membuat FileOutputStreamobjek, maka outputvariabelnya akan menjadi nol. Kemungkinan ini harus diperhitungkan dalam finallyblok.

Kedua, close()metode ini selalu dipanggil di dalam finallyblok, yang artinya tidak diperlukan di dalam tryblok. Kode terakhir akan terlihat seperti ini:

FileOutputStream output = null;

try
{
   output = new FileOutputStream(path);
   output.write(1);
}
catch (IOException e)
{
   e.printStackTrace();
}
finally
{
   if (output != null)
      output.close();
}

Bahkan jika kita tidak mempertimbangkan catchblok, yang dapat dihilangkan, maka 3 baris kode kita menjadi 10. Tapi pada dasarnya kita hanya membuka file dan menulis 1. Agak rumit, bukan begitu?


3. try-dengan-sumber daya

Dan di sini pencipta Java memutuskan untuk menaburkan gula sintaksis pada kami. Dimulai dengan versi ke-7, Java memiliki trypernyataan -with-resources baru.

Itu dibuat tepat untuk memecahkan masalah dengan panggilan wajib ke close()metode tersebut. Kasus umum terlihat cukup sederhana:

try (ClassName name = new ClassName())
{
     Code that works with the name variable
}

Ini adalah variasi lain dari try pernyataan tersebut . Anda perlu menambahkan tanda kurung setelah trykata kunci, lalu membuat objek dengan sumber daya eksternal di dalam tanda kurung. Untuk setiap objek dalam tanda kurung, kompiler menambahkan sebuah finallybagian dan panggilan ke close()metode.

Di bawah ini adalah dua contoh yang setara:

Kode panjang Kode dengan coba-dengan-sumber daya
FileOutputStream output = null;

try
{
   output = new FileOutputStream(path);
   output.write(1);
}
finally
{
   if (output != null)
   output.close();
}
try(FileOutputStream output = new FileOutputStream(path))
{
   output.write(1);
}

Kode yang menggunakan try-with-resources jauh lebih pendek dan lebih mudah dibaca. Dan semakin sedikit kode yang kita miliki, semakin kecil kemungkinan salah ketik atau kesalahan lainnya.

Ngomong-ngomong, kita bisa menambah catchdan finallymemblokir trypernyataan -with-resources. Atau Anda tidak dapat menambahkannya jika tidak diperlukan.



4. Beberapa variabel sekaligus

Omong-omong, Anda mungkin sering menghadapi situasi ketika Anda perlu membuka beberapa file secara bersamaan. Katakanlah Anda sedang menyalin file, jadi Anda memerlukan dua objek: file tempat Anda menyalin data dan file tempat Anda menyalin data.

Dalam hal ini, trypernyataan -with-resources memungkinkan Anda membuat satu tetapi beberapa objek di dalamnya. Kode yang membuat objek harus dipisahkan dengan titik koma. Inilah tampilan umum dari pernyataan ini:

try (ClassName name = new ClassName(); ClassName2 name2 = new ClassName2())
{
   Code that works with the name and name2 variables
}

Contoh menyalin file:

Kode panjang Kode pendek
String src = "c:\\projects\\log.txt";
String dest = "c:\\projects\\copy.txt";

FileInputStream input = null;
FileOutputStream output = null;

try
{
   input = new FileInputStream(src);
   output = new FileOutputStream(dest);

   byte[] buffer = input.readAllBytes();
   output.write(buffer);
}
finally
{
   if (input != null)
      input.close();
   if (output != null)
      output.close();
}
String src = "c:\\projects\\log.txt";
String dest = "c:\\projects\\copy.txt";

try(FileInputStream input = new FileInputStream(src);

FileOutputStream output = new FileOutputStream(dest))
{
   byte[] buffer = input.readAllBytes();
   output.write(buffer);
}

Nah, apa yang bisa kita katakan di sini? try-dengan-sumber daya adalah hal yang luar biasa!


5. AutoCloseableantarmuka

Tapi itu belum semuanya. Pembaca yang penuh perhatian akan segera mulai mencari jebakan yang membatasi bagaimana pernyataan ini dapat diterapkan.

Tapi bagaimana trycara kerja pernyataan -with-resources jika kelas tidak memiliki close()metode? Nah, misalkan tidak ada yang akan dipanggil. Tidak ada metode, tidak ada masalah.

Tetapi bagaimana trycara kerja pernyataan -with-resources jika kelas memiliki beberapa close()metode? Dan mereka membutuhkan argumen untuk disampaikan kepada mereka? Dan kelas tidak memiliki close()metode tanpa parameter?

Saya harap Anda benar-benar bertanya pada diri sendiri pertanyaan-pertanyaan ini, dan mungkin yang lainnya.

Untuk menghindari masalah seperti itu, pembuat Java membuat antarmuka khusus bernama AutoCloseable, yang hanya memiliki satu metode — close(), yang tidak memiliki parameter.

Mereka juga menambahkan batasan bahwa hanya objek dari kelas yang diimplementasikanAutoCloseable yang dapat dideklarasikan sebagai resource dalam trypernyataan -with-resources. Akibatnya, objek seperti itu akan selalu memiliki close()metode tanpa parameter.

Omong-omong, menurut Anda apakah trypernyataan -with-resources dapat dideklarasikan sebagai sumber daya sebuah objek yang kelasnya memiliki close()metode sendiri tanpa parameter tetapi tidak diimplementasikan AutoCloseable?

Berita buruknya: Jawaban yang benar adalah tidak — kelas harus mengimplementasikan AutoCloseableantarmuka.

Kabar baiknya: Java memiliki banyak kelas yang mengimplementasikan antarmuka ini, jadi kemungkinan besar semuanya akan berfungsi sebagaimana mestinya.