Technical guy: I'm going, you're still using try-catch-finally

Keywords: Java Python github

Second Brother, the one you wrote before I'll switch The article is so interesting that after reading it, you still haven't finished. Would you like to write another one?Although using Java 13 syntax, it is not very friendly to older versions.But who can guarantee that Java won't get another major update, like Java 8, that Java 6 will be snapped to the beach.Java 8 is fragrant, but it's going to be upgraded sooner or later. I stand up for you, brother. Don't care about the opposing voices.

I'm really impressed by the message that reader Alice sent me last week.Indeed, the last "I'll go" to read the bars, several large size were reloaded, including CSDN, the next day 15,000 read.But criticisms like "You thought you had something new, and you didn't expect to use Java 13" are not uncommon.

But my heart has always been big.Since I wrote my first article, the number of times I've been sprayed has been as dense as the amount of hair on my head.So I decided to keep going and bring a new "I'll go".

There is no need for remote review this time because our company has also resumed work.This review's code is still Xiao Wang's. Most of the code he wrote is beautiful, rigorous and comments are in place, which is very satisfying to me.But when I saw that he didn't use try-with-resources, I couldn't help shouting, "I wipe, Xiao Wang, you Ya are still using try-catch-finally!"

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("/Fucking great.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();
                }
            }
        }
    }
}

Well, it feels like this code is perfect. Try-catch-finally uses moderate rules, especially the filename. txt is bright.You can understand what this code does without commenting: read the contents of the file in the try block and print them line by line to the console.If the file cannot be found or an IO read-write error occurs, the stack information for the error is captured and printed in the catch.Finally, closing the BufferedReader object in finally effectively eliminates the serious performance consequences of not closing the resource.

Prior to Java 7, try-catch-final was really the best way to ensure that resources were shut down in a timely manner, regardless of whether the program threw an exception or not.

However, experienced readers will find two serious problems in this code:

1) The file name Niuquan.txt contains Chinese and needs to be escaped by decode() method of the java.net.URLDecoder class, otherwise this code will surely throw an exception that the file cannot find at run time.

2) If a FileReader object is created directly from the new FileReader("Niuju.txt"), Niuju.txt needs to be in the same level directory as the src of the project, otherwise the exception that the file cannot find will be thrown as well.In most cases, however, the file is placed in the resources directory, making it easier for the compiled file to appear in the classes directory, as shown in the following figure.

To solve these two 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("/Fucking great.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();
                }
            }
        }
    }
}

By running this code, the program can correctly output the contents of the file to the console.But if you're obsessed with the word "neat," you'll find the code bloated, especially in finally, like a belly full of 12 bottles of snowflake beer.

I see a Python programmer teasing Java programmer's magic map on the Internet and copy it directly to amuse you:

Moreover, try-catch-finally has a serious hidden danger from start to finish: br.readLine() in try may throw IOException, and br.close() in finally may throw IOException.If IOException is thrown in both places, the debugging task of the program becomes more complex, and it takes some effort to get where the error occurred, which is the result we don't want to see.

To simulate this, we customize a class named MyfinallyReadLineThrow, which has two methods, readLine() and close(), with the body of the method throwing the exception actively.

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

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

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

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 "mainjava.lang.Exceptionclose
    at com.cmower.dzone.trycatchfinally.MyfinallyOutThrow.close(TryfinallyCustomOutThrow.java:17)
    at com.cmower.dzone.trycatchfinally.TryfinallyCustomOutThrow.main(TryfinallyCustomOutThrow.java:10)

The exception information for the readLine() method is eaten by the stack information for the close() method, which must mislead us into thinking that the target of the investigation is the close() method, not the readLine() - although it should also be suspected.

But with try-with-resources, these problems are solved as long as the resources that need to be released (such as BufferedReader) implement the AutoCloseable interface.Now that we have a solution, let's slim down the previous finally block of code.

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 block disappears and instead writes the resource to be released in () after the try.If multiple resources (BufferedReader and PrintWriter) 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 a 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("Turn off custom resources");
    }
}

The output after the code is run is as follows:

Turn off custom resources

Is it amazing?We only have a new MyResource object in try (), and nothing else has been done, but the output statement in the close() method executed partially.Want to know why?Take a look at the decompiled byte code.

class MyResource implements AutoCloseable {
    MyResource() {
    }

    public void close() throws Exception {
        System.out.println("Turn off custom resources");
    }
}

public class TrywithresourcesCustom {
    public TrywithresourcesCustom() {
    }

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

    }
}

Alas, the compiler has proactively adapted try-with-resources by calling the close() method in try.

Next, we add an out() method to the custom class.

class MyResourceOut implements AutoCloseable {
    @Override
    public void close() throws Exception {
        System.out.println("Turn off custom resources");
    }

    public void out() throws Exception{
        System.out.println("Silence Wang Er, an interesting programmer");
    }
}

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();
        }
    }
}

Take another look at the decompiled byte code:

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 is its use?When an exception is thrown, there may be other exceptions that are suppressed by the exception and cannot be thrown properly.These suppressed methods can then be recorded by the addSuppressed() method.Suppressed exceptions appear in the stack information of thrown exceptions, which can also be obtained by the getSuppressed() method.The advantage of this is that no exceptions will be lost, making it easier for our developers to debug.

Wow, did you think of our previous example where, in try-finally, the exception information for the readLine() method was eaten by the stack information for the close() method?Now that you have try-with-resources, see if the out() method that works with the readLine () method will be eaten by close ().

Exceptions are thrown directly in 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 two 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.Exceptionout()
    at com.cmower.dzone.trycatchfinally.MyResourceOutThrow.out(TrywithresourcesCustomOutThrow.java:20)
    at com.cmower.dzone.trycatchfinally.TrywithresourcesCustomOutThrow.main(TrywithresourcesCustomOutThrow.java:6)
    Suppressedjava.lang.Exceptionclose()
        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 for out() is printed out, and the stack information for the close() method has a keyword Suppressed added to it.At a glance, it's good. I like it.

To summarize, try-with-resources, rather than try-catch-finally, is always a limited consideration when dealing with resources that must be closed.The former produces simpler, clearer code and more reliable exception information.How about promising me?Don't use try-catch-finally anymore.

Acknowledgement

Okay, my dear reader friend, that's all about this article. Did you feel like you've learned something new?I'm Silence King, an interesting programmer.Originality is not easy. Don't give me a blank ticket. Please agree with this point. It will be the strongest motivation for me to write more good articles.

If you find the article helpful, please WeChat to search for "Silence King II" for the first time to read, reply to [666] and more 500G HD instructional videos (categorized) that I have prepared for you.This paper GitHub Already included, there are complete test points for factory interviews. Welcome to Star.

Posted by nbalog on Sat, 04 Apr 2020 01:35:39 -0700