Java Finally块–异常处理
当代码中引发异常时,该方法的正常执行流程将中断,这可能导致打开的资源永不关闭。在这种情况下,我们确实需要一种清理机制,Java的最终阻止对清理机制有帮助。
例如,假设我们使用一种方法打开文件并开始使用输入流读取文件。在读取流时,将引发异常,并且永远不会执行关闭流的代码。这意味着流仍在使用资源,因为它从未关闭过。
通过使用finally块并将代码关闭在该finally块中,可以确保始终执行关闭流的代码。
Finally块
Java异常处理中的finally块可以立即跟随try块,使其成为try-finally块,或者我们可以拥有完整的try-catch-finally块。在try块之后必须是catch或者最后,当然我们也可以将两者都变成完整的try-catch-finally块。
无论是否在try块中引发异常,Java中的final块始终执行。如果try块内没有抛出异常,则try块存在时将执行finally块。
如果在try块中引发了异常,并且有一个catch块与所引发的异常的异常类型相匹配,则catch块首先执行,然后是finally块。万一catch块无法处理引发的异常,则在方法返回之前,仍然会执行block。
Java Finally代码块示例
让我们尝试通过示例Java程序查看这些场景
当catch块在那里并处理引发的异常时
public class ExceptionDemo { public static void main(String[] args) { ExceptionDemo ed = new ExceptionDemo(); double result = ed.division(7, 0); System.out.println("result is - " + result); } private double division(int num1, int num2){ double result; try{ result = num1/num2; }catch(ArithmeticException exp){ System.out.println("Exception occurred while dividing" + exp.getMessage()); // assigining zero to result result = 0; }finally{ System.out.println("in finally block"); } return result; } }
输出:
Exception occurred while dividing/ by zero in finally block result is - 0.0
当catch块存在但不处理引发的异常时
public class ExceptionDemo { public static void main(String[] args) { ExceptionDemo ed = new ExceptionDemo(); double result = ed.division(7, 0); System.out.println("result is - " + result); } private double division(int num1, int num2){ double result; try{ result = num1/num2; }catch(NullPointerException exp){ System.out.println("Exception occurred while dividing" + exp.getMessage()); // assigining zero to result result = 0; }finally{ System.out.println("in finally block"); } return result; } }
在catch块中,异常类型更改为NullPointerException,其中抛出的异常是ArithmeticException。在这种情况下,finally块仍将执行,然后将异常传播以由默认处理程序处理。
输出:
in finally block Exception in thread "main" java.lang.ArithmeticException: / by zero at com.theitroad.ExceptionDemo.division(ExceptionDemo.java:14) at com.theitroad.ExceptionDemo.main(ExceptionDemo.java:7)
如果在finally块中抛出异常会发生什么情况
如果try块中包含的代码引发异常,而finally块也引发异常,则finally子句引发的异常将掩盖try块中引发的异常。
让我们看一个例子来澄清它。假设我们具有读取文件的代码,并且传递的文件在给定路径中不存在。在这种情况下,将从try块引发FileNotFoundException。我们还有一个finally块,其中具有关闭BufferedReader对象的代码,但在这种情况下为null,因此finally块也将引发NullPointerException。
import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; public class FinallyDemo { public static void main(String[] args){ BufferedReader br = null; try { br = new BufferedReader(new InputStreamReader( new FileInputStream( new File("D:\test1.txt")))); //br = null; } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally{ System.out.println("In finally block"); try { br.close(); } catch (NullPointerException e) { // TODO Auto-generated catch block e.printStackTrace(); }catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
输出:
java.io.FileNotFoundException: D:\test1.txt (The system cannot find the file specified) at java.io.FileInputStream.open0(Native Method) at java.io.FileInputStream.open(Unknown Source) at java.io.FileInputStream.(Unknown Source) at com.theitroad.FinallyDemo.main(FinallyDemo.java:16) In finally block java.lang.NullPointerException at com.theitroad.FinallyDemo.main(FinallyDemo.java:25)
因此,我们应该避免在finally子句中引发异常。在上面的代码中,我们可以通过if条件检查是否为null至少避免NullPointerException。
if(br != null){ try { br.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
Java带return语句的finally块
即使try块中有return语句,并且try块中没有抛出异常,finally块仍将在方法返回之前执行。
public class FinallyDemo { public static void main(String[] args) { FinallyDemo fd = new FinallyDemo(); double result = fd.division(8, 4); System.out.println("result is - " + result); } private double division(int num1, int num2){ double result; try{ System.out.println("In try block"); return num1/num2; }finally{ System.out.println("in finally block"); } } }
输出:
In try block in finally block result is – 2.0
如果在finally子句中也有return语句,则try语句返回的值将替换为finally块返回的值。此外,它还将抑制从try块引发的任何异常。这就是为什么在finally块中不应有任何return语句的原因。将finally块用于清理代码。
返回finally块示例
如果我们使用上面使用的示例代码并将return语句放置在finally块中,则将返回该值。另请注意,有尝试将其除以零,但该异常被抑制了。
public class FinallyDemo { public static void main(String[] args) { FinallyDemo fd = new FinallyDemo(); double result = fd.division(8, 0); System.out.println("result is - " + result); } private double division(int num1, int num2){ double result; try{ System.out.println("In try block"); return num1/num2; }finally{ System.out.println("in finally block"); return 6; } } }
输出:
In try block in finally block result is – 6.0
如果在finally块中注释return语句,然后运行程序,则可以看到异常。
Exception in thread "main" In try block in finally block java.lang.ArithmeticException: / by zero at com.theitroad.FinallyDemo.division(FinallyDemo.java:17) at com.theitroad.FinallyDemo.main(FinallyDemo.java:9)
什么时候Java Finally块不执行
虽然无论是否在try块中引发任何异常,finally块将始终执行。但是,在某些情况下,Java中的finally块没有执行。
如果在执行try或者catch代码的同时退出JVM(System.exit()或者JVM崩溃),则finally块可能不会执行。同样,如果执行try或者catch代码的线程被中断或者杀死,即使整个应用程序继续运行,finally块也可能不会执行。
重要事项
最后,Java中的代码块用于清理代码。我们应该将代码放在finally块中以关闭打开的资源(输入流,输出流,打开的DB连接)。
虽然finally块是可选的,但try块必须紧随catch或者finally块之后。我们也可以使用完整的try-catch-finally块。
无论try块是否无异常退出或者由于异常而退出,总是执行finally块。
只有当JVM退出或者执行try或者catch代码的线程被中断或者杀死时,finally块才不会执行。
如果finally块引发异常,则该异常将掩盖try块中引发的异常。
如果在finally块中有一个return语句,则返回值。
在finally块中包含return语句可能会抑制引发的异常,因此在finally块中不应存在return语句。