· Zen HuiFer · Learn · 6 min read
Why is it recommended to prioritize using try with resources instead of try finally
Learn why try-with-resources is recommended over try-finally for handling resource management in Java. It automatically closes resources, reducing boilerplate code and risk of leaks.
Why is it recommended to prioritize using try with resources instead of try finally
1、 Background introduction
try-with-resources
It is a new exception handling mechanism introduced in JDK 7, It allows developers to avoid explicit releasetry-catch
Resources used in statement blocks 。
For example, we take file resource copying as an example, which is familiar to everyonetry-catch-finally
Write it as follows:
public class ResourceTest1 { 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 {
//Close file stream
if (bin != null) {
try {
bin.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bout != null) {
try {
bout.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
We will now change it to usetry-with-resources
You will be surprised to find that with just a few simple lines of code, programming can be done without explicitly closing resources. Here’s how to do it:
public class ResourceTest2 { 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();
}
}
}
Before JDK7, developers had to keep in mind when dealing with resources that had to be closedtry-catch
Used in statementsfinally
Implement the method of closing resources, otherwise as the program continues to run, resource leakage will accumulate into a major production accident. If multiple resources are opened simultaneously in your program, you will be surprised to find that there are more code to close resources than business code, making it even more difficult to read and manage the code clearly.
Therefore, in this context,try-with-resources
Thus born, its original design intention was to alleviate the release of developerstry
The resource burden used in the block.
used to ittry-catch-finally
Classmates who write may ask if all operations involving resources can be usedtry-with-resources
Programming? Are there any pitfalls in using this programming method? If there are pits, where should we pay attention when using them
Okay, without further ado, let’s take a look together todaytry-with-resources
Programming principles.
2、 Practical Explanation
try-with-resources
The statement ensures that each resource is closed at the end of the statement, But there is a prerequisite, which is that this resource must be implementedjava.lang.AutoCloseable
The interface can only be executed and closed 。
try-with-resources
In programming mode, the prerequisite for developers not to explicitly close resources is that the resource must be implementedjava.lang.AutoCloseable
Interface and rewriteclose
Method, otherwise it cannot be usedtry-with-resources
Declare variables in the middle.
For example, we can close a single resource as follows:
public class TryResourceDemo implements AutoCloseable { public void doSomething(){
System.out.println("do something");
} @Override
public void close() throws Exception {
System.out.println("resource is closed");
}
}
public class TryResourceTest { public static void main(String[] args) {
try(TryResourceDemo res = new TryResourceDemo()) {
res.doSomething();
} catch(Exception ex) {
ex.printStackTrace();
}
}
}
The running result is as follows:
do something
resource is closed
It can be clearly seen that,close
The method has been called!
Next, we will open the decompiled version againTryResourceTest.class
You will be surprised to find that the compiler automatically adds code to the file codefinally
Method, and will callclose
Method, close the resource!
public class TryResourceTest { public static void main(String[] args) {
try {
TryResourceDemo res = new TryResourceDemo();
Throwable var2 = null; try {
res.doSomething();
} catch (Throwable var12) {
var2 = var12;
throw var12;
} finally {
if (res != null) {
if (var2 != null) {
try {
res.close();
} catch (Throwable var11) {
var2.addSuppressed(var11);
}
} else {
res.close();
}
} }
} catch (Exception var14) {
var14.printStackTrace();
} }
}
That is to say, usingtry-with-resources
Programming is actually an explicit addition of code by the compilerfinally
Method, eliminating the need for developers to manually close resources!
3、 Order of resource closure
We only introduced the scenario of closing a single resource above. If there are multiple resources,try-with-resources
How was it closed?
Here are some examples to see the results.
public class TryResourceDemo1 implements AutoCloseable { public void doSomething(){
System.out.println("do something 1");
} @Override
public void close() throws Exception {
System.out.println("resource 1 is closed");
}
}
public class TryResourceDemo2 implements AutoCloseable { public void doSomething(){
System.out.println("do something 2");
} @Override
public void close() throws Exception {
System.out.println("resource 2 is closed");
}
}
public class TryResourceDemoTest { public static void main(String[] args) {
try(TryResourceDemo1 demo1 = new TryResourceDemo1();
TryResourceDemo2 demo2 = new TryResourceDemo2()) {
System.out.println("do...");
demo1.doSomething();
demo2.doSomething();
} catch(Exception ex) {
ex.printStackTrace();
}
}
}
The running result is as follows:
do...
do something 1
do something 2
resource 2 is closed
resource 1 is closed
From the results, it can be seen that,try
The last resource used in a statement is the earliest to be closed.
You can find the principle behind this from the decompiled code!
4、 Exception handling mechanism
Under normal circumstances,try
At the end of the statement, relevant resources will be closed. If an exception occurs during internal execution of the statement and we explicitly call itfinally
What is the method and the order of execution?
Let’s continue with the example below to see the results.
public class TryThrowResourceDemoTest { public static void main(String[] args) {
AutoCloseable obj1 = null;
AutoCloseable obj2 = null;
try (TryResourceDemo1 demo1 = new TryResourceDemo1();
TryResourceDemo2 demo2 = new TryResourceDemo2();) {
System.out.println("do...");
obj1 = demo1;
System.out.println(1 / 0);
obj2 = demo2;
System.out.println("over...");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
System.out.println("before finally close");
if (obj1 != null) {
obj1.close();
}
if (obj2 != null) {
obj2.close();
}
System.out.println("after finally close");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
The running result is as follows:
do...
resource 2 is closed
resource 1 is closed
before finally close
resource 1 is closed
after finally close
java.lang.ArithmeticException: / by zero
at com.example.java.trywithresources.a.TryThrowResourceDemoTest.main(TryThrowResourceDemoTest.java:18)
It can be clearly seen that the following conclusion can be drawn:
1. As long as it is achieved
AutoCloseable
The class of the interface, and intry
Declared object variables intry
After the end, Regardless of whether an abnormality occurs,close
All methods will be calledSecondly, in
try
The later an object is declared, the earlier it will be declaredclose
fall3.
try
Automatically called after completionclose
Method, this action will be earlier thanfinally
The method called in
5、 Suppress abnormal handling
In most cases, we usually don’t worry about resourcesclose
An exception will occur, assuming now thattry
The resource object declared in, when executedclose
What is the execution order of the method when throwing exceptions? How can we obtain this anomaly?
Seeing is believing, let’s take an example below to see the results.
public class TryThrowableResourceDemo1 implements AutoCloseable { public void doSomething(){
System.out.println("do something 1");
throw new NullPointerException("TryThrowableResourceDemo1: doSomething() NullPointerException");
} @Override
public void close() throws Exception {
System.out.println("TryThrowableResourceDemo1 is closed");
throw new NullPointerException("TryThrowableResourceDemo1: close() NullPointerException");
}
}
public class TryThrowableResourceDemo2 implements AutoCloseable { public void doSomething(){
System.out.println("do something 2");
throw new NullPointerException("TryThrowableResourceDemo2: doSomething() NullPointerException");
} @Override
public void close() throws Exception {
System.out.println("TryThrowableResourceDemo2 is closed");
throw new NullPointerException("TryThrowableResourceDemo2: close() NullPointerException");
}
}
public class TryThrowableResourceDemoTest { public static void main(String[] args) {
try (TryThrowableResourceDemo1 demo1 = new TryThrowableResourceDemo1();
TryThrowableResourceDemo2 demo2 = new TryThrowableResourceDemo2()) {
System.out.println("do...");
demo1.doSomething();
demo2.doSomething();
} catch (Exception e) {
System.out.println("gobal: exception");
System.out.println(e.getMessage());
Throwable[] suppressed = e.getSuppressed();
for (int i = 0; i < suppressed.length; i++){
System.out.println(suppressed[i].getMessage());
}
}
}
}
The running result is as follows:
do...
do something 1
TryThrowableResourceDemo2 is closed
TryThrowableResourceDemo1 is closed
gobal: exception
TryThrowableResourceDemo1: doSomething() NullPointerException
TryThrowableResourceDemo2: close() NullPointerException
TryThrowableResourceDemo1: close() NullPointerException
From the running results, we can clearly see that fortry
We can detect exceptions within the statement block throughe.getMessage()
Obtain, forclose()
The exceptions thrown by the method are actually specially handled by the compiler and placed in the collection array. Therefore, we need toe.getSuppressed()
Method to obtain.
The specific decompiled code is as follows:
public class TryThrowableResourceDemoTest { public static void main(String[] args) {
try {
TryThrowableResourceDemo1 demo1 = new TryThrowableResourceDemo1();
Throwable var34 = null; try {
TryThrowableResourceDemo2 demo2 = new TryThrowableResourceDemo2();
Throwable var4 = null; try {
System.out.println("do...");
demo1.doSomething();
demo2.doSomething();
} catch (Throwable var29) {
var4 = var29;
throw var29;
} finally {
if (demo2 != null) {
if (var4 != null) {
try {
demo2.close();
} catch (Throwable var28) {
var4.addSuppressed(var28);
}
} else {
demo2.close();
}
} }
} catch (Throwable var31) {
var34 = var31;
throw var31;
} finally {
if (demo1 != null) {
if (var34 != null) {
try {
demo1.close();
} catch (Throwable var27) {
var34.addSuppressed(var27);
}
} else {
demo1.close();
}
} }
} catch (Exception var33) {
System.out.println("gobal: exception");
System.out.println(var33.getMessage());
Throwable[] suppressed = var33.getSuppressed(); for(int i = 0; i < suppressed.length; ++i) {
System.out.println(suppressed[i].getMessage());
}
} }
}
6、 Close the pit of resources
In practical use, whether it is usingtry-with-resource
Programming or usingtry-catch-finally
programming It is necessary to understand the resourcesclose
The internal implementation logic of the method, otherwise it may still lead to resource leakage 。
For example, Java BIO employs a large number of decorator patterns. When calling the close method of the decorator, it is essentially calling the close method of the flow object wrapped by the decorator. For example:
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();
}
}
}
In the above code, we derive fromFileInputStream
Read bytes and write them toGZIPOutputStream
In the middle.GZIPOutputStream
Actually, it isFileOutputStream
The decorator.
becausetry-with-resource
The features of the code will be included later in the actual compiled codefinally
Code block and call it insidefin.close()
Method andout.close()
method.
Let’s take another lookGZIPOutputStream
Class ofclose
method.
public void close() throws IOException {
if (!closed) {
finish();
if (usesDefaultDeflater)
def.end();
out.close();
closed = true;
}
}
In callingout
The variableclose
Before the method,GZIPOutputStream
I also did itfinish
Operation, this operation will continueFileOutputStream
Write compressed information in the middle. If an exception occurs at this time, thenout.close()
The method will be skipped, andout
Variables actually represent the decorated onesFileOutputStream
Class, this is the lowest level resource shutdown method 。
The correct approach should be totry-with-resource
Declare the lowest level resources separately to ensure correspondingclose
The method can definitely be called. In the previous example, we need to declare each separatelyFileInputStream
as well asFileOutputStream
Change it to the following way:
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();
}
}
}
The compiler will automatically generatefout.close()
The code will definitely ensure that the real stream is closed.
7、 Summary
When dealing with resources that must be closed, usetry-with-resources
Sentence substitutiontry-catch-finally
Statements, you will be amazed to find that the code written is more concise and clear, while also eliminating the hassle of manually releasing resources explicitly.
Therefore, in the actual programming process, it is recommended that everyone adopt this method of writing, while paying attention to itclose
Internal implementation logic of the method to avoid resource leakage and service downtime!