Heim  >  Artikel  >  Java  >  Detaillierte Einführung in den Try-with-Ressource-Beispielcode in Java

Detaillierte Einführung in den Try-with-Ressource-Beispielcode in Java

黄舟
黄舟Original
2017-03-21 11:08:141470Durchsuche

Hintergrund

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.

Praktische Praxis

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.

Prinzip

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.

Ausnahmeschutz

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 sendDataAusnahmen 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 more
Wir 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

Eine kleine Frage

Bei der Verwendung von Try-with-Resource müssen Sie die interne Implementierungslogik der

-Methode der Ressource verstehen. Andernfalls kann es dennoch zu Ressourcenverlusten kommen. close

Zum Beispiel wird in Java BIO eine große Anzahl von

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!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn