1. Външни ресурси

Докато Java програма работи, понякога тя взаимодейства с обекти извън Java машината. Например с файлове на диск. Тези обекти обикновено се наричат ​​външни ресурси. Вътрешните ресурси са обектите, създадени в Java машината.

Обикновено взаимодействието следва следната схема:

Декларация за опит с ресурси

Ресурси за проследяване

Операционната система стриктно следи наличните ресурси и също така контролира споделения достъп до тях от различни програми. Например, ако една програма промени файл, тогава друга програма не може да промени (or изтрие) този файл. Този принцип не се ограничава до файлове, но те предоставят най-разбираемия пример.

Операционната система има функции (API), които позволяват на програмата да придобива и/or освобождава ресурси. Ако даден ресурс е зает, тогава само програмата, която го е придобила, може да работи с него. Ако даден ресурс е безплатен, тогава всяка програма може да го придобие.

Представете си, че в офиса ви има споделени чаши за кафе. Ако някой вземе чаша, други хора вече не могат да я вземат. Но след като чашата бъде използвана, измита и върната на мястото й, всеки може да я вземе отново. Ситуацията с местата в автобуса or метрото е същата. Ако има свободно място, всеки може да го заеме. Ако дадено място е заето, то се контролира от лицето, което го е заело.

Придобиване на външни ресурси .

Всеки път, когато вашата Java програма започне да работи с файл на диска, Java машината иска от операционната система изключителен достъп до него. Ако ресурсът е безплатен, тогава Java машината го придобива.

Но след като приключите работата с file, този ресурс (файл) трябва да бъде освободен, т.е. трябва да уведомите операционната система, че вече не ви е необходим. Ако не направите това, тогава ресурсът ще продължи да се държи от вашата програма.

Операционната система поддържа списък с ресурси, заети от всяка работеща програма. Ако вашата програма надхвърли определения лимит на ресурси, тогава операционната система вече няма да ви предоставя нови ресурси.

Добрата новина е, че ако вашата програма се прекрати, всички ресурси се освобождават автоматично (самата операционна система прави това).

Лошата новина е, че ако пишете сървърно приложение (а много сървърни applications са написани на Java), вашият сървър трябва да може да работи дни, седмици и месеци без спиране. И ако отваряте 100 file на ден и не ги затваряте, след няколко седмици приложението ви ще достигне лимита на ресурсите си и ще се срине. Това е много по-малко от месеци стабилна работа.


2. close()метод

Класовете, които използват външни ресурси, имат специален метод за тяхното освобождаване: close().

По-долу предоставяме пример за програма, която записва нещо във файл и след това затваря file, когато е готово, т.е. освобождава ресурсите на операционната система. Изглежда нещо подобно:

Код Забележка
String path = "c:\\projects\\log.txt";
FileOutputStream output = new FileOutputStream(path);
output.write(1);
output.close();
Пътят до file.
Вземете файловия обект: придобийте ресурса.
Записване във file
Затваряне на file - освобождаване на ресурса

След работа с файл (or други външни ресурси), трябва да извикате метода close()на обекта, свързан с външния ресурс.

Изключения

Всичко изглежда просто. Но могат да възникнат изключения, докато програмата се изпълнява и външният ресурс няма да бъде освободен. И това е много лошо.

За да гарантираме, че close()методът винаги се извиква, трябва да увием нашия code в блок try- catch- finallyи да добавим close()метода към finallyблока. Ще изглежда нещо подобно:

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

Този code няма да се компorра, тъй като outputпроменливата е декларирана вътре в try {}блока и следователно не се вижда в finallyблока.

Нека го поправим:

FileOutputStream output = new FileOutputStream(path);

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

Всичко е наред, но няма да работи, ако възникне грешка, когато създаваме обекта FileOutputStream, а това може да се случи доста лесно.

Нека го поправим:

FileOutputStream output = null;

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

Все още има няколко критики. Първо, ако възникне грешка при създаването на FileOutputStreamобекта, тогава outputпроменливата ще бъде нулева. Тази възможност трябва да се отчете в finallyблока.

Второ, close()методът винаги се извиква в finallyблока, което означава, че не е необходим в tryблока. Крайният code ще изглежда така:

FileOutputStream output = null;

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

Дори ако не вземем предвид блока catch, който може да бъде пропуснат, тогава нашите 3 реда code стават 10. Но ние просто отворихме file и написахме 1. Малко тромаво, не мислите ли?


3. try-с-ресурси

И тук създателите на Java решиха да ни поръсят със синтактична захар. Започвайки със своята 7-ма version, Java има нов tryоператор -with-resources.

Създаден е именно за да реши проблема със задължителното извикване на close()метода. Общият случай изглежда доста прост:

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

Това е друг вариант на try твърдението . Трябва да добавите скоби след tryключовата дума и след това да създадете обекти с външни ресурси в скобите. За всеки обект в скобите компилаторът добавя finallyраздел и извикване към close()метода.

По-долу са два еквивалентни примера:

Дълъг code Код с опит с ресурси
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);
}

Кодът, използващ try-with-resources, е много по-кратък и по-лесен за четене. И колкото по-малко code имаме, толкова по-малък е шансът да направим печатна or друга грешка.

Между другото, можем да добавим catchи finallyблокове към tryоператора -with-resources. Или можете да не ги добавите, ако не са необходими.



4. Няколко променливи едновременно

Между другото, често може да срещнете ситуация, когато трябва да отворите няколко file едновременно. Да кажем, че копирате файл, така че имате нужда от два обекта: файлът, от който копирате данни, и файлът, в който копирате данни.

В този случай tryоператорът -with-resources ви позволява да създадете един, но няколко обекта в него. Кодът, който създава обектите, трябва да бъде разделен с точка и запетая. Ето общия вид на това твърдение:

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

Пример за копиране на файлове:

Дълъг code Кратък code
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);
}

Е, Howво можем да кажем тук? try-с-ресурси е прекрасно нещо!


5. AutoCloseableинтерфейс

Но това не е всичко. Внимателният читател веднага ще започне да търси клопки, които ограничават начина, по който това твърдение може да се приложи.

Но How tryработи изразът -with-resources, ако класът няма close()метод? Е, да предположим, че нищо няма да бъде наречено. Няма метод, няма проблем.

Но How tryработи изразът -with-resources, ако класът има няколко close()метода? И имат нужда да им се подават аргументи? И класът няма close()метод без параметри?

Надявам се, че наистина сте си задали тези въпроси, а може би и други.

За да избегнат подобни проблеми, създателите на Java измислиха специален интерфейс, наречен AutoCloseable, който има само един метод — close(), който няма параметри.

Те също така добавиха ограничението, че само обекти от класове, които изпълняват,AutoCloseable могат да бъдат декларирани като ресурси в tryоператор -with-resources. В резултат на това такива обекти винаги ще имат close()метод без параметри.

Между другото, смятате ли, че е възможно tryоператор -with-resources да декларира като ресурс обект, чийто клас има свой собствен close()метод без параметри, но който не изпълнява AutoCloseable?

Лошата новина: Правилният отговор е не - класовете трябва да имплементират AutoCloseableинтерфейса.

Добрата новина: Java има много класове, които имплементират този интерфейс, така че е много вероятно всичко да работи Howто трябва.