Wie wir alle wissen, müssen alle geöffneten Systemressourcen, wie Streams, Dateien oder Socket-Verbindungen, manuell von Entwicklern geschlossen werden. Andernfalls kommt es bei der weiteren Ausführung des Programms zu Ressourcenverlusten . Schwerer Produktionsunfall.
In der Welt von Java gibt es ein Kung-Fu namens „finally“, das sicherstellen kann, dass Sie, wenn Sie beim Üben von Kampfkünsten verrückt werden, trotzdem einige Selbstrettungsaktionen durchführen können. In der Antike wurde Code, der das Schließen von Ressourcen abwickelte, normalerweise in finally-Blöcken geschrieben. Wenn Sie jedoch mehrere Ressourcen gleichzeitig öffnen, entsteht ein Albtraumszenario:
public class Demo { public static void main(String[] args) { BufferedInputStream bin = null; BufferedOutputStream bout = null; try { bin = new BufferedInputStream(new FileInputStream(new File("test.txt"))); bout = new BufferedOutputStream(new FileOutputStream(new File("out.txt"))); int b; while ((b = bin.read()) != -1) { bout.write(b); } } catch (IOException e) { e.printStackTrace(); } finally { if (bin != null) { try { bin.close(); } catch (IOException e) { e.printStackTrace(); } finally { if (bout != null) { try { bout.close(); } catch (IOException e) { e.printStackTrace(); } } } } } } }
Oh mein Gott! ! ! Es gibt mehr Codes zum Schließen von Ressourcen als Geschäftscodes! ! ! Dies liegt daran, dass wir nicht nur BufferedInputStream
schließen müssen, sondern auch sicherstellen müssen, dass BufferedInputStream
auch korrekt geschlossen werden muss, wenn beim Schließen von BufferedOutputStream
eine Ausnahme auftritt. Daher müssen wir endlich auf die Methode der endlichen Verschachtelung zurückgreifen. Man kann sich vorstellen, dass die Verschachtelung letztendlich umso tiefer sein wird, je mehr Ressourcen geöffnet werden! ! !
Was noch ekelhafter ist, ist, dass der PythonProgrammierer sich diesem Problem stellte und tatsächlich lächelte und charmant sagte: „Wir müssen überhaupt nicht darüber nachdenken~.“ ”:
Aber keine Panik, Bruder! Wir können den neuen syntaktischen Zucker „try-with-resource“ in Java 1.7 verwenden, um Ressourcen zu öffnen, ohne dass Programmierer selbst Ressourcen schreiben müssen, um den Code zu schließen. Mama muss sich keine Sorgen mehr machen, dass meine Handschrift kaputt geht! Wir verwenden try-with-resource, um das Beispiel gerade neu zu schreiben:
public class TryWithResource { public static void main(String[] args) { try (BufferedInputStream bin = new BufferedInputStream(new FileInputStream(new File("test.txt"))); BufferedOutputStream bout = new BufferedOutputStream(new FileOutputStream(new File("out.txt")))) { int b; while ((b = bin.read()) != -1) { bout.write(b); } } catch (IOException e) { e.printStackTrace(); } } }
Ist es nicht sehr einfach? Ist es nicht aufregend? Nie mehr von Python-Programmierern verachtet werden! Nun, das Implementierungsprinzip und der interne Mechanismus werden im Folgenden ausführlich erläutert.
Um mit try-with-resource zusammenzuarbeiten, muss die Ressource die AutoClosable
-Schnittstelle implementieren. Die Implementierungsklasse dieser Schnittstelle muss die Methode close
überschreiben:
public class Connection implements AutoCloseable { public void sendData() { System.out.println("正在发送数据"); } @Override public void close() throws Exception { System.out.println("正在关闭连接"); } }
Aufrufende Klasse:
public class TryWithResource { public static void main(String[] args) { try (Connection conn = new Connection()) { conn.sendData(); } catch (Exception e) { e.printStackTrace(); } } }
Ausgabeergebnis nach der Ausführung:
正在发送数据 正在关闭连接
Bestandenes Ergebnis Wir können sehen, dass die Close-Methode automatisch aufgerufen wird.
Wie wird das gemacht? Ich glaube, Sie, die schlau sind, müssen vermutet haben, dass dies alles tatsächlich vom Compiler-Master verursacht wird. Lassen Sie uns gerade die Klassendatei im Beispiel dekompilieren:
public class TryWithResource { public TryWithResource() { } public static void main(String[] args) { try { Connection e = new Connection(); Throwable var2 = null; try { e.sendData(); } catch (Throwable var12) { var2 = var12; throw var12; } finally { if(e != null) { if(var2 != null) { try { e.close(); } catch (Throwable var11) { var2.addSuppressed(var11); } } else { e.close(); } } } } catch (Exception var14) { var14.printStackTrace(); } } }
Haben Sie gesehen, dass der Compiler in den Zeilen 15 bis 27 automatisch den Final-Block für uns generiert und die Close-Methode der darin enthaltenen Ressource aufgerufen hat? Daher wird die Close-Methode im Beispiel zur Laufzeit ausgeführt.
Ich glaube, dass diejenigen unter Ihnen, die vorsichtig sind, entdeckt haben müssen, dass der Code, den Sie gerade dekompiliert haben (Zeile 21), einen weiteren addSuppressed
enthält als der in der Antike geschriebene Code. Um den Zweck dieses Codes zu verstehen, modifizieren wir das Beispiel gerade etwas: Wir ändern den Code gerade wieder auf die Art und Weise, wie in der Antike Ausnahmen manuell geschlossen wurden, und sendData
Ausnahmen auslösenclose und Methoden >:
public class Connection implements AutoCloseable { public void sendData() throws Exception { throw new Exception("send data"); } @Override public void close() throws Exception { throw new MyException("close"); } }Ändern Sie die Hauptmethode:
public class TryWithResource { public static void main(String[] args) { try { test(); } catch (Exception e) { e.printStackTrace(); } } private static void test() throws Exception { Connection conn = null; try { conn = new Connection(); conn.sendData(); } finally { if (conn != null) { conn.close(); } } } }Nachdem wir sie ausgeführt haben, haben wir Folgendes gefunden:
basic.exception.MyException: close at basic.exception.Connection.close(Connection.java:10) at basic.exception.TryWithResource.test(TryWithResource.java:82) at basic.exception.TryWithResource.main(TryWithResource.java:7) ......Okay, hier ist das Problem , da wir nur eine Ausnahme auslösen können, sehen Sie also auf der obersten Ebene die zuletzt ausgelöste Ausnahme – das heißt, das von der Methode
ausgelöste close
und das von MyException
ausgelöste sendData
wird ignoriert . Dies wird als Ausnahmeabschirmung bezeichnet. Aufgrund des Verlusts von Ausnahmeinformationen kann die Ausnahmemaskierung dazu führen, dass bestimmte Fehler extrem schwer zu finden sind. Wie können wir einen solchen Krebs nicht beseitigen? Um dieses Problem zu lösen, haben die Großen ab Java 1.7 glücklicherweise eine neue Exception
-Methode zur Throwable
-Klasse hinzugefügt, die das Anhängen einer Ausnahme an eine andere Ausnahme unterstützt, um eine Ausnahmemaskierung zu vermeiden. In welchem Format werden die blockierten Ausnahmeinformationen ausgegeben? Lassen Sie uns die Hauptmethode, die gerade mit „try-with-resource“ umschlossen wurde, erneut ausführen: addSuppressed
java.lang.Exception: send data at basic.exception.Connection.sendData(Connection.java:5) at basic.exception.TryWithResource.main(TryWithResource.java:14) ...... Suppressed: basic.exception.MyException: close at basic.exception.Connection.close(Connection.java:10) at basic.exception.TryWithResource.main(TryWithResource.java:15) ... 5 moreWir können sehen, dass es in den Ausnahmeinformationen eine zusätzliche
-Eingabeaufforderung gibt, die uns mitteilt, dass diese Ausnahme tatsächlich aus zwei besteht Ausnahmen Zusammensetzung, Suppressed
ist eine Ausnahme, die unterdrückt wird. Glückwunsch! MyException
-Methode der Ressource verstehen. Andernfalls kann es dennoch zu Ressourcenverlusten kommen. close
Dekorationsmustern verwendet. Wenn die -Methode des Dekorators aufgerufen wird, ruft sie im Wesentlichen die close
-Methode des im Dekorator eingeschlossenen Streams auf. Zum Beispiel: close
public class TryWithResource { public static void main(String[] args) { try (FileInputStream fin = new FileInputStream(new File("input.txt")); GZIPOutputStream out = new GZIPOutputStream(new FileOutputStream(new File("out.txt")))) { byte[] buffer = new byte[4096]; int read; while ((read = fin.read(buffer)) != -1) { out.write(buffer, 0, read); } } catch (IOException e) { e.printStackTrace(); } } }
在上述代码中,我们从FileInputStream
中读取字节,并且写入到GZIPOutputStream
中。GZIPOutputStream
实际上是FileOutputStream
的装饰器。由于try-with-resource的特性,实际编译之后的代码会在后面带上finally代码块,并且在里面调用fin.close()方法和out.close()方法。我们再来看GZIPOutputStream
类的close方法:
public void close() throws IOException { if (!closed) { finish(); if (usesDefaultDeflater) def.end(); out.close(); closed = true; } }
我们可以看到,out变量实际上代表的是被装饰的FileOutputStream
类。在调用out变量的close
方法之前,GZIPOutputStream
还做了finish
操作,该操作还会继续往FileOutputStream
中写压缩信息,此时如果出现异常,则会out.close()
方法被略过,然而这个才是最底层的资源关闭方法。正确的做法是应该在try-with-resource中单独声明最底层的资源,保证对应的close
方法一定能够被调用。在刚才的例子中,我们需要单独声明每个FileInputStream
以及FileOutputStream
:
public class TryWithResource { public static void main(String[] args) { try (FileInputStream fin = new FileInputStream(new File("input.txt")); FileOutputStream fout = new FileOutputStream(new File("out.txt")); GZIPOutputStream out = new GZIPOutputStream(fout)) { byte[] buffer = new byte[4096]; int read; while ((read = fin.read(buffer)) != -1) { out.write(buffer, 0, read); } } catch (IOException e) { e.printStackTrace(); } } }
由于编译器会自动生成fout.close()
的代码,这样肯定能够保证真正的流被关闭。
怎么样,是不是很简单呢,如果学会了话
Das obige ist der detaillierte Inhalt vonDetaillierte Einführung in den Try-with-Ressource-Beispielcode in Java. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!