java异常—— finally 子句+带资源的 try语句

【0】README

0.1) 本文描述+源代码均 转自 core java volume 1, 旨在理解 java异常—— finally 子句+带资源的 try语句 的相关知识;


【1】 finally 子句相关

1.1)产生资源回收问题:当代码抛出一个异常时, 就会终止方法中剩余代码的处理, 并退出这个方法的执行。如果方法获得了一些本地资源,且只有这个方法知道, 那么就会产生资源回收问题;
1.2)解决方法:

  • 1.2.1)一种解决方法是 捕获并重新抛出所有异常: 但是, 这种方法比较乏味, 因为需要在两个地方取出所有分配的资源;
  • 1.2.2)一种更好的解决方法是 finally 子句;

1.3)看个荔枝(不管是否异常被捕获, finally子句都会被执行),在下面的示例中, 程序将在所有cases 下关闭文件:

InputStream in = new FileIinputStream();
try
{
//1
code that might throw exceptions
//2
}
catch(IOException e)
{
//3
show error msg
//4
}
finally
{
    // 5
    in.close();
}
//6

对以上代码的分析(Analysis)(在上面的 代码中, 有下列3种cases 会执行finally 子句):

  • A1)代码没有抛出异常。在这种情况下, 程序首先执行try语句块中的全部代码, 然后执行 finally子句中的代码。随后执行try语句块之后的 第一条语句, 执行顺序为1、2、5、6;

  • A2)抛出一个在catch 子句中捕获的异常。

    • A2.1)如果catch子句没有抛出异常, 程序将执行try 语句块之后的第一条语句, 在这里的执行顺序为 1、3、4、5、6;
    • A2.2)如果catch 子句抛出异常, 异常将被抛出这个方法的调用者, 在这里, 执行顺序为 1、3、5;
  • A3)代码抛出了一个异常, 但catch 子句无法捕获; 在这里, 执行顺序为 1、5;

1.4)try语句可以只有 finally子句,而没有catch子句:如,

InputStream in = "...";
try
{}
finally
{
    in.close();
}
  • 1.4.1)无论在try 语句块中是否遇到异常,finally子句中的 in.close() 语句都会被执行, 当然, 如果真的遇到一个异常, 这个异常将会被重新抛出, 并且必须由另一个catch 子句捕获;
  • 1.4.2)我们在需要关闭资源的时候, 用这种方式使用 finally 子句是一个不错 的选择;

Hint)这里, 强烈建议独立使用 try/catch 和 try/finally 语句块, 可以提高代码的清晰度。如,(强烈推荐-strongly recommended)

InputStream in = "...";
try
{
    try
    {
        code that might throw exceptions
    }    
    finaly
    {
        in.close();
    }    
}
catch(IOException e)
{
    show error msg
}
  • H1)内层的try语句块有一个职责, 就是确保关闭输入流;
  • H2)外层的try 语句块也只有一个职责, 就是确保报告出现的错误;
  • H3)这种设计不仅清楚, 而且还将报告finally子句中出现的错误;

Warning)

  • W1)当finally子句包含 return 语句时, 将会出现一种意想不到的结果;
  • W2)假设利用return 语句从try 语句块中退出。 在这个方法返回前, finally子句的内容将被执行;

1.5)如果finally子句也有一个 return, 这个返回值将会覆盖原始的返回, 看个荔枝

public static int f(int n)
{
    try
    {
        int r = n * n;
        return r;
    }
    finally
    {
          if(n==2) return 0;
    }
}

对上述代码的分析(Analysis):如果调用 f(2),那么try语句块return 4, 而方法返回前, 还要执行 finally子句, 其返回0, 这个返回值会覆盖掉原始的返回值4**(即最终的返回值是0 而不是4);**

1.6)出现的问题:

InputStream in = "...";
try
{
    code that might throw exceptions
}
finally
{
    in.close();
}
  • 1.6.1)问题描述:对于上述代码, 假设在 try 语句块中的代码抛出了 IOException异常, 这些异常只有这个方法的调用者才能够处理。 执行finally语句块, 并调用close 方法。 而 close 方法本身也可能抛出 IOException异常。 当出现这种case 时, 原始的异常数据将丢失, 转而抛出 close 方法的异常;
  • 1.6.2)解决方法: 如果你想做适当处理的话, 重新抛出原来的异常,代码会变得极其繁琐;如下图所示:
InputStream in = "...";
Exception ex = null;
try
{
    try
    {
        code that might throw exceptions
    }        
    catch(Exception e)
    {
        ex = e;
        throw e;
    }
}
finaly
{
    try
    {
        in.close();
    }
    catch(Exception e)
    {
        if(ex == null) throw e;
    }
}    

【2】 带资源的 try语句

2.1)对于以下代码模式:

open a resource
try
{
    work with the resource
}
finally
{
    close the resource
}
  • 2.1.1)假设资源属于一个实现了 AutoCloseable 接口的类, AutoCloseable 接口有一个方法:
void close()  throws Exception;

Annotation)还有一个 Closeable 接口, 它是 AutoCloseable 的子接口, 也包含一个 close方法, 不过, 这个方法声明为抛出一个 IOException;
2.2)带资源的 try语句(try-with-resource)的最简单形式为:

try(Resource res)
{
    work with  res
}
  • 2.2.1)try块退出时, 会自动调用 res.close()。看个荔枝——读取一个文件中的所有单词:
try(Scanner in = new Scanner(new FileInputStream("/usr/.../")))
{
    while(in.hasNext())
        System.out.println(in.next());
}
  • 上述代码中的 try块正常退出时, 或者存在一个异常时, 都会调用 in.close()方法, 就好像使用了 finally块一样;
  • 2.2.2)还可以指定多个 资源:
try(Scanner in = new Scanner(new FileInputStream("/usr/.../")), PrintWriter out = new PrintWriter("out.txt"))
{
    while(in.hasNext())
        out.println(in.next().toUpperCase());
}
  • 2.2.3)无论这个块如何退出, in 和 out 都会关闭: 如果你使用常规方式手动编程,就需要两个嵌套的 try/finally 子句;

Conclusion)

  • C1)我们看到, 如果try块中抛出一个异常, 而且close方法也抛出一个异常, 这就会带来一个难题, 现在 带资源的try块解决了这个问题;
  • C2)原来的异常会被重新抛出, 而 close方法抛出的异常会“被抑制”;
  • C3)这些异常将自动捕获, 并由 addSuppressed 方法增加到原来的异常。如果对异常有兴趣, 可以调用 getSuppressed 方法, 它会得到从 close方法 抛出并被抑制的异常列表;

Attention)只要需要关闭资源, 就要尽可能地使用带资源的 try语句;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值