1. Eksterne ressurser
Når et Java-program kjører, samhandler det noen ganger med enheter utenfor Java-maskinen. For eksempel med filer på disk. Disse enhetene kalles vanligvis eksterne ressurser. Interne ressurser er objektene som er opprettet inne i Java-maskinen.
Vanligvis følger interaksjonen denne ordningen:
Sporingsressurser
Operativsystemet holder nøye oversikt over ressursene som er tilgjengelige, og kontrollerer også delt tilgang til dem fra forskjellige programmer. For eksempel, hvis ett program endrer en fil, kan ikke et annet program endre (eller slette) den filen. Dette prinsippet er ikke begrenset til filer, men de gir det lettest forståelige eksemplet.
Operativsystemet har funksjoner (API) som lar et program anskaffe og/eller frigjøre ressurser. Hvis en ressurs er opptatt, kan bare programmet som har anskaffet den jobbe med den. Hvis en ressurs er gratis, kan ethvert program skaffe den.
Tenk deg at kontoret ditt har delte kaffekrus. Hvis noen tar et krus, kan ikke andre ta det lenger. Men når kruset er brukt, vasket og satt tilbake på plass, kan hvem som helst ta det igjen. Situasjonen med seter på buss eller T-bane er den samme. Hvis et sete er ledig, kan hvem som helst ta det. Hvis et sete er opptatt, kontrolleres det av personen som tok det.
Anskaffe eksterne ressurser .
Hver gang Java-programmet ditt begynner å jobbe med en fil på disken, ber Java-maskinen operativsystemet om eksklusiv tilgang til den. Hvis ressursen er ledig, kjøper Java-maskinen den.
Men etter at du er ferdig med å jobbe med filen, må denne ressursen (filen) frigis, dvs. du må varsle operativsystemet om at du ikke lenger trenger den. Hvis du ikke gjør dette, vil ressursen fortsatt holdes av programmet ditt.
Operativsystemet opprettholder en liste over ressurser som er okkupert av hvert kjørende program. Hvis programmet ditt overskrider den tildelte ressursgrensen, vil operativsystemet ikke lenger gi deg nye ressurser.
Den gode nyheten er at hvis programmet avsluttes, frigjøres alle ressursene automatisk (operativsystemet selv gjør dette).
Den dårlige nyheten er at hvis du skriver en serverapplikasjon (og mange serverapplikasjoner er skrevet i Java), må serveren din kunne kjøre i dager, uker og måneder uten å stoppe. Og hvis du åpner 100 filer om dagen og ikke lukker dem, vil applikasjonen i løpet av et par uker nå ressursgrensen og krasje. Det er langt fra måneder med stabilt arbeid.
2. close()
metode
Klasser som bruker eksterne ressurser har en spesiell metode for å frigjøre dem: close()
.
Nedenfor gir vi et eksempel på et program som skriver noe til en fil og så lukker filen når den er ferdig, dvs. at det frigjør operativsystemets ressurser. Det ser omtrent slik ut:
Kode | Merk |
---|---|
|
Banen til filen. Hent filobjektet: hent ressursen. Skriv til filen Lukk filen - slipp ressursen |
Etter å ha jobbet med en fil (eller andre eksterne ressurser), må du kalle close()
metoden på objektet som er knyttet til den eksterne ressursen.
Unntak
Det hele virker enkelt. Men unntak kan forekomme når et program kjører, og den eksterne ressursen vil ikke bli frigitt. Og det er veldig dårlig.
For å sikre at close()
metoden alltid kalles, må vi pakke inn koden vår i en try
- catch
- finally
blokk og legge close()
metoden til finally
blokken. Det vil se omtrent slik ut:
try
{
FileOutputStream output = new FileOutputStream(path);
output.write(1);
output.close();
}
catch (IOException e)
{
e.printStackTrace();
}
finally
{
output.close();
}
Denne koden vil ikke kompilere, fordi output
variabelen er deklarert inne i try {}
blokken, og derfor ikke er synlig i blokken finally
.
La oss fikse det:
FileOutputStream output = new FileOutputStream(path);
try
{
output.write(1);
output.close();
}
catch (IOException e)
{
e.printStackTrace();
}
finally
{
output.close();
}
Det er greit, men det vil ikke fungere hvis det oppstår en feil når vi oppretter objektet FileOutputStream
, og dette kan skje ganske enkelt.
La oss fikse det:
FileOutputStream output = null;
try
{
output = new FileOutputStream(path);
output.write(1);
output.close();
}
catch (IOException e)
{
e.printStackTrace();
}
finally
{
output.close();
}
Det er fortsatt noen kritikk. For det første, hvis det oppstår en feil når du oppretter objektet FileOutputStream
, output
vil variabelen være null. Denne muligheten må redegjøres for i finally
blokken.
For det andre close()
kalles metoden alltid i finally
blokken, noe som betyr at den ikke er nødvendig i try
blokken. Den endelige koden vil se slik ut:
FileOutputStream output = null;
try
{
output = new FileOutputStream(path);
output.write(1);
}
catch (IOException e)
{
e.printStackTrace();
}
finally
{
if (output != null)
output.close();
}
Selv om vi ikke vurderer catch
blokken, som kan utelates, så blir våre 3 linjer med kode 10. Men vi åpnet i grunnen bare filen og skrev 1. Litt tungvint, synes du ikke?
3. try
-med-ressurser
Og her bestemte Javas skapere å strø litt syntaktisk sukker på oss. Fra og med sin 7. versjon har Java en ny try
-with-resources-setning.
Den ble opprettet nettopp for å løse problemet med det obligatoriske kallet til metoden close()
. Den generelle saken ser ganske enkel ut:
try (ClassName name = new ClassName())
{
Code that works with the name variable
}
Dette er en annen variant av try
uttalelsen . Du må legge til parenteser etter try
nøkkelordet, og deretter opprette objekter med eksterne ressurser inne i parentesene. For hvert objekt i parentes legger kompilatoren til en finally
seksjon og et kall til close()
metoden.
Nedenfor er to tilsvarende eksempler:
Lang kode | Kode med prøv-med-ressurser |
---|---|
|
|
Koden som bruker try
-med-ressurser er mye kortere og lettere å lese. Og jo mindre kode vi har, jo mindre sjanse for å gjøre en skrivefeil eller annen feil.
Forresten, vi kan legge til catch
og finally
blokkere til try
-with-resources-setningen. Eller du kan ikke legge dem til hvis de ikke er nødvendige.
4. Flere variabler samtidig
Forresten, du kan ofte støte på en situasjon når du trenger å åpne flere filer samtidig. La oss si at du kopierer en fil, så du trenger to objekter: filen du kopierer data fra og filen du kopierer data til.
I dette tilfellet lar try
-with-resources-setningen deg lage ett men flere objekter i den. Koden som lager objektene må skilles med semikolon. Her er det generelle utseendet til denne uttalelsen:
try (ClassName name = new ClassName(); ClassName2 name2 = new ClassName2())
{
Code that works with the name and name2 variables
}
Eksempel på kopiering av filer:
Lang kode | Kort kode |
---|---|
|
|
Vel, hva kan vi si her? try
-med-ressurser er en fantastisk ting!
5. AutoCloseable
grensesnitt
Men det er ikke alt. Den oppmerksomme leseren vil umiddelbart begynne å se etter fallgruver som begrenser hvordan dette utsagnet kan brukes.
Men hvordan try
fungerer -with-resources-setningen hvis klassen ikke har en close()
metode? Vel, anta at ingenting vil bli kalt. Ingen metode, ikke noe problem.
Men hvordan try
fungerer -with-resources-setningen hvis klassen har flere close()
metoder? Og de trenger argumenter for å bli sendt til dem? Og klassen har ikke en close()
metode uten parametere?
Jeg håper du virkelig stilte deg selv disse spørsmålene, og kanskje flere andre.
For å unngå slike problemer kom Javas skapere med et spesielt grensesnitt kalt AutoCloseable
, som bare har én metode - close()
, som ikke har noen parametere.
De la også til begrensningen at bare objekter av klasser som implementererAutoCloseable
kan deklareres som ressurser i en try
-with-resources-setning. Som et resultat vil slike objekter alltid ha en close()
metode uten parametere.
Tror du forresten det er mulig for en try
-with-resources-setning å erklære som en ressurs et objekt hvis klasse har sin egen close()
metode uten parametere, men som ikke implementerer AutoCloseable
?
Den dårlige nyheten: Det riktige svaret er nei - klassene må implementere grensesnittet AutoCloseable
.
Den gode nyheten: Java har mange klasser som implementerer dette grensesnittet, så det er svært sannsynlig at alt vil fungere som det skal.