"Амиго, десет-хът!"

„Щастлив съм да науча Java, капитане!“

"Спокойно, Амиго. Днес имаме супер интересна тема. Ще говорим за това How Java програма взаимодейства с външни ресурси и ще изучим едно много интересно Java изявление. По-добре не си затваряйте ушите."

"Целият съм в слух."

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

„Тогава Howво се считат за вътрешни ресурси?“

„Вътрешните ресурси са обектите, създадени вътре в Java машината. Обикновено взаимодействието следва тази схема:

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

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

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

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

"Разбрах. Това е като местата в метрото or друг градски транспорт. Ако дадено място е свободно, всеки може да го заеме. Ако място е заето, то се контролира от човека, който го е заел."

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

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

— Звучи честно.

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

„Това е като програми, които могат да изядат цялата памет...“

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

„Ако това са добрите новини, значи ли, че има и лоши?“

„Точно така. Лошата новина е, че ако пишете сървърно приложение...“

„Ама аз ли пиша такива молби?

„Много сървърни applications са написани на Java, така че най-вероятно ще ги пишете за работа. Както казах, ако пишете сървърно приложение, вашият сървър трябва да работи без прекъсване дни, седмици, месеци, и т.н."

"С други думи, програмата не се прекратява и това означава, че паметта не се освобождава автоматично."

„Точно така. И ако отваряте 100 file на ден и не ги затваряте, след няколко седмици приложението ви ще достигне лимита на ресурсите си и ще се срине.“

„Това е много по-малко от месеци стабилна работа! Какво може да се направи?“

„Класовете, които използват външни ресурси, имат специален метод за тяхното освобождаване: 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.“

„Фу... Това е хубаво нещо, което приключва въпроса. Относително разбираемо, но донякъде досадно, нали?“

„Така е. Ето защо създателите на Java ни помогнаха, като добавиха малко синтактична захар. Сега нека да преминем към акцента на програмата or по-скоро към този урок:

try-с-ресурси

„Започвайки със своята 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. Или можете да не ги добавяте, ако не са необходими.

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

"Често може да срещнете ситуация, когато трябва да отворите няколко 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";

try(FileInputStream input = new FileInputStream(src);

FileOutputStream output = new FileOutputStream(dest))
{
   byte[] buffer = input.readAllBytes();
   output.write(buffer);
}
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();
}

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

„Това, което можем да кажем е, че трябва да го използваме.“