1.Java内置了一套异常处理机制,总是使用异常来表示错误。

异常是一种class,因此它本身带有类型信息。异常可以在任何地方抛出,但只需要在上层捕获,这样就和方法调用分离了。

2.异常继承关系

Java之异常处理 - 图1

从继承关系可知:Throwable是异常体系的根,它继承自Object。Throwable有两个体系:Error和Exception,Error表示严重的错误,程序对此一般无能为力,例如:

OutOfMemoryError:内存耗尽

NoClassDefFoundError:无法加载某个Class

StackOverflowError:栈溢出

而Exception则是运行时的错误,它可以被捕获并处理。

某些异常是应用程序逻辑处理的一部分,应该捕获并处理。例如:

NumberFormatException:数值类型的格式错误

FileNotFoundException:未找到文件

SocketException:读取网络失败

还有一些异常是程序逻辑编写不对造成的,应该修复程序本身。例如:

NullPointerException:对某个null的对象调用方法或字段

IndexOutOfBoundsException:数组索引越界

Exception又分为两大类:

RuntimeException以及它的子类;

非RuntimeException(包括IOException、ReflectiveOperationException等等)

Java规定

必须捕获的异常,包括Exception及其子类,但不包括RuntimeException及其子类,这种类型的异常称为Checked Exception。

不需要捕获的异常,包括Error及其子类,RuntimeException及其子类。

捕获异常

1.捕获异常使用try…catch语句,把可能发生异常的代码放到try {…}中,然后使用catch捕获对应的Exception及其子类。

2.多个catch语句只有一个能被执行,所以多个catch语句的匹配顺序非常重要,子类必须放在前面。

  1. public static void main(String[] args) {
  2. try {
  3. process1();
  4. process2();
  5. process3();
  6. } catch (UnsupportedEncodingException e) {
  7. System.out.println("Bad encoding");
  8. } catch (IOException e) {
  9. System.out.println("IO error");
  10. }
  11. }


3.finally
finally语句保证了有无异常都会执行,它是可选的。
finally的特点:

  • finally语句不是必须的,可写可不写;
  • finally总是最后执行。

一个catch语句也可以匹配多个非继承关系的异常。

抛出异常

1.通过printStackTrace()可以打印出方法的调用栈, 对于调试错误非常有用。
2.如何抛出异常?
(1) 创建某个Exception的实例;
(2) 用throw语句抛出。

  1. void process2(String s) {
  2. if (s == null) {
  3. NullPointerException e = new NullPointerException();
  4. throw e;
  5. //throw new NullPointerException();
  6. }
  7. }

3.异常屏蔽
如果在执行finally语句时抛出异常,那么,catch语句的异常不能继续抛出,如

  1. public class Main {
  2. public static void main(String[] args) {
  3. try {
  4. Integer.parseInt("abc");
  5. } catch (Exceptione) {
  6. System.out.println("catched");
  7. throw new RuntimeException(e);
  8. } finally {
  9. System.out.println("finally");
  10. throw new IllegalArgumentException();
  11. }
  12. }
  13. }
  14. 输出:
  15. catched
  16. finally
  17. Exception in thread "main" java.lang.IllegalArgumentException
  18. at Main.main(Main.java:11)

在极少数的情况下,我们需要获知所有的异常。如何保存所有的异常信息?方法是先用origin变量保存原始异常,然后调用Throwable.addSuppressed(),把原始异常添加进来,最后在finally抛出:

  1. public class Main {
  2. public static void main(String[] args) throws Exception {
  3. Exception origin = null;
  4. try {
  5. System.out.println(Integer.parseInt("abc"));
  6. } catch (Exception e) {
  7. origin = e;
  8. throw e;
  9. } finally {
  10. Exception e = new IllegalArgumentException();
  11. if (origin != null) {
  12. e.addSuppressed(origin);
  13. }
  14. throw e;
  15. }
  16. }
  17. }
  18. 输出:
  19. Exception in thread "main" java.lang.IllegalArgumentException at Main.main(Main.java:11)
  20. Suppressed: java.lang.NumberFormatException: For input string: "abc" at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
  21. at java.base/java.lang.Integer.parseInt(Integer.java:652)
  22. at java.base/java.lang.Integer.parseInt(Integer.java:770)
  23. at Main.main(Main.java:6)

自定义异常

1.Java标准库定义的常用异常

Exception

├─ RuntimeException
│ │
│ ├─ NullPointerException
│ │
│ ├─ IndexOutOfBoundsException
│ │
│ ├─ SecurityException
│ │
│ └─ IllegalArgumentException
│ │
│ └─ NumberFormatException

├─ IOException
│ │
│ ├─ UnsupportedCharsetException
│ │
│ ├─ FileNotFoundException
│ │
│ └─ SocketException

├─ ParseException

├─ GeneralSecurityException

├─ SQLException

└─ TimeoutException

2.自定义异常方法

在一个大型项目中,可以自定义新的异常类型,但是,保持一个合理的异常继承体系是非常重要的。

一个常见的做法是自定义一个BaseException作为“根异常”,然后,派生出各种业务类型的异常。

BaseException需要从一个适合的Exception派生,通常建议从RuntimeException派生:

public class BaseException extends RuntimeException {
    public BaseException() {
        super();
    }

    public BaseException(String message, Throwable cause) {
        super(message, cause);
    }

    public BaseException(String message) {
        super(message);
    }

    public BaseException(Throwable cause) {
        super(cause);
    }
}


其他业务类型的异常就可以从BaseException派生:

public class UserNotFoundException extends BaseException {
}

public class LoginFailedException extends BaseException {
}