最后有时让我感到困惑
Finally sometimes confuse me
今天在大学里我们谈了一些关于 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();
}
在finally
中关闭文件和我们只是这样做有什么区别:
PrintWriter out = null;
try {
out = new PrintWriter(...); // We open file here
} catch (Exception e) {
e.printStackTrace();
}
out.close();
catch之后的这段代码会一直执行
你能给我一些很好的例子来说明我们使用 finally
和将代码放在 catch 之后的区别吗?我知道 finally 总是会执行,但是程序也会在 catch 块之后保持 运行。
如果代码抛出 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
.
上失败
如果在try
块中发生了未捕获的错误,或者即使在catch
块中发生了错误,也不会执行catch之后的'piece of code',但是finally
块将。
finally
will always be executed.
来自 Java 文档:
The finally block always executes when the try block exits. This
ensures that the finally block is executed even if an unexpected
exception occurs. But finally is useful for more than just exception
handling — it allows the programmer to avoid having cleanup code
accidentally bypassed by a return, continue, or break. Putting cleanup
code in a finally block is always a good practice, even when no
exceptions are anticipated.
如果 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();
}
如果 PrintWriter
.
的构造函数中发生异常,您的第二个示例可能会抛出不需要的 NullPointerException
另一种可能性是out.close();
会抛出一个错误,但没有被捕获。
如果您将代码移动到 finally
块中,它将始终执行 - 无论 try 块是否成功。如果您的 try
块引发了 未 捕获的异常,这 特别 有用。在您的第二个示例中,这将导致 out.close()
不被执行,而对于 finally 块,即使 try
块抛出未捕获的错误,它也会被执行。
finally 的正常用例是当您不想在同一方法中捕获异常时。
在这种情况下,您可以使用带有 finally 块的 try 而没有捕获。这样您就可以确保您的资源已关闭,而不必在方法本身中捕获异常。
虽然答案本身并不完整,但try-finally
(错误)使用的这两个示例可能具有启发性:
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
上找到
A try statement with a finally block is executed by first executing the try block. Then there is a choice:
• If execution of the try block completes abruptly because of a throw of a value
V, then there is a choice:
[...]
– If the run-time type of V is not assignment compatible with a catchable
exception class of any catch clause of the try statement, then the finally
block is executed. Then there is a choice:
[...]
› If the finally block completes abruptly for reason S, then the try statement
completes abruptly for reason S (and the throw of value V is discarded and
forgotten).
• If execution of the try block completes abruptly for any other reason R, then the finally block is executed, and then there is a choice:
– If the finally block completes normally, then the try statement completes
abruptly for reason R.
– If the finally block completes abruptly for reason S, then the try statement
completes abruptly for reason S (and reason R is discarded).
编辑我的
认为 return
是完成 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 块中的代码在
控制可能离开方法的所有地方。任何 try
块
它有一个 finally
但不是 catch-all 处理程序相当于一个具有立即抛出的 catch-all 处理程序的处理程序(然后处理器可以在 catch-all 之前插入 finally
代码的副本处理程序。
一个更小的例子:
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
,可能在调用者中.
编写 finally
块时请务必小心 - 它不在 try
内,因此那里的任何异常都将抢占正在进行的任何异常。简短的建议是尽量避免可能从 finally
或 catch
.
内部抛出的东西
今天在大学里我们谈了一些关于 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();
}
在finally
中关闭文件和我们只是这样做有什么区别:
PrintWriter out = null;
try {
out = new PrintWriter(...); // We open file here
} catch (Exception e) {
e.printStackTrace();
}
out.close();
catch之后的这段代码会一直执行
你能给我一些很好的例子来说明我们使用 finally
和将代码放在 catch 之后的区别吗?我知道 finally 总是会执行,但是程序也会在 catch 块之后保持 运行。
如果代码抛出 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
.
如果在try
块中发生了未捕获的错误,或者即使在catch
块中发生了错误,也不会执行catch之后的'piece of code',但是finally
块将。
finally
will always be executed.
来自 Java 文档:
The finally block always executes when the try block exits. This ensures that the finally block is executed even if an unexpected exception occurs. But finally is useful for more than just exception handling — it allows the programmer to avoid having cleanup code accidentally bypassed by a return, continue, or break. Putting cleanup code in a finally block is always a good practice, even when no exceptions are anticipated.
如果 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();
}
如果 PrintWriter
.
NullPointerException
另一种可能性是out.close();
会抛出一个错误,但没有被捕获。
如果您将代码移动到 finally
块中,它将始终执行 - 无论 try 块是否成功。如果您的 try
块引发了 未 捕获的异常,这 特别 有用。在您的第二个示例中,这将导致 out.close()
不被执行,而对于 finally 块,即使 try
块抛出未捕获的错误,它也会被执行。
finally 的正常用例是当您不想在同一方法中捕获异常时。
在这种情况下,您可以使用带有 finally 块的 try 而没有捕获。这样您就可以确保您的资源已关闭,而不必在方法本身中捕获异常。
虽然答案本身并不完整,但try-finally
(错误)使用的这两个示例可能具有启发性:
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
上找到A try statement with a finally block is executed by first executing the try block. Then there is a choice:
• If execution of the try block completes abruptly because of a throw of a value V, then there is a choice:
[...]
– If the run-time type of V is not assignment compatible with a catchable exception class of any catch clause of the try statement, then the finally block is executed. Then there is a choice:
[...]
› If the finally block completes abruptly for reason S, then the try statement completes abruptly for reason S (and the throw of value V is discarded and forgotten).• If execution of the try block completes abruptly for any other reason R, then the finally block is executed, and then there is a choice:
– If the finally block completes normally, then the try statement completes abruptly for reason R.
– If the finally block completes abruptly for reason S, then the try statement completes abruptly for reason S (and reason R is discarded).
编辑我的
认为 return
是完成 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 块中的代码在
控制可能离开方法的所有地方。任何 try
块
它有一个 finally
但不是 catch-all 处理程序相当于一个具有立即抛出的 catch-all 处理程序的处理程序(然后处理器可以在 catch-all 之前插入 finally
代码的副本处理程序。
一个更小的例子:
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
,可能在调用者中.
编写 finally
块时请务必小心 - 它不在 try
内,因此那里的任何异常都将抢占正在进行的任何异常。简短的建议是尽量避免可能从 finally
或 catch
.