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ă:
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ă |
---|---|
|
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 finally
bloc. 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 output
variabila este declarată în interiorul try {}
blocului și, prin urmare, nu este vizibilă în finally
bloc.
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 FileOutputStream
obiectului, atunci output
variabila va fi nulă. Această posibilitate trebuie luată în considerare în finally
bloc.
În al doilea rând, close()
metoda este apelată întotdeauna în finally
bloc, ceea ce înseamnă că nu este necesară în try
bloc. 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ă try
instrucț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ă try
cuvântul cheie și apoi să creați obiecte cu resurse externe în interiorul parantezelor. Pentru fiecare obiect din paranteze, compilatorul adaugă o finally
secțiune și un apel la close()
metodă.
Mai jos sunt două exemple echivalente:
Cod lung | Codați cu resurse de încercare |
---|---|
|
|
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 finally
bloca try
declaraț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, try
instrucț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 |
---|---|
|
|
Ei bine, ce putem spune aici? try
-cu-resurse este un lucru minunat!
5. AutoCloseable
interfata
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 try
funcț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 try
funcț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 try
instrucț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 try
instrucț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.
GO TO FULL VERSION