1. Externe middelen

Terwijl een Java-programma wordt uitgevoerd, communiceert het soms met entiteiten buiten de Java-machine. Bijvoorbeeld met bestanden op schijf. Deze entiteiten worden meestal externe bronnen genoemd. Interne bronnen zijn de objecten die in de Java-machine zijn gemaakt.

Meestal volgt de interactie dit schema:

Proberen-met-bronnen-verklaring

Bronnen volgen

Het besturingssysteem houdt nauwgezet de beschikbare bronnen bij en regelt ook de gedeelde toegang ertoe vanuit verschillende programma's. Als het ene programma bijvoorbeeld een bestand wijzigt, kan een ander programma dat bestand niet wijzigen (of verwijderen). Dit principe is niet beperkt tot bestanden, maar ze bieden het gemakkelijkst te begrijpen voorbeeld.

Het besturingssysteem heeft functies (API's) waarmee een programma bronnen kan verwerven en/of vrijgeven. Als een bron bezet is, kan alleen het programma dat het heeft verkregen ermee werken. Als een bron gratis is, kan elk programma deze verkrijgen.

Stel je voor dat je kantoor gedeelde koffiemokken heeft. Als iemand een mok pakt, kunnen andere mensen hem niet meer pakken. Maar als de mok eenmaal is gebruikt, gewassen en weer op zijn plaats is gezet, kan iedereen hem weer meenemen. De situatie met zitplaatsen in een bus of metro is hetzelfde. Als er een stoel vrij is, kan iedereen die plaatsen. Als een stoel bezet is, wordt deze gecontroleerd door de persoon die deze heeft ingenomen.

Acquisitie van externe middelen .

Elke keer dat uw Java-programma begint te werken met een bestand op schijf, vraagt ​​de Java-machine het besturingssysteem om exclusieve toegang daartoe. Als de bron vrij is, verwerft de Java-machine deze.

Maar nadat u klaar bent met het werken met het bestand, moet deze bron (bestand) worden vrijgegeven, dwz u moet het besturingssysteem laten weten dat u het niet langer nodig hebt. Als u dit niet doet, blijft de bron in het bezit van uw programma.

Het besturingssysteem houdt een lijst bij met bronnen die worden gebruikt door elk actief programma. Als uw programma de toegewezen resourcelimiet overschrijdt, geeft het besturingssysteem u geen nieuwe resources meer.

Het goede nieuws is dat als uw programma wordt beëindigd, alle bronnen automatisch worden vrijgegeven (het besturingssysteem zelf doet dit).

Het slechte nieuws is dat als je een servertoepassing schrijft (en veel servertoepassingen worden in Java geschreven), je server dagen, weken en maanden moet kunnen draaien zonder te stoppen. En als u 100 bestanden per dag opent en ze niet sluit, bereikt uw applicatie binnen een paar weken de limiet van de beschikbare middelen en crasht. Dat is veel minder dan maandenlang stabiel werk.


2. close()methode

Klassen die externe bronnen gebruiken, hebben een speciale methode om ze vrij te geven: close().

Hieronder geven we een voorbeeld van een programma dat iets naar een bestand schrijft en het bestand vervolgens sluit wanneer het klaar is, dwz het maakt de bronnen van het besturingssysteem vrij. Het ziet er ongeveer zo uit:

Code Opmerking
String path = "c:\\projects\\log.txt";
FileOutputStream output = new FileOutputStream(path);
output.write(1);
output.close();
Het pad naar het bestand.
Haal het bestandsobject op: verkrijg de bron.
Schrijf naar het bestand
Sluit het bestand - geef de bron vrij

Nadat u met een bestand (of andere externe bronnen) hebt gewerkt, moet u de close()methode aanroepen op het object dat aan de externe bron is gekoppeld.

Uitzonderingen

Het lijkt allemaal eenvoudig. Maar er kunnen uitzonderingen optreden terwijl een programma wordt uitgevoerd en de externe bron wordt niet vrijgegeven. En dat is heel erg.

Om ervoor te zorgen dat de close()methode altijd wordt aangeroepen, moeten we onze code in een try- catch- finallyblok wikkelen en de close()methode aan het finallyblok toevoegen. Het zal er ongeveer zo uitzien:

try
{
   FileOutputStream output = new FileOutputStream(path);
   output.write(1);
   output.close();
}
catch (IOException e)
{
   e.printStackTrace();
}
finally
{
   output.close();
}

Deze code wordt niet gecompileerd, omdat de outputvariabele binnen het try {}blok wordt gedeclareerd en daarom niet zichtbaar is in het finallyblok.

Laten we het oplossen:

FileOutputStream output = new FileOutputStream(path);

try
{
   output.write(1);
   output.close();
}
catch (IOException e)
{
   e.printStackTrace();
}
finally
{
   output.close();
}

Het is oké, maar het werkt niet als er een fout optreedt wanneer we het FileOutputStreamobject maken, en dit kan vrij gemakkelijk gebeuren.

Laten we het oplossen:

FileOutputStream output = null;

try
{
   output = new FileOutputStream(path);
   output.write(1);
   output.close();
}
catch (IOException e)
{
   e.printStackTrace();
}
finally
{
   output.close();
}

Er zijn nog enkele punten van kritiek. Ten eerste, als er een fout optreedt bij het maken van het FileOutputStreamobject, outputis de variabele null. Met deze mogelijkheid moet rekening worden gehouden in het finallyblok.

Ten tweede wordt de close()methode altijd in het blok aangeroepen finally, wat betekent dat het niet nodig is in het tryblok. De uiteindelijke code ziet er als volgt uit:

FileOutputStream output = null;

try
{
   output = new FileOutputStream(path);
   output.write(1);
}
catch (IOException e)
{
   e.printStackTrace();
}
finally
{
   if (output != null)
      output.close();
}

Zelfs als we geen rekening houden met het catchblok, dat weggelaten kan worden, dan worden onze 3 regels code 10. Maar we hebben eigenlijk gewoon het bestand geopend en 1 geschreven. Een beetje omslachtig, vind je niet?


3. try-met-middelen

En hier besloten de makers van Java om wat syntactische suiker over ons te strooien. Vanaf de 7e versie heeft Java een nieuwe try-with-resources-instructie.

Het is precies gemaakt om het probleem op te lossen met de verplichte aanroep van de close()methode. Het algemene geval ziet er vrij eenvoudig uit:

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

Dit is een andere variant van de try verklaring . U moet haakjes toevoegen achter het trytrefwoord en vervolgens objecten maken met externe bronnen tussen haakjes. Voor elk object tussen haakjes voegt de compiler een finallysectie en een aanroep toe aan de close()methode.

Hieronder staan ​​twee gelijkwaardige voorbeelden:

Lange code Codeer met try-with-resources
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);
}

De code die try-with-resources gebruikt, is veel korter en gemakkelijker te lezen. En hoe minder code we hebben, hoe kleiner de kans op een typefout of een andere fout.

Overigens kunnen we de instructie -with-resources toevoegen catchen blokkeren . Of u kunt ze niet toevoegen als ze niet nodig zijn.finallytry



4. Meerdere variabelen tegelijk

Overigens komt u vaak een situatie tegen waarin u meerdere bestanden tegelijkertijd moet openen. Stel dat u een bestand kopieert, dus u hebt twee objecten nodig: het bestand waaruit u gegevens kopieert en het bestand waarnaar u gegevens kopieert.

In dit geval trykunt u met de instructie -with-resources er één of meerdere objecten in maken. De code waarmee de objecten worden gemaakt, moet worden gescheiden door puntkomma's. Dit is het algemene uiterlijk van deze verklaring:

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

Voorbeeld van het kopiëren van bestanden:

Lange code Korte code
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);
}

Tja, wat kunnen we hier zeggen? try-met-middelen is iets geweldigs!


5. AutoCloseableinterface

Maar dat is niet alles. De oplettende lezer zal onmiddellijk op zoek gaan naar valkuilen die de toepassing van deze uitspraak beperken.

Maar hoe trywerkt de instructie -with-resources als de klasse geen close()methode heeft? Nou, stel dat er niets wordt gebeld. Geen methode, geen probleem.

Maar hoe trywerkt de instructie -with-resources als de klasse meerdere close()methoden heeft? En ze hebben argumenten nodig om aan hen te worden doorgegeven? En de klasse heeft geen close()methode zonder parameters?

Ik hoop dat je jezelf deze vragen echt hebt gesteld, en misschien nog wel andere.

Om dergelijke problemen te voorkomen, bedachten de makers van Java een speciale interface genaamd AutoCloseable, die slechts één methode heeft - close(), die geen parameters heeft.

Ze voegden ook de beperking toe dat alleen objecten van klassen die implementeren,AutoCloseable kunnen worden gedeclareerd als resources in een try-with-resources-instructie. Als gevolg hiervan hebben dergelijke objecten altijd een close()methode zonder parameters.

Trouwens, denk je dat het mogelijk is dat een try-with-resources statement een object declareert als een resource waarvan de klasse zijn eigen close()methode heeft zonder parameters maar die niet implementeert AutoCloseable?

Het slechte nieuws: het juiste antwoord is nee - de klassen moeten de AutoCloseableinterface implementeren.

Het goede nieuws: Java heeft veel klassen die deze interface implementeren, dus het is zeer waarschijnlijk dat alles zal werken zoals het hoort.