1. Externe Ressourcen
Während 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 üblicherweise als externe Ressourcen bezeichnet. Interne Ressourcen sind die in der Java-Maschine erstellten Objekte.
Typischerweise folgt die Interaktion diesem Schema:
Ressourcen verfolgen
Das Betriebssystem überwacht 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 ist nicht auf Dateien beschränkt, aber sie bieten 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 sie von jedem Programm erworben werden.
Stellen Sie sich vor, dass in Ihrem Büro Kaffeetassen geteilt werden. Wenn jemand einen Becher nimmt, können andere ihn nicht mehr nehmen. Aber sobald der Becher benutzt, gewaschen und wieder an seinen Platz gestellt wird, kann ihn jeder wieder mitnehmen. Ähnlich verhält es sich mit den Sitzplätzen im Bus oder in der U-Bahn. Wenn ein Platz frei ist, kann ihn jeder nehmen. Wenn ein Sitzplatz belegt ist, wird er von der Person kontrolliert, die ihn eingenommen hat.
Akquise 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 sie die Java-Maschine.
Nachdem Sie jedoch mit der Arbeit an der Datei fertig sind, muss diese Ressource (Datei) freigegeben werden, dh Sie müssen dem Betriebssystem mitteilen, dass Sie sie nicht mehr benötigen. Wenn Sie dies nicht tun, wird die Ressource weiterhin von Ihrem Programm gehalten.
Das Betriebssystem führt 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.
Die gute Nachricht ist, dass beim Beenden Ihres Programms automatisch alle Ressourcen freigegeben werden (dies geschieht durch das Betriebssystem selbst).
Die schlechte Nachricht ist: Wenn Sie eine Serveranwendung schreiben (und viele Serveranwendungen sind in Java geschrieben), muss Ihr Server tage-, wochen- und monatelang ohne Unterbrechung laufen können. 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 ist weit weniger als Monate stabiler Arbeit.
2. close()
Methode
Klassen, die externe Ressourcen verwenden, verfügen über eine spezielle Methode, um diese freizugeben: close()
.
Im Folgenden stellen wir ein Beispiel für ein Programm vor, das etwas in eine Datei schreibt und die Datei dann schließt, wenn es fertig ist, d. h. es gibt Ressourcen des Betriebssystems frei. Es sieht ungefähr so aus:
Code | Notiz |
---|---|
|
Der Pfad zur Datei. Holen Sie sich das Dateiobjekt: Erwerben Sie die Ressource. In die Datei schreiben. Die Datei schließen – die Ressource freigeben |
Nachdem Sie mit einer Datei (oder anderen externen Ressourcen) gearbeitet haben, müssen Sie die close()
Methode für das mit der externen Ressource verknüpfte Objekt aufrufen.
Ausnahmen
Es scheint alles einfach zu sein. Bei der Ausführung eines Programms können jedoch Ausnahmen auftreten und die externe Ressource wird nicht freigegeben. Und das ist sehr schlimm.
Um sicherzustellen , dass die close()
Methode immer aufgerufen wird, müssen wir unseren Code in einen --Block einschließen try
und die Methode zum Block hinzufügen. Es wird ungefähr so aussehen:catch
finally
close()
finally
try
{
FileOutputStream output = new FileOutputStream(path);
output.write(1);
output.close();
}
catch (IOException e)
{
e.printStackTrace();
}
finally
{
output.close();
}
Dieser Code lässt sich nicht kompilieren, da die output
Variable innerhalb des Blocks deklariert wird try {}
und daher im finally
Block nicht sichtbar ist.
Beheben wir das Problem:
FileOutputStream output = new FileOutputStream(path);
try
{
output.write(1);
output.close();
}
catch (IOException e)
{
e.printStackTrace();
}
finally
{
output.close();
}
Es ist in Ordnung, aber es funktioniert nicht, wenn beim Erstellen des FileOutputStream
Objekts ein Fehler auftritt, und das kann ganz einfach passieren.
Beheben wir das Problem:
FileOutputStream output = null;
try
{
output = new FileOutputStream(path);
output.write(1);
output.close();
}
catch (IOException e)
{
e.printStackTrace();
}
finally
{
output.close();
}
Es gibt noch ein paar Kritikpunkte. Wenn beim Erstellen des FileOutputStream
Objekts ein Fehler auftritt, output
ist die Variable zunächst null. Diese Möglichkeit muss im Block berücksichtigt werden finally
.
Zweitens close()
wird die Methode immer im finally
Block aufgerufen, was bedeutet, dass sie im try
Block nicht erforderlich ist. Der endgültige Code sieht folgendermaßen 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 wir haben im Grunde nur die Datei geöffnet und 1 geschrieben. Ein bisschen umständlich, finden Sie nicht?
3. try
-mit-Ressourcen
Und hier haben die Entwickler von Java beschlossen, uns mit etwas syntaktischem Zucker zu bestreuen. Ab der 7. Version verfügt Java über eine neue try
-with-resources-Anweisung.
Es wurde genau zur Lösung des Problems mit dem obligatorischen Aufruf der close()
Methode erstellt. Der allgemeine Fall sieht ganz einfach aus:
try (ClassName name = new ClassName())
{
Code that works with the name variable
}
Dies ist eine weitere Variation der try
Aussage . Sie müssen nach dem try
Schlü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 finally
Abschnitt und einen Aufruf der close()
Methode hinzu.
Nachfolgend finden Sie zwei gleichwertige Beispiele:
Langer Code | Code mit Try-with-Ressourcen |
---|---|
|
|
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.
Übrigens können wir der -with-resources-Anweisung catch
auch Blöcke hinzufügen . Oder Sie können sie nicht hinzufügen, wenn sie nicht benötigt werden.finally
try
4. Mehrere Variablen gleichzeitig
Übrigens kommt es oft 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 try
können Sie mit der Anweisung -with-resources ein, aber mehrere Objekte darin erstellen. Der Code, der die Objekte erstellt, muss durch Semikolons getrennt werden. Hier ist das allgemeine Erscheinungsbild dieser Aussage:
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:
Langer Code | Kurzcode |
---|---|
|
|
Nun, was können wir hier sagen? try
-with-resources ist eine wunderbare Sache!
5. AutoCloseable
Schnittstelle
Aber das ist nicht alles. Der aufmerksame Leser wird sofort nach Fallstricken suchen, die die Anwendung dieser Aussage einschränken.
Aber wie try
funktioniert die Anweisung -with-resources, wenn die Klasse keine close()
Methode hat? Angenommen, es wird nichts aufgerufen. Keine Methode, kein Problem.
Aber wie try
funktioniert die -with-resources-Anweisung, wenn die Klasse mehrere close()
Methoden hat? Und sie brauchen Argumente, die ihnen vermittelt werden? Und die Klasse hat keine close()
Methode ohne Parameter?
Ich hoffe, Sie haben sich diese und vielleicht noch andere Fragen wirklich gestellt.
Um solche Probleme zu vermeiden, haben die Java-Entwickler eine spezielle Schnittstelle namens entwickelt AutoCloseable
, die nur eine Methode hat – close()
die keine Parameter hat.
Sie haben außerdem die Einschränkung hinzugefügt, dass nur Objekte von Klassen, die implementieren,AutoCloseable
in einer try
-with-resources-Anweisung als Ressourcen deklariert werden können. Daher verfügen solche Objekte immer über eine close()
Methode ohne Parameter.
Halten Sie es übrigens für möglich, dass eine try
-with-resources-Anweisung ein Objekt als Ressource deklariert, dessen Klasse über eine eigene close()
Methode ohne Parameter verfügt, die jedoch nicht implementiert wird AutoCloseable
?
Die schlechte Nachricht: Die richtige Antwort ist nein – die Klassen müssen die AutoCloseable
Schnittstelle implementieren.
Die gute Nachricht: Java verfügt über viele Klassen, die diese Schnittstelle implementieren, daher ist es sehr wahrscheinlich, dass alles so funktioniert, wie es sollte.