Java 的异常是 class,它的继承关系如下:
┌───────────┐│ Object │└───────────┘▲│┌───────────┐│ Throwable │└───────────┘▲┌─────────┴─────────┐│ │┌───────────┐ ┌───────────┐│ Error │ │ Exception │└───────────┘ └───────────┘▲ ▲┌───────┘ ┌────┴──────────┐│ │ │┌─────────────────┐ ┌─────────────────┐┌───────────┐│OutOfMemoryError │... │RuntimeException ││IOException│...└─────────────────┘ └─────────────────┘└───────────┘▲┌───────────┴─────────────┐│ │┌─────────────────────┐ ┌─────────────────────────┐│NullPointerException │ │IllegalArgumentException │...└─────────────────────┘ └─────────────────────────┘
Throwable 是异常体系的根,它继承自 Object。Throwable 分为 Error 和 Exception 两类,Error 表示严重的错误,而 Exception 则是运行时的错误,通常都会捕获并处理。Exception 又细分为 RuntimeException 和 非 RuntimeException(IOException等)。
通常情况下,
Error是无需捕获的严重错误,Exception是应该捕获的可处理的错误。RuntimeException及其子类无需强制捕获,非RuntimeException(Checked Exception)需强制捕获,或者用throws声明;
捕获异常
多 catch 语句
可以使用多个 catch 语句,每个 catch 分别捕获对应的 Exception 及其子类。JVM 在捕获到异常后,会从上到下匹配 catch 语句,匹配到某个 catch 后,执行 catch 代码块,然后不再继承匹配。
使用多 catch 语句时,子类 Exception catch语句必须写在前面,否则异常会被父类捕获处理
public static void main(String[] args) {try {process1();process2();process3();} catch (UnsupportedEncodingException e) {// UnsupportedEncodingException 是 IOException 子类System.out.println("Bad encoding");} catch (IOException e) {System.out.println("IO error");}}
finally 语句
finally 语句中声明了必须执行的代码。如果没有发生异常,就正常执行 try {...} 语句块,然后执行 finally 语句。如果发生了异常,就中断执行 try {...} 语句块,然后跳转执行匹配的 catch 语句块,最后执行 finally 语句块。在 catch 语句块中使用 return 语句不会影响 finally 语句块的执行。
var str = "a";try {int ii = Integer.parseInt(str);} catch (NumberFormatException e) {e.printStackTrace();System.out.println("catch");return;} finally {System.out.println("finally");}
常用异常
Java 标准库定义的常用异常如下:
Exception│├─ RuntimeException│ ││ ├─ NullPointerException│ ││ ├─ IndexOutOfBoundsException│ ││ ├─ SecurityException│ ││ └─ IllegalArgumentException // 参数检查不合法│ ││ └─ NumberFormatException // 数值类型格式化错误│├─ IOException│ ││ ├─ UnsupportedCharsetException│ ││ ├─ FileNotFoundException│ ││ └─ SocketException│├─ ParseException│├─ GeneralSecurityException│├─ SQLException│└─ TimeoutException
自定义异常
项目开发中,通常会封装包含业务语义的自定义的异常。常见做法是自定义一个 BaseException 作为“根异常”,然后派生出各种对应业务语义类型的异常。自定义异常通常还和错误码设计搭配组合使用
// 根异常public class BaseException extends RuntimeException {public BaseException(String message, Throwable cause) {super(message, cause);}public BaseException(String message) {super(message);}public BaseException(Throwable cause) {super(cause);}}// 不包含该用户、用户未找到public class UserNotFoundException extends BaseException {}// 未登录、登录失败public class LoginFailedException extends BaseException {}
NullPointerException
NullPointerException 空指针异常,简称 NPE。如果一个对象为 null ,调用其方法或访问其字段就会产生 NullPointerException。 实际上,NullPointerException 是一种代码逻辑错误,一个好的编码习惯可以极大的降低这种异常的产生。
- 成员变量在定义时给定初始值
String类型默认值使用""空字符串而不是默认的null- 方法返回时尽量避免返回
null,而是返回""空字符、空对象、空集合。如果调用方需要用到null,推荐返回Optional<T>对象,调用方可以通过Optional.isPresent()方法判断是否有结果public Optional<String> readFromFile(String file) {if (!fileExist(file)) {return Optional.empty();}...}
