1. Sumber luar

Semasa program Java berjalan, kadangkala ia berinteraksi dengan entiti di luar mesin Java. Contohnya, dengan fail pada cakera. Entiti ini biasanya dipanggil sumber luaran. Sumber dalaman ialah objek yang dibuat di dalam mesin Java.

Biasanya, interaksi mengikut skema ini:

Pernyataan cuba-dengan-sumber

Menjejak sumber

Sistem pengendalian dengan teliti menjejaki sumber yang ada, dan juga mengawal akses dikongsi kepada mereka daripada program yang berbeza. Contohnya, jika satu program menukar fail, maka program lain tidak boleh menukar (atau memadam) fail tersebut. Prinsip ini tidak terhad kepada fail, tetapi ia menyediakan contoh yang paling mudah difahami.

Sistem pengendalian mempunyai fungsi (API) yang membenarkan program memperoleh dan/atau mengeluarkan sumber. Jika sumber sibuk, maka hanya program yang memperolehnya boleh berfungsi dengannya. Jika sumber adalah percuma, maka mana-mana program boleh memperolehnya.

Bayangkan pejabat anda telah berkongsi cawan kopi. Jika seseorang mengambil mug, maka orang lain tidak boleh mengambilnya lagi. Tetapi apabila cawan itu digunakan, dibasuh, dan diletakkan semula di tempatnya, maka sesiapa sahaja boleh mengambilnya semula. Keadaan tempat duduk di dalam bas atau kereta bawah tanah adalah sama. Jika kerusi adalah percuma, maka sesiapa sahaja boleh mengambilnya. Jika kerusi diduduki, maka ia dikawal oleh orang yang mengambilnya.

Memperoleh sumber luaran .

Setiap kali program Java anda mula berfungsi dengan fail pada cakera, mesin Java meminta sistem pengendalian untuk akses eksklusif kepadanya. Jika sumber itu percuma, maka mesin Java memperolehnya.

Tetapi selepas anda selesai bekerja dengan fail, sumber (fail) ini mesti dikeluarkan, iaitu anda perlu memberitahu sistem pengendalian bahawa anda tidak lagi memerlukannya. Jika anda tidak melakukan ini, maka sumber tersebut akan terus dipegang oleh program anda.

Sistem pengendalian mengekalkan senarai sumber yang diduduki oleh setiap program yang sedang berjalan. Jika program anda melebihi had sumber yang ditetapkan, maka sistem pengendalian tidak lagi akan memberi anda sumber baharu.

Berita baiknya ialah jika program anda ditamatkan, semua sumber dikeluarkan secara automatik (sistem pengendalian itu sendiri melakukan ini).

Berita buruknya ialah jika anda menulis aplikasi pelayan (dan banyak aplikasi pelayan ditulis dalam Java), pelayan anda perlu dapat dijalankan selama beberapa hari, minggu dan bulan tanpa henti. Dan jika anda membuka 100 fail sehari dan tidak menutupnya, maka dalam beberapa minggu aplikasi anda akan mencapai had sumber dan ranapnya. Itu kurang daripada bulan kerja yang stabil.


2. close()kaedah

Kelas yang menggunakan sumber luaran mempunyai kaedah khas untuk melepaskannya: close().

Di bawah ini kami menyediakan contoh program yang menulis sesuatu pada fail dan kemudian menutup fail apabila ia selesai, iaitu ia membebaskan sumber sistem pengendalian. Ia kelihatan seperti ini:

Kod Catatan
String path = "c:\\projects\\log.txt";
FileOutputStream output = new FileOutputStream(path);
output.write(1);
output.close();
Laluan ke fail.
Dapatkan objek fail: peroleh sumber.
Tulis pada fail
Tutup fail - lepaskan sumber

Selepas bekerja dengan fail (atau sumber luaran lain), anda perlu memanggil close()kaedah pada objek yang dipautkan kepada sumber luaran.

Pengecualian

Semuanya nampak mudah. Tetapi pengecualian boleh berlaku semasa program berjalan dan sumber luaran tidak akan dikeluarkan. Dan itu sangat buruk.

Untuk memastikan close()kaedah sentiasa dipanggil, kita perlu membungkus kod kita dalam try- catch- finallyblok dan menambah close()kaedah pada finallyblok. Ia akan kelihatan seperti ini:

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

Kod ini tidak akan disusun, kerana outputpembolehubah diisytiharkan di dalam try {}blok, dan oleh itu tidak kelihatan dalam finallyblok.

Mari kita betulkan:

FileOutputStream output = new FileOutputStream(path);

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

Tidak mengapa, tetapi ia tidak akan berfungsi jika ralat berlaku semasa kami mencipta objek FileOutputStream, dan ini boleh berlaku dengan mudah.

Mari kita betulkan:

FileOutputStream output = null;

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

Masih ada sedikit kritikan. Pertama, jika ralat berlaku semasa mencipta FileOutputStreamobjek, maka outputpembolehubah akan menjadi batal. Kemungkinan ini mesti diambil kira dalam finallyblok.

Kedua, close()kaedah itu sentiasa dipanggil dalam finallyblok, yang bermaksud bahawa ia tidak perlu dalam tryblok. Kod akhir akan kelihatan seperti ini:

FileOutputStream output = null;

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

Walaupun kita tidak menganggap catchblok, yang boleh ditinggalkan, maka 3 baris kod kita menjadi 10. Tetapi pada asasnya kita hanya membuka fail dan menulis 1. Sedikit menyusahkan, tidakkah anda fikir?


3. try-dengan-sumber

Dan di sini pencipta Java memutuskan untuk menaburkan beberapa gula sintaksis pada kami. Bermula dengan versi ke-7nya, Java mempunyai trypernyataan -dengan-sumber yang baharu.

Ia dicipta dengan tepat untuk menyelesaikan masalah dengan panggilan wajib kepada close()kaedah tersebut. Kes umum kelihatan agak mudah:

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

Ini adalah satu lagi variasi try kenyataan . Anda perlu menambah kurungan selepas trykata kunci, dan kemudian mencipta objek dengan sumber luaran di dalam kurungan. Untuk setiap objek dalam kurungan, pengkompil menambah finallybahagian dan panggilan kepada close()kaedah.

Di bawah adalah dua contoh yang setara:

Kod panjang Kod dengan cuba-dengan-sumber
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);
}

Kod menggunakan try-with-resources adalah lebih pendek dan lebih mudah dibaca. Dan semakin sedikit kod yang kita ada, semakin kurang peluang untuk melakukan kesilapan menaip atau ralat lain.

Dengan cara ini, kita boleh menambah catchdan finallymenyekat trypernyataan -with-resources. Atau anda tidak boleh menambahnya jika ia tidak diperlukan.



4. Beberapa pembolehubah pada masa yang sama

Dengan cara ini, anda mungkin sering menghadapi situasi apabila anda perlu membuka beberapa fail pada masa yang sama. Katakan anda sedang menyalin fail, jadi anda memerlukan dua objek: fail dari mana anda menyalin data dan fail yang anda salin data.

Dalam kes ini, trypernyataan -with-resources membolehkan anda mencipta satu tetapi beberapa objek di dalamnya. Kod yang mencipta objek mesti dipisahkan dengan koma bertitik. Inilah rupa umum pernyataan ini:

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

Contoh menyalin fail:

Kod panjang Kod 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 boleh kita katakan di sini? try-dengan-sumber adalah perkara yang mengagumkan!


5. AutoCloseableantara muka

Tetapi bukan itu sahaja. Pembaca yang penuh perhatian akan segera mula mencari perangkap yang mengehadkan bagaimana pernyataan ini boleh digunakan.

Tetapi bagaimana trypernyataan -dengan-sumber berfungsi jika kelas tidak mempunyai close()kaedah? Baiklah, andaikan tiada apa yang akan dipanggil. Tiada kaedah, tiada masalah.

Tetapi bagaimana trypernyataan -dengan-sumber berfungsi jika kelas mempunyai beberapa close()kaedah? Dan mereka memerlukan hujah untuk disampaikan kepada mereka? Dan kelas tidak mempunyai close()kaedah tanpa parameter?

Saya harap anda benar-benar bertanya kepada diri sendiri soalan ini, dan mungkin yang lain.

Untuk mengelakkan isu sedemikian, pencipta Java menghasilkan antara muka khas yang dipanggil AutoCloseable, yang hanya mempunyai satu kaedah — close(), yang tidak mempunyai parameter.

Mereka juga menambah sekatan bahawa hanya objek kelas yang melaksanakanAutoCloseable boleh diisytiharkan sebagai sumber dalam trypernyataan -dengan-sumber. Akibatnya, objek sedemikian akan sentiasa mempunyai close()kaedah tanpa parameter.

Dengan cara ini, adakah anda fikir mungkin untuk trypernyataan -with-resources untuk mengisytiharkan sebagai sumber objek yang kelasnya mempunyai close()kaedah sendiri tanpa parameter tetapi yang tidak melaksanakan AutoCloseable?

Berita buruk: Jawapan yang betul ialah tidak - kelas mesti melaksanakan AutoCloseableantara muka.

Berita baik: Java mempunyai banyak kelas yang melaksanakan antara muka ini, jadi kemungkinan besar semuanya akan berfungsi sebagaimana mestinya.