Java 最后,有时我会感到困惑

Java 最后,有时我会感到困惑,java,exception,exception-handling,try-catch,finally,Java,Exception,Exception Handling,Try Catch,Finally,今天在大学里,我们谈了一些关于尝试,捕获和最终。 我对这两个例子感到困惑: PrintWriter out = null; try { out = new PrintWriter(...); // We open file here } catch (Exception e) { e.printStackTrace(); } finally { // And we close it here out.close(); } 最后在中关闭文件与按此方式关闭文件有什么区别: PrintW

今天在大学里,我们谈了一些关于
尝试
捕获
最终
。 我对这两个例子感到困惑:

PrintWriter out = null;
try {
  out = new PrintWriter(...); // We open file here
} catch (Exception e) {
  e.printStackTrace();
} finally { // And we close it here
  out.close();
}
最后在
中关闭文件与按此方式关闭文件有什么区别:

PrintWriter out = null;
try {
  out = new PrintWriter(...); // We open file here
} catch (Exception e) {
  e.printStackTrace();
}
out.close();
捕获后的这段代码将始终执行


你能给我一些很好的例子说明当我们最后使用
和当我们把代码放在catch之后时的区别吗?我知道finally将始终执行,但程序也将在catch块之后继续运行。

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

其次,如果出于任何原因
e.printStackTrace()
抛出异常(尽管这非常罕见),同样的情况也会发生-
最终仍将执行

总的来说,
最后
是一种非常安全的方式,无论发生什么情况都可以释放资源。自Java 7以来,支持的安全性更高,因为它可以轻松管理关闭操作期间抛出的多个异常。在本例中,它看起来像:

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

上失败,如果
try
块内发生未捕获的错误,或者即使
catch
块内发生错误,也不会执行catch之后的“代码片段”,但是
最后将执行

最后
将始终执行

从Java文档中:

当try块退出时,finally块始终执行。这 确保即使出现意外错误,也会执行finally块 出现异常。但最后,它不仅适用于例外情况 处理-它允许程序员避免清理代码 由于返回、继续或中断而意外绕过。清理 finally块中的代码始终是一种良好的实践,即使没有 预计会出现例外情况


若catch块中的某个内容将抛出异常怎么办?out.close将不执行。您还可以使用“”,以确保所有资源在使用后都已关闭。试试这个例子:

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

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

另一种可能性是
out.close()将抛出一个未捕获的错误


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

finally的正常用例是当您不想用相同的方法捕获异常时


在这种情况下,您可以使用try with finally块,而无需捕获。通过这种方式,您可以确保关闭资源,而不必捕获方法本身的异常。

虽然本身没有完整的答案,但这两个使用
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

原因可在上找到

带有finally块的try语句通过首先执行try块来执行。还有一个选择:

•如果try块的执行由于抛出值而突然完成 五、 然后有一个选择:
[…]
–如果V的运行时类型与可捕获的分配不兼容 try语句的任何catch子句的异常类,然后 块被执行。然后有一个选择:
[…]
如果finally块由于原因S而突然完成,则try语句 由于原因S而突然完成(并且值V的抛出被丢弃并且 已忘记)。

如果try块的执行由于任何其他原因而突然完成,则执行finally块,然后有一个选择:
–如果finally块正常完成,则try语句完成 由于某种原因而突然 –如果finally块由于原因S而突然完成,则try语句 由于原因S而突然完成(并且原因R被丢弃)。

编辑矿山


假设
返回是一种完成
try
finally
块的突然方式。

在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块中的代码在 控件可能离开方法的所有位置。任何
尝试
块
具有
finally
但不是catch-all处理程序的处理程序相当于具有立即抛出的catch-all处理程序的处理程序(处理器可以在catch-all处理程序之前插入
finally
代码的副本)。

一个较小的示例:

PrintWriter out = null;
try {
    out = new PrintWriter();
    out.print(data);
} finally {
    out.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
    部分,foll