"Amigo, tiohydda!"

"Jag är glad över att lära mig Java, kapten!"

"Lugn, Amigo. Idag har vi ett superintressant ämne. Vi kommer att prata om hur ett Java-program interagerar med externa resurser och vi kommer att studera ett mycket intressant Java-uttalande. Bättre att inte hålla för öronen."

"Jag lyssnar."

"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."

"Vad anses då vara interna resurser?"

"Interna resurser är de objekt som skapas inuti Java-maskinen. Vanligtvis följer interaktionen detta schema:

Prova-med-resurser uttalande

"Operativsystemet håller noggrant reda på de tillgängliga resurserna och kontrollerar också 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änsade 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 ledig kan vilket program som helst förvärva Det.

"Föreställ dig att ett kontor har delade kaffemuggar. Om någon tar en mugg, då kan andra människor inte längre ta den. Men när muggen väl är använd, tvättad och satt tillbaka på sin plats, då kan vem som helst ta den igen."

"Förstår det. Det är som platser på tunnelbanan eller annan kollektivtrafik. Om en plats är ledig kan vem som helst ta den. Om en plats är upptagen så kontrolleras den av personen som tog den."

"Det stämmer. Och nu ska vi prata om att 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, hämtar Java-maskinen Det.

"Men efter att du har arbetat klart med filen måste den här resursen (filen) 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 vara innehas av ditt program."

"Det låter rättvist."

"För att behålla det så, upprätthåller operativsystemet 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.

"Det är som program som kan äta upp allt minne..."

"Något sådant. Den goda nyheten är att om ditt program avslutas släpps alla resurser automatiskt (operativsystemet självt gör detta)."

"Om det är de goda nyheterna, betyder det att det finns dåliga nyheter?"

"Precis så. De dåliga nyheterna är att om du skriver en serverapplikation..."

"Men skriver jag sådana ansökningar?"

"Många serverapplikationer är skrivna i Java, så troligtvis kommer du att skriva dem för jobbet. Som jag sa, om du skriver en serverapplikation måste din server köras non-stop i dagar, veckor, månader, etc."

"Med andra ord, programmet avslutas inte, och det betyder att minnet inte släpps automatiskt."

"Precis. Och om du öppnar 100 filer om dagen och inte stänger dem, så kommer din applikation om ett par veckor att nå sin resursgräns och krascha."

"Det är långt ifrån månader av stabilt arbete! Vad kan göras?"

"Klasser som använder externa resurser har en speciell metod för att frigöra dem: close().

"Här är ett exempel på ett program som skriver något till en fil och sedan stänger filen när den är klar, dvs det frigör operativsystemets resurser. Det ser ungefär ut 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

"Ah... Så, efter att ha arbetat med en fil (eller andra externa resurser), måste jag anropa metoden close()på objektet som är länkat till den externa resursen."

"Ja. Allt 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. Vad ska man göra?"

"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();
}

"Hmm... Något är fel här?"

"Rätt. Den här koden 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();
}

"Är allt okej nu?"

"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();
}

"Och fungerar allt nu?"

"Det finns fortfarande några kritiker. För det första, om ett fel uppstår när objektet skapas, FileOutputStreamkommer outputvariabeln att vara null. Denna möjlighet måste tas med i blocket finally.

"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."

"Phh... Det är bra som avslutar saken. Relativt förståeligt, men lite tråkigt, eller hur?"

"Så är det. Det var därför Javas skapare hjälpte oss genom att lägga till lite syntaktisk socker. Låt oss nu gå vidare till programmets höjdpunkt, eller snarare, den här lektionen:

try-med-resurser

"Från och med sin sjunde version har Java en ny try-with-resources-satsning.

"Den skapades just för att lösa problemet med det obligatoriska anropet till metoden. close()"

"Det låter lovande!"

"Det allmänna fallet ser ganska enkelt ut:

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

"Så detta är en annan variant av try uttalandet ?"

"Ja. Du måste lägga till parenteser efter nyckelordet tryoch sedan skapa objekt med externa resurser inom parentesen. För varje objekt inom parentesen lägger kompilatorn till en finallysektion och ett anrop till close()metoden.

"Nedan är 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);
}

"Coolt! Koden som använder try-med-resurser ä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."

"Jag är glad att du gillar det. Förresten, vi kan lägga till catchoch finallyblockera i try-med-resurser-satsen. Eller så kan du inte lägga till dem om de inte behövs.

Flera variabler samtidigt

"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 till vilken du kopierar data .

"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å denna sats:

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

Exempel på kopiering av filer:

Kort kod Lång kod
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);
}
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();
}

"Tja, vad kan vi säga här? try-med-resurser är en underbar sak!"

"Vad vi kan säga är att vi borde använda den."