1. Resurse externe

Pe măsură ce rulează un program Java, uneori interacționează cu entități din afara mașinii Java. De exemplu, cu fișiere de pe disc. Aceste entități sunt de obicei numite resurse externe. Resursele interne sunt obiectele create în interiorul mașinii Java.

De obicei, interacțiunea urmează această schemă:

Declarație de încercare cu resurse

Urmărirea resurselor

Sistemul de operare urmărește cu rigurozitate resursele disponibile și, de asemenea, controlează accesul partajat la acestea din diferite programe. De exemplu, dacă un program modifică un fișier, atunci un alt program nu poate schimba (sau șterge) acel fișier. Acest principiu nu se limitează la fișiere, dar ele oferă exemplul cel mai ușor de înțeles.

Sistemul de operare are funcții (API-uri) care permit unui program să achiziționeze și/sau să elibereze resurse. Dacă o resursă este ocupată, atunci numai programul care a achiziționat-o poate funcționa cu ea. Dacă o resursă este gratuită, atunci orice program o poate achiziționa.

Imaginează-ți că biroul tău are căni de cafea în comun. Dacă cineva ia o cană, atunci alții nu o mai pot lua. Dar odată ce cana este folosită, spălată și pusă la loc, oricine o poate lua din nou. Situația cu locurile în autobuz sau metrou este aceeași. Dacă un loc este liber, atunci oricine îl poate ocupa. Dacă un loc este ocupat, atunci acesta este controlat de persoana care l-a ocupat.

Achiziția de resurse externe .

De fiecare dată când programul dvs. Java începe să lucreze cu un fișier de pe disc, mașina Java cere sistemului de operare acces exclusiv la acesta. Dacă resursa este gratuită, atunci mașina Java o achiziționează.

Dar după ce ați terminat de lucrat cu fișierul, această resursă (fișier) trebuie eliberată, adică trebuie să anunțați sistemul de operare că nu mai aveți nevoie de ea. Dacă nu faceți acest lucru, atunci resursa va continua să fie deținută de programul dvs.

Sistemul de operare menține o listă de resurse ocupate de fiecare program care rulează. Dacă programul dvs. depășește limita de resurse alocată, atunci sistemul de operare nu vă va mai oferi resurse noi.

Vestea bună este că, dacă programul dvs. se încheie, toate resursele sunt eliberate automat (sistemul de operare însuși face acest lucru).

Vestea proastă este că, dacă scrieți o aplicație server (și multe aplicații server sunt scrise în Java), serverul dvs. trebuie să poată rula zile, săptămâni și luni fără oprire. Și dacă deschideți 100 de fișiere pe zi și nu le închideți, atunci în câteva săptămâni aplicația dvs. va atinge limita de resurse și se va prăbuși. Asta e mult sub luni de muncă stabilă.


2. close()metoda

Clasele care folosesc resurse externe au o metodă specială de eliberare a acestora: close().

Mai jos oferim un exemplu de program care scrie ceva într-un fișier și apoi închide fișierul când este gata, adică eliberează resursele sistemului de operare. Arata cam asa:

Cod Notă
String path = "c:\\projects\\log.txt";
FileOutputStream output = new FileOutputStream(path);
output.write(1);
output.close();
Calea către fișier.
Obțineți obiectul fișier: obțineți resursa.
Scrieți în fișier
Închideți fișierul - eliberați resursa

După ce lucrați cu un fișier (sau alte resurse externe), trebuie să apelați close()metoda pe obiectul legat de resursa externă.

Excepții

Totul pare simplu. Dar pot apărea excepții pe măsură ce un program rulează, iar resursa externă nu va fi eliberată. Și asta este foarte rău.

Pentru a ne asigura că close()metoda este întotdeauna apelată, trebuie să ne înfășurăm codul într-un bloc try- catch- finallyși să adăugăm close()metoda la finallybloc. Va arata cam asa:

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

Acest cod nu se va compila, deoarece outputvariabila este declarată în interiorul try {}blocului și, prin urmare, nu este vizibilă în finallybloc.

Hai să o reparăm:

FileOutputStream output = new FileOutputStream(path);

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

Este în regulă, dar nu va funcționa dacă apare o eroare când creăm obiectul FileOutputStream, iar acest lucru s-ar putea întâmpla destul de ușor.

Hai să o reparăm:

FileOutputStream output = null;

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

Mai sunt câteva critici. În primul rând, dacă apare o eroare la crearea FileOutputStreamobiectului, atunci outputvariabila va fi nulă. Această posibilitate trebuie luată în considerare în finallybloc.

În al doilea rând, close()metoda este apelată întotdeauna în finallybloc, ceea ce înseamnă că nu este necesară în trybloc. Codul final va arăta astfel:

FileOutputStream output = null;

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

Chiar dacă nu luăm în considerare blocul catch, care poate fi omis, atunci cele 3 linii de cod ale noastre devin 10. Dar practic am deschis fișierul și am scris 1. Un pic greoi, nu crezi?


3. try-cu-resurse

Și aici creatorii lui Java au decis să ne stropească niște zahăr sintactic. Începând cu a 7-a versiune, Java are o nouă tryinstrucțiune -with-resources.

A fost creat tocmai pentru a rezolva problema cu apelul obligatoriu la close()metodă. Cazul general pare destul de simplu:

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

Aceasta este o altă variantă a try afirmației . Trebuie să adăugați paranteze după trycuvântul cheie și apoi să creați obiecte cu resurse externe în interiorul parantezelor. Pentru fiecare obiect din paranteze, compilatorul adaugă o finallysecțiune și un apel la close()metodă.

Mai jos sunt două exemple echivalente:

Cod lung Codați cu resurse de încercare
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);
}

Codul care utilizează try-with-resources este mult mai scurt și mai ușor de citit. Și cu cât avem mai puțin cod, cu atât sunt mai mici șansele de a face o greșeală de scriere sau altă eroare.

Apropo, putem adăuga catchși finallybloca trydeclarația -with-resources. Sau nu le puteți adăuga dacă nu sunt necesare.



4. Mai multe variabile în același timp

Apropo, este posibil să întâlniți adesea o situație în care trebuie să deschideți mai multe fișiere în același timp. Să presupunem că copiați un fișier, deci aveți nevoie de două obiecte: fișierul din care copiați datele și fișierul în care copiați datele.

În acest caz, tryinstrucțiunea -with-resources vă permite să creați unul, dar mai multe obiecte în ea. Codul care creează obiectele trebuie separat prin punct și virgulă. Iată aspectul general al acestei afirmații:

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

Exemplu de copiere a fișierelor:

Cod lung Cod scurt
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);
}

Ei bine, ce putem spune aici? try-cu-resurse este un lucru minunat!


5. AutoCloseableinterfata

Dar asta nu este tot. Cititorul atent va începe imediat să caute capcane care limitează modul în care această afirmație poate fi aplicată.

Dar cum tryfuncționează instrucțiunea -with-resources dacă clasa nu are o close()metodă? Ei bine, să presupunem că nu se va numi nimic. Nicio metodă, nicio problemă.

Dar cum tryfuncționează instrucțiunea -with-resources dacă clasa are mai multe close()metode? Și au nevoie de argumente care să le fie transmise? Și clasa nu are o close()metodă fără parametri?

Sper că ți-ai pus cu adevărat aceste întrebări, și poate încă altele.

Pentru a evita astfel de probleme, creatorii Java au venit cu o interfață specială numită AutoCloseable, care are o singură metodă - close(), care nu are parametri.

Ei au adăugat, de asemenea, restricția conform căreia numai obiectele claselor implementateAutoCloseable pot fi declarate ca resurse într-o tryinstrucțiune -with-resources. Ca rezultat, astfel de obiecte vor avea întotdeauna o close()metodă fără parametri.

Apropo, credeți că este posibil ca o tryinstrucțiune -with-resources să declare ca resursă un obiect a cărui clasă are propria close()metodă fără parametri dar care nu o implementează AutoCloseable?

Vestea proastă: răspunsul corect este nu — clasele trebuie să implementeze interfața AutoCloseable.

Vestea bună: Java are o mulțime de clase care implementează această interfață, așa că este foarte probabil ca totul să funcționeze așa cum ar trebui.