创建销毁对象(第九条:比起TRY – FINALLY要更喜欢TRY -WITH-RESOURCES)

翻译 2018年04月17日 18:06:54

Java类库里面包含很多必须通过调用close方法手动关闭的资源。比如InputStream,OutputStream,和java.sql.Connection。关闭资源经常被客户端所忽视,情理之中也就导致了一些可怕的性能问题。尽管很多资源使用finalizers作为安全保障,但是finalizers却工作的不太好(Item 8)。以前,try – finally语句是保证资源被及时关闭的最好的方式,尽管遇到异常或者返回也是如此。

// try-finally - No longer the best way to close resources!
    static String firstLineOfFile(String path) throws IOException {
        BufferedReader br = new BufferedReader(new
                FileReader(path));
        try {
            return br.readLine();
        } finally {
            br.close();
        }
    }

这可能看起来也不坏,但是你在加一个资源的时候它就变坏了。

// try-finally is ugly when used with more than one resource!
    static void copy(String src, String dst) throws IOException {
        InputStream in = new FileInputStream(src);
        try {
            OutputStream out = new FileOutputStream(dst);
            try {
                byte[] buf = new byte[BUFFER_SIZE];
                int n;
                while ((n = in.read(buf)) >= 0)
                    out.write(buf, 0, n);
            } finally {
                out.close();
            }
        } finally {
            in.close();
        }
    }


很多时候优秀的开发人员也会犯这种错,这可能很难相信。一开始,我把Java Puzzlers第88页弄错了,好几年都没人发现。事实上,在2007年Java类库中三分之二的close方法的使用都是错的。

甚至对于正确的使用try – finally去关闭资源的语句,比如上面的两个例子,都是存在隐含的缺陷的。在try代码块和finally代码块里面的代码是能够抛出异常的。比如,在firstLineOfFile方法里面,由于基础的物理设备的故障,调用readLine方法就会抛出一个异常。那么调用close就会因为相同原因也失败掉。在这种情况下,第二个异常就完全把第一个给抹去了。在异常栈追踪里面没有第一个异常,这样会在真实系统中极大地增加调试的复杂性,尤其在你想看第一个异常来诊断异常的时候。尽管可以写代码来阻止第二个异常抛出来以达到让第一个异常显示,但是事实上却没人这么做,因为代码太繁琐了。

当java 7引入了try -with-resources语句后,所有的这些问题一下子都被解决了。为了使用这种结构,资源必须实现AutoCloseable接口,这个接口仅仅由一个返回值为void的close方法组成。在java类库还有第三方类库中,很多类和接口都实现或者继承了AutoCloseable。如果你要写一个代表资源的类并且它必须被关闭,那么你的类也应该实现AutoCloseable。

这儿是我们使用try -with-resources的第一个例子:

// try-with-resources - the the best way to close resources!
    static String firstLineOfFile(String path) throws IOException {
        try (BufferedReader br = new BufferedReader(
                new FileReader(path))) {
            return br.readLine();
        }
    }


还有我们使用try -with-resources的第二个例子:

// try-with-resources on multiple resources - short and sweet
    static void copy(String src, String dst) throws IOException {
        try (InputStream in = new FileInputStream(src);
             OutputStream out = new FileOutputStream(dst)) {
            byte[] buf = new byte[BUFFER_SIZE];
            int n;
            while ((n = in.read(buf)) >= 0)
                out.write(buf, 0, n);
        }
    }


try -with-resources版本的代码不仅比之前的更短,可读性更高,并且它们能提供更好的诊断能力。考虑firstLineOfFile方法。如果readLine的调用还有(看不到的)close的调用都抛出了异常,那么为了支持前者后面的异常就被吃掉了。事实上,为了维护你真实想看的异常,多个异常可能都被吃掉了。这种被吃掉的异常也不是直接丢掉了;它们带着一个标记被打印在堆栈里面,这个标记会表明它们是被抑制了的。你也可以通过程序中的getSuppressed方法去访问它们,它是在Java 7里面加到Throwable里面的。你可以给try -with-resources语句加上catch语句,正如你通常在try - finally 语句里面添加一样。这样你就可以处理不被其他嵌套层代码所污染的异常了。

就像下面这个稍微有点不自然的例子,在这个版本中我们的firstLineOfFile方法不抛出异常,但是如果它打不开文件或者不能读,它接收一个默认值用来返回:

// try-with-resources with a catch clause
    static String firstLineOfFile(String path, String defaultVal) {
        try (BufferedReader br = new BufferedReader(
                new FileReader(path))) {
            return br.readLine();
        } catch (IOException e) {
            return defaultVal;
        }
    }


这一课很清晰:当使用一些必须被关闭的资源的时候,比起try-finally要总是更喜欢使用try -with-resources。try -with-resources的代码更短更清晰,它产生的异常也更有用。try- with-resources语句让写必须关闭资源的正确的代码变得简单,这一点使用try – finally实际上是不可能的。

JavaSE之异常

-
  • 1970年01月01日 08:00

PYTHON中try finally与try except不能同时使用吗?

网络上一些文章(如《PYTHON编程金典读书笔记》、《PYTHON中的finally》)都讲述try finally与try except不能同时使用,若同时使用,就会出现语法错误。根据我的调查,这些...
  • zfqcn
  • zfqcn
  • 2012-06-18 11:08:54
  • 4778

try-catch-finally 与返回值的修改

先看一段java代码,func返回值为int:public static int func() { int result = 0; try { result = 1; ...
  • Next_Second
  • Next_Second
  • 2017-06-12 12:35:42
  • 881

Python文件异常try/except/finally

try: data = open("my.txt") print(data.readline(),end='') except IOError as err: ...
  • zhang_xiaomeng
  • zhang_xiaomeng
  • 2017-05-05 10:40:43
  • 1125

try catch finally 的用法,你知道多少?最详细、最到位的讲解,配合代码实例讲解,让你轻松掌握和理解

try catch finally 的用法,你知道多少?最详细、最到位的讲解,配合代码实例讲解,让你轻松掌握和理解...
  • jspping
  • jspping
  • 2015-12-17 15:25:16
  • 1591

测试 __try, __finally, __except

C语言标准是没有 try-catch语法 的, M$家自己提供了一组. /// @file ClassroomExamples.c /// @brief 验证C语言的非标准try, catch #...
  • LostSpeed
  • LostSpeed
  • 2015-12-30 23:45:30
  • 435

try catch finally 正确使用方法

public class AAA {          public static void main(String[] args) {       System.out.println("=====...
  • zh521zh
  • zh521zh
  • 2016-02-16 14:08:38
  • 1514

try、catch、finally中return的执行顺序

try、catch、finally中的return        今天在做一个多线程加读写锁的测试时,考虑效率问题,想及时return结果,但存在一个严肃的问题,那就是锁的开启和关闭问题。因为锁开启后...
  • wangqingbo0829
  • wangqingbo0829
  • 2016-09-07 12:33:28
  • 7649

java中try{}catch{}和finally{}的执行顺序问题

今天我给大家讲解一下java的的错误和异常处理机制以及相关异常的执行顺序问题。如有不足的地方,欢迎批评指正~   1、首相简单介绍一下java中的错误(Error)和异常(Exceptio...
  • Busquets5
  • Busquets5
  • 2016-04-04 22:18:54
  • 388

关于try,finally里面的return,到底谁先执行的问题

在JAVA语言的异常处理中,finally里面代码块是为了保证无论出现了什么样的情况,finally里的代码一定会被执行。但是return 的意思就是结束当前函数的调用并跳出这个函数,因此finall...
  • java19950529
  • java19950529
  • 2016-07-27 11:49:28
  • 5008
收藏助手
不良信息举报
您举报文章:创建销毁对象(第九条:比起TRY – FINALLY要更喜欢TRY -WITH-RESOURCES)
举报原因:
原因补充:

(最多只允许输入30个字)