1. Externa resurser
När ett Java-program körs interagerar det ibland med enheter utanför Java-maskinen. Till exempel med filer på disk. Dessa enheter kallas vanligtvis externa resurser. Interna resurser är de objekt som skapas inuti Java-maskinen.
Vanligtvis följer interaktionen detta schema:
Spårningsresurser
Operativsystemet håller noggrant reda på tillgängliga resurser och kontrollerar även delad åtkomst till dem från olika program. Till exempel, om ett program ändrar en fil, kan ett annat program inte ändra (eller ta bort) den filen. Denna princip är inte begränsad till filer, men de är det mest lättbegripliga exemplet.
Operativsystemet har funktioner (API) som gör att ett program kan förvärva och/eller släppa resurser. Om en resurs är upptagen kan bara programmet som skaffat den arbeta med den. Om en resurs är gratis kan vilket program som helst skaffa den.
Föreställ dig att ditt kontor har delade kaffemuggar. Om någon tar en mugg, kan andra människor inte längre ta den. Men när muggen väl har använts, tvättats och satts tillbaka på sin plats, då kan vem som helst ta den igen. Situationen med sittplatser på en buss eller tunnelbana är densamma. Om en plats är ledig kan vem som helst ta den. Om en plats är upptagen kontrolleras den av personen som tog den.
Skaffa externa resurser .
Varje gång ditt Java-program börjar arbeta med en fil på disken, ber Java-maskinen operativsystemet om exklusiv åtkomst till den. Om resursen är ledig, förvärvar Java-maskinen den.
Men efter att du har arbetat klart med filen måste denna resurs (fil) släppas, dvs du måste meddela operativsystemet att du inte längre behöver den. Om du inte gör detta kommer resursen att fortsätta att hållas av ditt program.
Operativsystemet har en lista över resurser som upptas av varje program som körs. Om ditt program överskrider den tilldelade resursgränsen kommer operativsystemet inte längre att ge dig nya resurser.
Den goda nyheten är att om ditt program avslutas släpps alla resurser automatiskt (operativsystemet själv gör detta).
Den dåliga nyheten är att om du skriver en serverapplikation (och många serverapplikationer är skrivna i Java), måste din server kunna köras i dagar, veckor och månader utan att stanna. Och om du öppnar 100 filer om dagen och inte stänger dem, kommer din applikation att nå sin resursgräns inom ett par veckor och krascha. Det är långt ifrån månader av stabilt arbete.
2. close()
metod
Klasser som använder externa resurser har en speciell metod för att frigöra dem: close()
.
Nedan ger vi ett exempel på ett program som skriver något till en fil och sedan stänger filen när det är klart, dvs det frigör operativsystemets resurser. Det ser ut ungefär så här:
Koda | Notera |
---|---|
|
Sökvägen till filen. Hämta filobjektet: skaffa resursen. Skriv till filen Stäng filen - släpp resursen |
Efter att ha arbetat med en fil (eller andra externa resurser) måste du anropa close()
metoden på objektet som är länkat till den externa resursen.
Undantag
Det hela verkar enkelt. Men undantag kan inträffa när ett program körs, och den externa resursen kommer inte att släppas. Och det är väldigt dåligt.
För att säkerställa att close()
metoden alltid anropas måste vi linda in vår kod i ett try
- catch
- finally
block och lägga till close()
metoden i finally
blocket. Det kommer att se ut ungefär så här:
try
{
FileOutputStream output = new FileOutputStream(path);
output.write(1);
output.close();
}
catch (IOException e)
{
e.printStackTrace();
}
finally
{
output.close();
}
Denna kod kommer inte att kompileras, eftersom output
variabeln deklareras inuti try {}
blocket och därför inte är synlig i finally
blocket.
Låt oss fixa det:
FileOutputStream output = new FileOutputStream(path);
try
{
output.write(1);
output.close();
}
catch (IOException e)
{
e.printStackTrace();
}
finally
{
output.close();
}
Det är okej, men det kommer inte att fungera om ett fel uppstår när vi skapar objektet, FileOutputStream
och det kan hända ganska enkelt.
Låt oss fixa det:
FileOutputStream output = null;
try
{
output = new FileOutputStream(path);
output.write(1);
output.close();
}
catch (IOException e)
{
e.printStackTrace();
}
finally
{
output.close();
}
Det finns fortfarande en del kritik. För det första, om ett fel uppstår när FileOutputStream
objektet skapas, output
kommer variabeln att vara null. Denna möjlighet måste redovisas i finally
blocket.
För det andra close()
anropas metoden alltid i finally
blocket, vilket betyder att den inte är nödvändig i try
blocket. Den slutliga koden kommer att se ut så här:
FileOutputStream output = null;
try
{
output = new FileOutputStream(path);
output.write(1);
}
catch (IOException e)
{
e.printStackTrace();
}
finally
{
if (output != null)
output.close();
}
Även om vi inte överväger catch
blocket, som kan utelämnas, så blir våra 3 rader kod 10. Men vi öppnade i princip bara filen och skrev 1. Lite krångligt, tycker du inte?
3. try
-med-resurser
Och här bestämde sig Javas skapare för att strö lite syntaktisk socker på oss. Från och med sin sjunde version har Java en ny try
-with-resources-sats.
Den skapades just för att lösa problemet med det obligatoriska anropet till close()
metoden. Det allmänna fallet ser ganska enkelt ut:
try (ClassName name = new ClassName())
{
Code that works with the name variable
}
Detta är en annan variant av try
uttalandet . Du måste lägga till parenteser efter try
nyckelordet och sedan skapa objekt med externa resurser inom parentesen. För varje objekt inom parentes lägger kompilatorn till en finally
sektion och ett anrop till close()
metoden.
Nedan finns två likvärdiga exempel:
Lång kod | Koda med prova-med-resurser |
---|---|
|
|
Koden som använder try
-with-resources är mycket kortare och lättare att läsa. Och ju mindre kod vi har, desto mindre chans att göra ett stavfel eller annat fel.
Förresten, vi kan lägga till catch
och finally
blockera till try
-with-resources-satsen. Eller så kan du inte lägga till dem om de inte behövs.
4. Flera variabler samtidigt
Förresten, du kan ofta stöta på en situation när du behöver öppna flera filer samtidigt. Låt oss säga att du kopierar en fil, så du behöver två objekt: filen som du kopierar data från och filen som du kopierar data till.
I det här fallet try
låter -with-resources-satsen dig skapa ett men flera objekt i den. Koden som skapar objekten måste separeras med semikolon. Här är det allmänna utseendet på detta uttalande:
try (ClassName name = new ClassName(); ClassName2 name2 = new ClassName2())
{
Code that works with the name and name2 variables
}
Exempel på kopiering av filer:
Lång kod | Kort kod |
---|---|
|
|
Vad kan vi säga här? try
-med-resurser är en underbar sak!
5. AutoCloseable
gränssnitt
Men det är inte allt. Den uppmärksamma läsaren kommer omedelbart att börja leta efter fallgropar som begränsar hur detta påstående kan tillämpas.
Men hur try
fungerar -with-resources-satsen om klassen inte har en close()
metod? Tja, anta att ingenting kommer att kallas. Ingen metod, inga problem.
Men hur try
fungerar -with-resources-satsen om klassen har flera close()
metoder? Och de behöver argument för att skickas till dem? Och klassen har ingen close()
metod utan parametrar?
Jag hoppas att du verkligen ställde dessa frågor till dig själv, och kanske ytterligare andra.
För att undvika sådana problem kom Javas skapare med ett speciellt gränssnitt som heter , AutoCloseable
som bara har en metod — , close()
som inte har några parametrar.
De lade också till begränsningen att endast objekt av klasser som implementerarAutoCloseable
kan deklareras som resurser i en try
-with-resources-sats. Som ett resultat kommer sådana objekt alltid att ha en close()
metod utan parametrar.
Tror du förresten att det är möjligt för en try
-with-resources-sats att deklarera som en resurs ett objekt vars klass har sin egen close()
metod utan parametrar men som inte implementerar AutoCloseable
?
De dåliga nyheterna: Det korrekta svaret är nej – klasserna måste implementera gränssnittet AutoCloseable
.
De goda nyheterna: Java har många klasser som implementerar detta gränssnitt, så det är mycket troligt att allt kommer att fungera som det ska.
GO TO FULL VERSION