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:

Prova-med-resurser uttalande

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
String path = "c:\\projects\\log.txt";
FileOutputStream output = new FileOutputStream(path);
output.write(1);
output.close();
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- finallyblock och lägga till close()metoden i finallyblocket. 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 outputvariabeln deklareras inuti try {}blocket och därför inte är synlig i finallyblocket.

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, FileOutputStreamoch 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 FileOutputStreamobjektet skapas, outputkommer variabeln att vara null. Denna möjlighet måste redovisas i finallyblocket.

För det andra close()anropas metoden alltid i finallyblocket, vilket betyder att den inte är nödvändig i tryblocket. 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 catchblocket, 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 trynyckelordet och sedan skapa objekt med externa resurser inom parentesen. För varje objekt inom parentes lägger kompilatorn till en finallysektion och ett anrop till close()metoden.

Nedan finns två likvärdiga exempel:

Lång kod Koda med prova-med-resurser
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);
}

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 catchoch finallyblockera 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 trylå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
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);
}

Vad kan vi säga här? try-med-resurser är en underbar sak!


5. AutoCloseablegrä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 tryfungerar -with-resources-satsen om klassen inte har en close()metod? Tja, anta att ingenting kommer att kallas. Ingen metod, inga problem.

Men hur tryfungerar -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 , AutoCloseablesom 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.