异常机制和分类
- 什么是异常?
- 异常(Exception)是指再程序运行期间可能会发生的错误或意外情况,这些情况可以干扰程序的正常执行流程,导致程序出现异常或崩溃。
Design for failure
通过使用异常处理机制,程序可以更好地应对错误和意外情况,提高程序的健壮性和可靠性
-
关于异常的 What/Where/Why
- 异常类型回答了 “什么” 被抛出
- 异常堆栈跟踪回答了 “在哪” 抛出
- 异常信息回答了 “为什么” 会抛出
-
异常分类
- 按继承结构
- Error 与 Exception
- 按编译器检查角度
- CheckedException 与 UnCheckedException
- 按继承结构
如何进行异常处理
-
异常关键字
- try:把可能出现异常代码放在try语句块之内,当try语句块内发生异常时,异常就被抛出
- catch:用于捕获异常。catch用于捕获try语句块中发生的异常
- finally:用于try中打开的资源回收,常常用try-catch-finally
- throw:用于抛出异常
- throws:用在方法签名中,用于声明该方法可能抛出的异常
-
3种异常的处理机制
- 声明受检异常
- 抛出运行时异常
- 捕获异常
-
如何自定义异常
- 自定义非受检异常:继承RuntimeException
- 自定义受检异常:继承Exception
- 自定义异常最佳实践分析:AWS Java SDK异常设计案例
异常处理的最佳实践
- 不要忽略捕捉的异常
public static void method2(String filePath) {
File file = new File(filePath);
String line;
try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
while ((line = reader.readLine()) != null) {
System.out.println("Line: " + line);
}
} catch (IOException e) { // 错误示范:什么都不做,没有任何意义
}
}
- 捕获具体的异常而不是 Exception
public static void method1(String filePath) {
File file = new File(filePath);
String line;
try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
while ((line = reader.readLine()) != null) {
System.out.println("Line: " + line);
}
} catch (Exception e) { // 错误示范:不要写Exception,没有任何意义
System.out.println("File Not Found!!!");
e.printStackTrace();
// throw new RuntimeException(e);
}
}
- 只捕获实际可处理的异常(不会发生的不处理)
public static void method3(String filePath) {
File file = new File(filePath);
String line;
try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
while ((line = reader.readLine()) != null) {
System.out.println("Line: " + line);
}
} catch (NoSuchMethodException e) { // 错误示范:Exception 'java.lang.NoSuchMethodException' is never thrown in the corresponding try block
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace(); // 生产环境不要用 printStackTrace()
}
}
- 生产环境不要用
printStackTrace() - 在方法签名上要声明具体受检异常并在javadoc中体现
/**
* Do something according to complete flag
* @param complete flag indicated whether complete or not
* @throws IOException
* @throws SQLException
*/
public static void doSomething(boolean complete) throws IOException, SQLException {
System.out.println("Doing something");
if (!complete) {
// 抛出一个异常
// throw new RuntimeException("The flag complete is false");
throw new IOException("The flag complete is false");
}
System.out.println("Completed!");
throw new SQLException("SQL Exception!!!");
}
- 用
finally或try-with-resource关闭或释放资源 - 先捕获子类异常,再捕获父类异常
public static String readFileV2(String filePath) {
File file = new File(filePath);
String line;
try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
while ((line = reader.readLine()) != null) {
System.out.println("Line: " + line);
}
return "OK";
} catch (FileNotFoundException e) { // 把子类,范围小的放在前面catch,进行处理
System.out.println("File Not Found!!!");
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
- Throw early, catch late.(late 不一定是越晚越好)
- 捕获异常不要捕获 Throwable
- 封装异常再抛出时不要忘记原来的 cause
public static void testUncheckedException() {
IndexOutOfBoundsException exception = new IndexOutOfBoundsException("Index is out of bounds");
throw new CustomizeUncheckedException("Unchecked Exception met!", exception); // 不要忘记原始的cause(exception)
}
- 能尽量使用JDK标准异常就不要自定义
- 不要同时抛出异常和log异常