Java Finally块–异常处理

时间:2020-01-09 10:34:56  来源:igfitidea点击:

当代码中引发异常时,该方法的正常执行流程将中断,这可能导致打开的资源永不关闭。在这种情况下,我们确实需要一种清理机制,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语句。