最后有时让我感到困惑

Finally sometimes confuse me

今天在大学里我们谈了一些关于 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 之后的区别吗?我知道 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 是完成 tryfinally 块的一种突然方式。

在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 内,因此那里的任何异常都将抢占正在进行的任何异常。简短的建议是尽量避免可能从 finallycatch.

内部抛出的东西