God, you are still using try-catch-finally

Author | Silent King II

Source | Silent King II (ID: cmower)

2nd Brother, your previous article on "I Go to Switch " is too interesting. After reading it, I still haven't done enough. Do you want to write another one? Although the syntax of Java 13 is used, it is not very friendly to the old version. But who can guarantee that Java will not come again with a major update, just like Java 8, lively killed Java 6 on the beach. Java 8 is fragrant, but sooner or later, I will upgrade, I support you, second brother, don't care about those opposing voices.

This is the message that reader Alice sent me specifically last week, which really moved me. Indeed, last time I went to read the bars, several large ones have been reproduced, including CSDN, and 15,000 were read on the same day. But criticisms such as "I thought you had any new tricks and I didn't expect to use Java 13" are also not rare.

But my heart has always been great. Since I wrote the first article, the number of sprays is like the dense hair on the top of the head, and the count is countless. So I decided to work harder and bring a new "I Go".

There is no need to remotely review this time, because our company has also resumed work. The code of this review is still Xiao Wang's. Most of the code he wrote is beautiful, rigorous and well commented, which makes me very satisfied. But when I saw that he was useless with try-with-resources, I couldn't help but yell: "I wipe, Xiao Wang, you are still using try-catch-finally!"

Let's take a look at the code written by Xiao Wang.

public class Trycatchfinally {
    public static void main(String[] args) {
        BufferedReader br = null;
        try {
            br = new BufferedReader(new FileReader("/牛逼.txt"));
            String str = null;
            while ((str =br.readLine()) != null) {
                System.out.println(str);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

Hey, I feel that this code is perfect, try-catch-finally is quite satisfactory, especially the file name is very good.txt is very bright. You can understand what this code does without writing comments: read the contents of the file in the try block and print it to the console line by line. If the file cannot be found or an IO read or write error occurs, the incorrect stack information is captured and printed in the catch. Finally, closing the buffered character reader object BufferedReader in finally, effectively eliminates the serious performance consequences caused by resources not being closed.

Before Java 7, try–catch-finally was indeed the best way to ensure that resources would be shut down in a timely manner, regardless of whether the program would throw an exception.

However, experienced readers will find 2 serious problems from the above code:

1) The file name "牛逼 .txt" contains Chinese and needs to be escaped by the decode () method of the java.net.URLDecoder class, otherwise this code will definitely throw an exception that the file cannot be found at runtime.

2) If you create a FileReader object directly through new FileReader ("牛逼 .txt"), "牛逼 .txt" needs to be in the same level directory as the project's src, otherwise it will also throw a file not found exception. But in most cases, the (configuration) file will be placed in the resources directory, so that the compiled files will appear in the classes directory, see the following figure.

In order to solve the above 2 problems, we need to optimize the code:

public class TrycatchfinallyDecoder {
    public static void main(String[] args) {
        BufferedReader br = null;
        try {
            String path = TrycatchfinallyDecoder.class.getResource("/牛逼.txt").getFile();
            String decodePath = URLDecoder.decode(path,"utf-8");
            br = new BufferedReader(new FileReader(decodePath));

            String str = null;
            while ((str =br.readLine()) != null) {
                System.out.println(str);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

Run this code, the program can correctly output the contents of the file to the console. But if you are longing for the word "tidy", you will feel that this code is very bloated, especially the code in finally, like a big belly filled with 12 bottles of snowflake beer.

Moreover, try-catch-finally has a serious hidden danger: br.readLine () in try may throw IOException, and br.close () in finally may throw IOException. If the IOException is thrown unfortunately in both places, the debugging task of the program becomes complicated. In the end, where something went wrong, it takes some effort. This is the result we do not want to see.

In order to simulate the above situation, we self-define a class MyfinallyReadLineThrow, it has two methods, namely readLine () and close (), the method body is actively throwing an exception.

class MyfinallyReadLineThrow {
    public void close() throws Exception {
        throw new Exception("close");
    }

    public void readLine() throws Exception {
        throw new Exception("readLine");
    }
}

Then we use try-finally in the main () method to call the readLine () and close () methods of MyfinallyReadLineThrow:

public class TryfinallyCustomReadLineThrow {
    public static void main(String[] args) throws Exception {
        MyfinallyReadLineThrow myThrow = null;
        try {
            myThrow = new MyfinallyReadLineThrow();
            myThrow.readLine();
        } finally {
            myThrow.close();
        }
    }
}

After running the above code, the error stack looks like this:

Exception in thread "main" java.lang.Exception: close
    at com.cmower.dzone.trycatchfinally.MyfinallyOutThrow.close(TryfinallyCustomOutThrow.java:17)
    at com.cmower.dzone.trycatchfinally.TryfinallyCustomOutThrow.main(TryfinallyCustomOutThrow.java:10)

The exception information of the readLine () method is actually eaten by the stack information of the close () method, which will inevitably make us think that the target to be investigated is the close () method instead of readLine ()-although it is also the object of doubt.

But since try-with-resources, these problems have been solved, as long as the resources that need to be released (such as BufferedReader) implement the AutoCloseable interface. With the solution, let's slim down the finally code block.

try (BufferedReader br = new BufferedReader(new FileReader(decodePath));) {
    String str = null;
    while ((str =br.readLine()) != null) {
        System.out.println(str);
    }
} catch (IOException e) {
    e.printStackTrace();
}

You see, the finally code block disappeared, and instead the resource to be released is written in () after try. If there are multiple resources (BufferedReader and PrintWriter) that need to be released, they can be added directly in ().

try (BufferedReader br = new BufferedReader(new FileReader(decodePath));
     PrintWriter writer = new PrintWriter(new File(writePath))) {
    String str = null;
    while ((str =br.readLine()) != null) {
        writer.print(str);
    }
} catch (IOException e) {
    e.printStackTrace();
}

If you want to release the custom resource, just let it implement the AutoCloseable interface and provide the close () method.

public class TrywithresourcesCustom {
    public static void main(String[] args) {
        try (MyResource resource = new MyResource();) {
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

class MyResource implements AutoCloseable {
    @Override
    public void close() throws Exception {
        System.out.println("关闭自定义资源");
    }
}

The output after the code runs is as follows:

关闭自定义资源

Is it amazing? We just added a MyResource object in try (), and did nothing else, but the output statement in the close () method was executed. Want to know why? Let's take a look at the decompiled bytecode.

class MyResource implements AutoCloseable {
    MyResource() {
    }

    public void close() throws Exception {
        System.out.println("关闭自定义资源");
    }
}

public class TrywithresourcesCustom {
    public TrywithresourcesCustom() {
    }

    public static void main(String[] args) {
        try {
            MyResource resource = new MyResource();
            resource.close();
        } catch (Exception var2) {
            var2.printStackTrace();
        }

    }
}

Hey, the compiler took the initiative to transform try-with-resources and called the close () method in try.

Next, we add another out () method in the custom class,

class MyResourceOut implements AutoCloseable {
    @Override
    public void close() throws Exception {
        System.out.println("关闭自定义资源");
    }

    public void out() throws Exception{
        System.out.println("沉默王二,一枚有趣的程序员");
    }
}

This time, we call the out () method in try:

public class TrywithresourcesCustomOut {
    public static void main(String[] args) {
        try (MyResourceOut resource = new MyResourceOut();) {
            resource.out();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Let's look at the decompiled bytecode again:

public class TrywithresourcesCustomOut {
    public TrywithresourcesCustomOut() {
    }

    public static void main(String[] args) {
        try {
            MyResourceOut resource = new MyResourceOut();

            try {
                resource.out();
            } catch (Throwable var5) {
                try {
                    resource.close();
                } catch (Throwable var4) {
                    var5.addSuppressed(var4);
                }

                throw var5;
            }

            resource.close();
        } catch (Exception var6) {
            var6.printStackTrace();
        }

    }
}

This time, resource.close () is actively called in the catch block, and there is a critical piece of code var5.addSuppressed (var4). What use is it? When an exception is thrown, there may be other exceptions that are suppressed because of the exception and cannot be thrown normally. At this time, these suppressed methods can be recorded by the addSuppressed () method. The suppressed exceptions will appear in the stack information of the thrown exceptions, and these exceptions can also be obtained through the getSuppressed () method. The advantage of this is that no exception will be lost, which is convenient for our developers to debug.

Wow, did you think of our previous example-in try-finally, the exception information of the readLine () method was eaten up by the stack information of the close () method. Now that you have try-with-resources, let's see if the out () method, which has the same effect as the readLine () method, will be eaten by close ().

Exceptions are directly thrown in the close () and out () methods:

class MyResourceOutThrow implements AutoCloseable {
    @Override
    public void close() throws Exception {
        throw  new Exception("close()");
    }

    public void out() throws Exception{
        throw new Exception("out()");
    }
}

Call these 2 methods:

public class TrywithresourcesCustomOutThrow {
    public static void main(String[] args) {
        try (MyResourceOutThrow resource = new MyResourceOutThrow();) {
            resource.out();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

The output of the program is as follows:

java.lang.Exception: out()
    at com.cmower.dzone.trycatchfinally.MyResourceOutThrow.out(TrywithresourcesCustomOutThrow.java:20)
    at com.cmower.dzone.trycatchfinally.TrywithresourcesCustomOutThrow.main(TrywithresourcesCustomOutThrow.java:6)
    Suppressed: java.lang.Exception: close()
        at com.cmower.dzone.trycatchfinally.MyResourceOutThrow.close(TrywithresourcesCustomOutThrow.java:16)
        at com.cmower.dzone.trycatchfinally.TrywithresourcesCustomOutThrow.main(TrywithresourcesCustomOutThrow.java:5)

Look, this time it won't. The exception stack information of out () is printed out, and a keyword Suppressed is added to the stack information of the close () method. At a glance, it's good, I like it.

To summarize, when dealing with resources that must be closed, always try to use try-with-resources instead of try-catch-finally. The code generated by the former is more concise and clear, and the abnormal information generated is more reliable. Promise me, OK? Don't use try-catch-finally anymore.

About the author: Silent King 2 (ID: cmower), CSDN blogger: Silent King 2 readily.

【END】

More exciting recommendations

☞From VMWare to Ali Shenlong, the 40-year evolutionary history of virtualization technology

weird increased knowledge of the history of braces you know?

☞One -stop killer AI development platform is here! Say goodbye to switching scattered modeling tools

Those god-like programmers

☞Realize the statistical prediction model of time series data through Python code

Bitcoin as ransom, WannaRen extortion virus struck the second time!

☞Do you know? In fact, Oracle histogram automatic statistical algorithm has these defects! (With verification steps)

Every "watching" you order, I take it seriously

Published 1958 original articles · 40,000+ praises · 18.17 million views

Guess you like

Origin blog.csdn.net/csdnnews/article/details/105548971