„Amigo, Zehn-Hütte!“

„Ich freue mich, Java zu lernen, Captain!“

„Beruhigen Sie sich, Amigo. Heute haben wir ein superinteressantes Thema. Wir werden darüber sprechen, wie ein Java-Programm mit externen Ressourcen interagiert, und wir werden eine sehr interessante Java-Anweisung studieren. Halten Sie sich besser nicht die Ohren zu.“

"Ich bin ganz Ohr."

„Wenn ein Java-Programm ausgeführt wird, interagiert es manchmal mit Entitäten außerhalb der Java-Maschine. Zum Beispiel mit Dateien auf der Festplatte. Diese Entitäten werden normalerweise als externe Ressourcen bezeichnet.“

„Was gelten dann als interne Ressourcen?“

„Interne Ressourcen sind die Objekte, die innerhalb der Java-Maschine erstellt werden. Typischerweise folgt die Interaktion diesem Schema:

Try-with-resources-Anweisung

„Das Betriebssystem verfolgt strikt die verfügbaren Ressourcen und kontrolliert auch den gemeinsamen Zugriff darauf von verschiedenen Programmen aus. Wenn beispielsweise ein Programm eine Datei ändert, kann ein anderes Programm diese Datei nicht ändern (oder löschen). Dieses Prinzip gilt nicht.“ sind auf Dateien beschränkt, bieten aber das am besten verständliche Beispiel.

„Das Betriebssystem verfügt über Funktionen (APIs), die es einem Programm ermöglichen, Ressourcen zu erwerben und/oder freizugeben. Wenn eine Ressource ausgelastet ist, kann nur das Programm, das sie erworben hat, damit arbeiten. Wenn eine Ressource frei ist, kann jedes Programm sie erwerben.“ Es.

„Stellen Sie sich vor, dass in einem Büro Kaffeetassen geteilt werden. Wenn jemand eine Tasse nimmt, können die anderen sie nicht mehr nehmen. Aber sobald die Tasse benutzt, gewaschen und wieder an ihren Platz gestellt wird, kann sie jeder wieder nehmen.“

„Verstanden. Es ist wie mit Sitzplätzen in der U-Bahn oder anderen öffentlichen Verkehrsmitteln. Wenn ein Sitzplatz frei ist, kann ihn jeder nehmen. Wenn ein Sitzplatz belegt ist, wird er von der Person kontrolliert, die ihn genommen hat.“

„Das ist richtig. Und jetzt reden wir über den Erwerb externer Ressourcen . Jedes Mal, wenn Ihr Java-Programm beginnt, mit einer Datei auf der Festplatte zu arbeiten, bittet die Java-Maschine das Betriebssystem um exklusiven Zugriff darauf. Wenn die Ressource frei ist, erwirbt die Java-Maschine Es.

„Aber nachdem Sie die Arbeit mit der Datei beendet haben, muss diese Ressource (Datei) freigegeben werden, d. h. Sie müssen dem Betriebssystem mitteilen, dass Sie sie nicht mehr benötigen. Wenn Sie dies nicht tun, bleibt die Ressource weiterhin bestehen.“ von Ihrem Programm gehalten.

"Das klingt fair."

„Damit das so bleibt, führt das Betriebssystem eine Liste der von jedem laufenden Programm belegten Ressourcen. Wenn Ihr Programm das zugewiesene Ressourcenlimit überschreitet, stellt Ihnen das Betriebssystem keine neuen Ressourcen mehr zur Verfügung.

„Es ist wie bei Programmen, die den gesamten Speicher verschlingen können …“

„So etwas in der Art. Die gute Nachricht ist, dass alle Ressourcen automatisch freigegeben werden, wenn Ihr Programm beendet wird (das Betriebssystem selbst erledigt dies).“

„Wenn das die guten Nachrichten sind, bedeutet das dann, dass es auch schlechte Nachrichten gibt?“

„Genau so. Die schlechte Nachricht ist, dass, wenn Sie eine Serveranwendung schreiben …“

„Aber schreibe ich solche Bewerbungen?“

„Viele Serveranwendungen sind in Java geschrieben, daher werden Sie sie höchstwahrscheinlich für die Arbeit schreiben. Wie ich bereits sagte: Wenn Sie eine Serveranwendung schreiben, muss Ihr Server tagelang, wochenlang, monatelang ununterbrochen laufen. usw."

„Mit anderen Worten: Das Programm wird nicht beendet, und das bedeutet, dass der Speicher nicht automatisch freigegeben wird.“

„Genau. Und wenn Sie täglich 100 Dateien öffnen und nicht schließen, erreicht Ihre Anwendung in ein paar Wochen ihre Ressourcengrenze und stürzt ab.“

„Das bleibt weit hinter monatelanger stabiler Arbeit zurück! Was kann getan werden?“

„Klassen, die externe Ressourcen verwenden, verfügen über eine spezielle Methode, um diese freizugeben: close().

„Hier ist ein Beispiel für ein Programm, das etwas in eine Datei schreibt und die Datei dann schließt, wenn es fertig ist, also die Ressourcen des Betriebssystems freigibt. Es sieht ungefähr so ​​aus:

Code Notiz
String path = "c:\\projects\\log.txt";
FileOutputStream output = new FileOutputStream(path);
output.write(1);
output.close();
Der Pfad zur Datei.
Holen Sie sich das Dateiobjekt: Erwerben Sie die Ressource.
In die Datei schreiben.
Die Datei schließen – die Ressource freigeben

„Ah... Also, nachdem ich mit einer Datei (oder anderen externen Ressourcen) gearbeitet habe, muss ich die close()Methode für das Objekt aufrufen, das mit der externen Ressource verknüpft ist.“

„Ja. Es scheint alles einfach zu sein. Aber während der Ausführung eines Programms können Ausnahmen auftreten und die externe Ressource wird nicht freigegeben.“

„Und das ist sehr schlimm. Was tun?“

„Um sicherzustellen, dass die close()Methode immer aufgerufen wird, müssen wir unseren Code in einen --Block einschließen tryund die Methode zum Block hinzufügen. Es sieht catchin etwa so aus:finallyclose()finally

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

„Hmm... Etwas stimmt hier nicht?“

„Richtig. Dieser Code lässt sich nicht kompilieren, da die outputVariable innerhalb des Blocks deklariert wird try{}und daher im finallyBlock nicht sichtbar ist.

Lassen Sie es uns beheben:

FileOutputStream output = new FileOutputStream(path);

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

„Ist jetzt alles in Ordnung?“

„Es ist in Ordnung, aber es funktioniert nicht, wenn beim Erstellen des FileOutputStreamObjekts ein Fehler auftritt, und das kann ganz einfach passieren.“

Lassen Sie es uns beheben:

FileOutputStream output = null;

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

„Und funktioniert jetzt alles?“

„Es gibt noch ein paar Kritikpunkte. Erstens: Wenn beim Erstellen des FileOutputStreamObjekts ein Fehler auftritt, outputist die Variable null. Diese Möglichkeit muss im finallyBlock berücksichtigt werden.“

„Zweitens wird die close()Methode immer im finallyBlock aufgerufen, was bedeutet, dass sie im tryBlock nicht notwendig ist. Der endgültige Code sieht so aus:

FileOutputStream output = null;

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

„Selbst wenn wir den Block nicht berücksichtigen catch, der weggelassen werden kann, werden aus unseren drei Codezeilen zehn. Aber im Grunde haben wir einfach die Datei geöffnet und eine geschrieben.“

„Puh... Das ist doch gut, dass die Sache abgeschlossen ist. Relativ verständlich, aber doch etwas langweilig, oder?“

„So ist es. Deshalb haben uns die Java-Entwickler geholfen, indem sie etwas syntaktischen Zucker hinzugefügt haben. Kommen wir nun zum Höhepunkt des Programms, oder besser gesagt, dieser Lektion:

try-mit-Ressourcen

„Ab der 7. Version hat Java eine neue try-with-resources-Anweisung.

„Es wurde genau dafür geschaffen, das Problem mit dem obligatorischen Aufruf der close()Methode zu lösen.“

„Das klingt vielversprechend!“

„Der allgemeine Fall sieht ganz einfach aus:

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

„Das ist also eine weitere Variation der try Aussage ?“

„Ja. Sie müssen nach dem trySchlüsselwort Klammern hinzufügen und dann Objekte mit externen Ressourcen innerhalb der Klammern erstellen. Für jedes Objekt in den Klammern fügt der Compiler einen finallyAbschnitt und einen Aufruf der close()Methode hinzu.

„Im Folgenden finden Sie zwei gleichwertige Beispiele:

Langer Code Code mit Try-with-Ressourcen
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);
}

„Cool! Der Code, der try-with-resources verwendet, ist viel kürzer und einfacher zu lesen. Und je weniger Code wir haben, desto geringer ist die Wahrscheinlichkeit, dass wir einen Tippfehler oder einen anderen Fehler machen.“

„Ich freue mich, dass es Ihnen gefällt. Übrigens können wir der -with-resources-Anweisung Blöcke hinzufügen. Oder Sie können sie nicht hinzufügen, wenn sie nicht benötigt werden catch.finallytry

Mehrere Variablen gleichzeitig

„Es kommt häufig vor, dass Sie mehrere Dateien gleichzeitig öffnen müssen. Nehmen wir an, Sie kopieren eine Datei und benötigen daher zwei Objekte: die Datei, aus der Sie Daten kopieren, und die Datei, in die Sie Daten kopieren .

„In diesem Fall trykönnen Sie mit der -with-resources-Anweisung ein, aber mehrere Objekte darin erstellen. Der Code, der die Objekte erstellt, muss durch Semikolons getrennt werden. Hier ist das allgemeine Erscheinungsbild dieser Anweisung:

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

Beispiel für das Kopieren von Dateien:

Kurzcode Langer Code
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();
}

„Nun, was können wir hier sagen? try-with-resources ist eine wunderbare Sache!“

„Was wir sagen können ist, dass wir es nutzen sollten.“