最后有时候会让我困惑

时间:2016-04-26 13:08:17

标签: java exception exception-handling try-catch finally

今天在大学时我们谈了trycatchfinally。 我对这两个例子感到困惑:

PrintWriter out = null;
try {
  out = new PrintWriter(...); // We open file here
} catch (Exception e) {
  e.printStackTrace();
} finally { // And we close it here
  out.close();
}

关闭finally中的文件与我们是否这样做有什么区别:

PrintWriter out = null;
try {
  out = new PrintWriter(...); // We open file here
} catch (Exception e) {
  e.printStackTrace();
}
out.close();

catch之后的这段代码将始终执行。

您能否给我一些很好的例子,说明我们何时使用finally和何时将代码放在catch之后的差异?我知道最终将永远执行,但程序也会在catch块之后继续运行。

8 个答案:

答案 0 :(得分:33)

如果代码抛出Error,它仍然会有所不同。这不会在代码中捕获,因此try/catch/finally之后的任何部分都不会被捕获。如果它是finally的一部分,即使Error也会执行。

其次,如果由于某种原因e.printStackTrace()抛出异常(虽然它非常罕见),同样会发生 - finally仍然会被执行。

一般来说,finally是释放资源的非常安全的方式,无论发生什么。从Java 7开始支持try-with-resources更加安全,因为它可以轻松管理在关闭操作期间抛出的多个异常。在这个例子中,它看起来像:

try (PrintWriter out = new PrintWriter(...)) {
    // do whatever with out
}
catch (Exception e) {
    e.print... (whatever)
}
// no need to do anything else, close is invoked automatically by try block

编辑:还要注意你的代码不是真的正确(无论哪个版本)。如果PrintWriter构造函数引发异常,则行out.close()将在NullPointerException上失败。

答案 1 :(得分:3)

如果在try块中发生未被捕获的错误,或者即使在catch块中发生错误,也不会执行catch之后的'code of code',而是{阻止{1}}阻止。

  

finally将永远执行。

来自Java文档:

  

当try块退出时,finally块始终执行。这个   确保即使意外发生也执行finally块   异常发生。但最终对于不仅仅是异常更有用   处理 - 它允许程序员避免使用清理代码   被意外绕过返回,继续或休息。进行清理   finally块中的代码总是很好的做法,即使没有   预计有例外。

答案 2 :(得分:2)

如果catch块中的某些内容会抛出异常怎么办? out.close不会执行。您还可以使用" try with resources"确保所有资源在使用后关闭。试试这个例子:

public static void withFinnaly() {
        try {
            throwException();
            System.out.println("This won't execute");
        } catch (Exception e) {
            System.out.println("Exception is caught");
            throwException();
        } finally {
            System.out.println("Finally is always executed," +
                    " even if method in catch block throwed Exception");
        }
    }

    public static void withOutFinnaly() {
        try {
            throwException();
            System.out.println("This won't execute");
        } catch (Exception e) {
            System.out.println("Exception is caught");
            throwException();
        }
        System.out.println("Looks like we've lost this... " +
                "This wont execute");
    }

    public static void throwException() throws RuntimeException {
        throw new RuntimeException();
    }

答案 3 :(得分:2)

finally的正常用例是当您不想在同一方法中捕获异常时。

在这种情况下,你使用finally块的try而没有catch。这样,您可以确保关闭资源,而不必在方法本身中捕获异常。

答案 4 :(得分:2)

在Java中,源代码:

void foo()
{
  try {
    if (W())
      return;
  }
  catch (FooException ex) {
    if (X())
      throw;
  }
  finally {
    Y();
  }
  Z();
}

将由编译器转换为:

void foo()
{
  try {
    if (W()) {
      Y();
      return;
    }
  }
  catch (FooException ex) {
    if (X()) {
      Y();
      throw;
    }
  }
  catch {
    Y();
    throw;
  }
  Y();
  Z();
}

效果是导致Finally块中的代码被复制 所有控制可能离开方法的地方。任何try块 具有finally但不是catch-all处理程序的处理程序相当于一个具有立即抛出的catch-all处理程序的处理程序(然后处理器可以在catch-all之前插入finally代码的副本处理程序。

答案 5 :(得分:1)

如果在NullPointerException的构造函数中发生异常,您的第二个示例可能会抛出不需要的PrintWriter

另一种可能性是out.close();会抛出错误,而不会被捕获。

如果将代码移动到finally块,它将始终执行 - 无论try块是否成功。如果您的try - 块引发捕获的异常,则尤其非常有用。在第二个示例中,这将导致out.close()未被执行,而对于finally块,即使try块引发未被捕获的错误,也会执行它。

答案 6 :(得分:1)

虽然答案本身不完整,但try-finally(mis)使用的这两个例子可能具有启发性:

public class JavaApplication3 
{
    static int foo()
    {
        try
        {
            return 6;
        }
        finally
        {
            return 4;
        }

    }

    public static void main(String[] args)
    {
        System.out.println("foo: " + foo());
    }
}


public class JavaApplication3 
{
    static int foo()
    {
        try
        {
            throw new Exception();
        }
        finally
        {
            return 4;
        }

    }
    public static void main(String[] args)
    {
        System.out.println("foo: " + foo());
    }
}

两个程序都输出 4

可以在Chapter 14.20.2 of JLS

上找到原因
  

首先执行try块,执行带有finally块的try语句。然后有一个选择:

     

•如果由于抛出值而导致try块的执行突然完成   V,然后有一个选择:
   [...]
   - 如果V的运行时类型与可捕获的分配不兼容   try语句的任何catch子句的异常类,然后是finally   块被执行。然后有一个选择:
   [...]
  > 如果finally块因为S而突然完成,那么try语句   由于原因S突然完成(并且丢弃值V的抛出并且   忘了)。

     

如果由于任何其他原因R 突然完成try块的执行,则执行finally块,然后有一个选择:
   - 如果finally块正常完成,则try语句完成   因为理由而突然出现    - 如果finally块因为S而突然完成,那么try语句   由于原因S突然完成(原因R被丢弃)。

编辑我的

考虑return是完成tryfinally阻止的突然方法。

答案 7 :(得分:0)

一个较小的例子:

PrintWriter out = null;
try {
    out = new PrintWriter();
    out.print(data);
} finally {
    out.close();
}

在这里,我们不会发现任何异常(由来电者处理),但我们确实希望close作者是否

  • 通过try块或
  • 正常运行
  • 通过例外离开。

在这两种情况下,finally中的代码都是作为离开块的一部分执行的。

在这里,我们捕获了一些例外情况:

PrintWriter out = null;
try {
    out = new PrintWriter();
    out.print(data);
} catch (IOException e) {
    log(e);
} finally {
    out.close();
}
do_something_else();

这里有三种可能的途径:

  • 正常执行try部分,然后是finally,然后是do_something_else()
  • try部分抛出IOException,捕获并记录,然后finally块运行,然后do_something_else()
  • try部分抛出一个不同的throwable,它没有被捕获,但是finally块运行,然后代码跳转到下一个封闭的try,可能在调用者中。 / LI>

在编写finally块时要小心 - 它不在try内,因此任何例外情况都会先于任何正在进行的异常。简短的建议是尽量避免可能从finallycatch内部抛出的东西。