1 什么是异常

如果某个方法无法按照正常的途径完成任务,则需要提供另外一种途径退出方法。在这种情况下会抛出一个封装了错误信息的对象。此时,这个方法立即退出不返回任何值。此外,调用这个方法的其他代码也无法继续执行,异常处理机制将代码执行交给异常处理器。
Throwable 是所有异常或错误的父类,其子类有 Error 和 Exception,Exception 的子类有 RuntimeException 和 IOException 。Exception 能被程序本身处理(try-catch), Error 是无法处理的(只能尽量避免)。

Exception 可以分为运行时异常和编译时异常。

  • 运行时异常(RuntimeException):都是非受查异常(此外,还包括Error),在运行过程中才出现,Java编译器不会检查。程序可以选择处理或者不处理。

常见的几个运行时异常:

  • NullPointerException(空指针异常)
  • ArithmeticException(运算异常)
  • ClassCastException(类转换异常)
  • ArrayIndexOutOfBoundsException(数组越界异常)
  • StringIndexOutOfBoundsException(字符串下标越界异常)
    • 编译时异常:除了RuntimeException及其子类以外,其它Exception类及其子类都是受查异常。程序必须进行处理的异常,如果不处理,编译就不能通过。

常见的几个编译时异常:

  • FileNotFoundException
  • ClassNotFoundException
  • SQLException
  • NoSuchFieldException
  • NoSuchMethodException

2 Java异常类层次结构图

Java异常类层次结构图.png
Java异常类层次结构图2.png

3 Exception和Error的区别

  • Error,指Java运行时系统的内部错误和资源耗尽错误。例如,Java 虚拟机运行错误(Virtual MachineError)、虚拟机内存不够错误(OutOfMemoryError)、类定义错误(NoClassDefFoundError)等 。应用程序不会抛出该类对象。一旦发生这些异常,JVM一般选择线程终止。
  • Exception,指程序在运行过程中出现了各种意外事件,这些事件可以被Java异常处理机制处理。

    4 受查异常和非受查异常

  • 受查异常:除了RuntimeException及其子类以外,其他的Exception类及其子类都属于受检查异常 。常见的受检查异常有: IO 相关的异常、ClassNotFoundExceptionSQLException

    • 受查异常必须被catch/throw 处理,否则不能通过编译。
  • 非受查异常:RuntimeException 及其子类都统称为非受检查异常,例如:NullPointerExceptionNumberFormatException(字符串转换为数字)、ArrayIndexOutOfBoundsException(数组越界)、ClassCastException(类型转换错误)、ArithmeticException(算术错误)等。

    • 非受查即使不处理,也能通过编译。
    • 除了RuntimeException 及其子类以外,Error也是非受查异常。

      5 Throw和Throws的区别

  • throws 用于函数外,后面紧跟异常类,可以有多个异常类;throw 用在函数内,只能抛出一种异常类。

  • throws 用于声明可能发生的异常,且该异常不一定发生;throw 则是抛出异常,一旦执行则必然发生异常。
  • 二者遇到异常都仅仅抛出,而不是进行处理,真正的处理由函数的上层调用处理。

    6 try-catch-finally

  • try块: 用于捕获异常。后面可以接零个或多个 catch 块,如果没有 catch 块,则必须跟一个 finally 块。

  • catch块: 用于处理 try 捕获到的异常,参数为可能的异常类型。
  • finally 块: 无论是否捕获或处理异常,finally 块里的语句都会被执行。当在 try 块或 catch 块中遇到 return 语句时,finally 语句块将在方法返回之前被执行。

注意:

  • 当 try 语句块和 finally 语句块中都有 return 语句时,在方法返回之前,finally 语句块的内容将被执行,并且 finally 语句块的返回值将会覆盖原始的返回值。
  • finally语句块中无法对try语句块中的return值进行修改。 ```java package org.example;

public class TryCatchTest { public static void main(String[] args) { System.out.println(r(1)); System.out.println(r2(1)); } public static int r(int i) { try { return i; } finally { return i + 1; } } public static int r2(int i) { try { return i; } finally { i++; } } }

  1. 输出结果:
  2. ```java
  3. 2
  4. 1

原因:当try语句块中调用了return,则会把需要return的值进行保存,然后再执行finally语句块。执行完finally语句块后,再返回来获取保存的return值进行return。

  • 当finally语句块中也有return时,则直接就从finally中return出去了,不会返回去获取之前保存的return进行return。
  • 所以:在finally中修改try中return值是不会生效的。因为此时return的值已经被保存了。

在下面三种情况,finally语句块不会被执行:

  • tryfinally块中用了 System.exit(int)退出程序。但是,如果 System.exit(int) 在异常语句之后,finally 还是会被执行。
  • 程序所在的线程死亡。
  • 关闭 CPU。

7 try-with-resources

  • 适用范围(资源的定义): 任何实现 java.lang.AutoCloseable或者 java.io.Closeable 的对象
  • 关闭资源和 finally 块的执行顺序:try-with-resources 语句中,任何 catch 或 finally 块在声明的资源关闭后运行

《Effecitve Java》中明确指出:

面对必须要关闭的资源,我们总是应该优先使用 try-with-resources 而不是try-finally。随之产生的代码更简短,更清晰,产生的异常对我们也更有用。try-with-resources语句让我们更容易编写必须要关闭的资源的代码,若采用try-finally则几乎做不到这点。

Java 中类似于InputStreamOutputStreamScannerPrintWriter等的资源都需要我们手动调用close()方法来关闭,一般情况下我们都是通过try-catch-finally语句来实现这个需求。
在JDK7之后,可以使用try-with-resources来实现自动关闭。多个资源之间用 ; 分隔开。

  1. package org.example;
  2. import java.io.*;
  3. import java.util.Scanner;
  4. public class TryResourcesTest {
  5. public static void main(String[] args) {
  6. tryCatch();
  7. System.out.println("=========");
  8. tryResources();
  9. tryResources2();
  10. }
  11. public static void tryCatch() {
  12. Scanner sc = null;
  13. try {
  14. sc = new Scanner(new File("G:\\javaCode\\demo_code\\demo\\src\\org\\example\\tryResources.txt"));
  15. while (sc.hasNext()) {
  16. System.out.println(sc.nextLine());
  17. }
  18. } catch (FileNotFoundException e) {
  19. e.printStackTrace();
  20. } finally {
  21. if (sc != null) {
  22. sc.close();
  23. }
  24. }
  25. }
  26. public static void tryResources() {
  27. try (Scanner sc = new Scanner(new File("G:\\javaCode\\demo_code\\demo\\src\\org\\example\\tryResources.txt"));) {
  28. while (sc.hasNext()) {
  29. System.out.println(sc.nextLine());
  30. }
  31. } catch (FileNotFoundException e) {
  32. e.printStackTrace();
  33. }
  34. }
  35. // 多个资源之间用 ; 隔开
  36. public static void tryResources2() {
  37. try (BufferedInputStream bis = new BufferedInputStream(
  38. new FileInputStream(new File("G:\\javaCode\\demo_code\\demo\\src\\org\\example\\tryResources.txt")));
  39. BufferedOutputStream bos = new BufferedOutputStream(
  40. new FileOutputStream(new File("G:\\javaCode\\demo_code\\demo\\src\\org\\example\\tryResources2.txt")))
  41. ) {
  42. int next;
  43. while ((next = bis.read()) != -1 ) {
  44. bos.write(next);
  45. }
  46. System.out.println("输出完毕!");
  47. } catch (FileNotFoundException e) {
  48. e.printStackTrace();
  49. } catch (IOException e) {
  50. e.printStackTrace();
  51. }
  52. }
  53. }

参考