Java 异常处理机制
Exception 和 Error 有什么区别?
Exception 和Error 都是继承 Throwable 类,在 java 只有 Throwable 类型的实例才可以被抛出(throw)或者捕获(catch),它是异常处理机制的基本组成类型。Exception 和 Error 提现了 Java 平台设计者不同异常情况的类,Exception 在程序运行中,可预料的意外情况,并且可能被捕获,进行相应处理。
- Error 是指在正常情况下,不大可能出现的情况,可能是因为JVM自身导致的,非正常情况,不便于捕获,常见的比如 OutOfMemorryError 之类的,都是Error 的子类。
- Exception 又分为可检测异常(checked)和不检查异常(unchecked),可检查异常在源代码里必须显示的进行捕获处理。
Exception 可检测异常和不检查异常有哪些?
- 不可检测异常(RunntimeException):NullPointerException、ClasCastException、ArrayIndexOutOfBoundsException ;
- 检查异常: 如 IO 错误导致的 IOException 、SQLException。
异常处理的基本原则
尽量不要捕获类似 Exception 这样的通用异常
try { // 业务代码 // … Thread.sleep(1000L); } catch (Exception e) { // Ignore it }
上面的代码有什么问题?
根据异常处理的基本原则,不要使用 Exception 这样的通用异常,需要捕获特点异常,上面的代码 Thread.sleep 会抛出 InterruptedException 应该捕获这个异常,软件工程师需要协作的,程序员有义务让代码能够直观体现出更多的信息。
其次不要生吞了异常,不能 ingonore ,当不需要抛出异常时,需要打印出相关异常信息。否则程序很可能以不可控的方式结束,不能够判断是哪里出现了异常。
不要生吞异常,(就是不要基于假设这段代码可能不会发生,或者感觉异常无所谓)如果这样很可能会导致非常难以诊断的诡异异常。
try { // 业务代码 // … } catch (IOException e) { e.printStackTrace(); }
- https://docs.oracle.com/javase/9/docs/api/java/lang/Throwable.html#printStackTrace-- 生吞异常是指不把异常抛处理,也不输出日志Logger.这样很难定位是什么原因产生了异常。
“ Prints this throwable and its backtrace to the standard error stream ” 上面的代码可能出现的问题时,当系统复杂是,无法找到堆栈轨迹,不知道日志输出到哪了。
throw early ,catch late 原则
public void readPreferences(String fleName){ //...perform operations... InputStream in = new FileInputStream(fleName); //...read the preferences fle... }
比如说当从输入参数 filename 为null 时,程序就会抛出 NullPointerException ,但是由于没有爆出这个问题,堆栈信息可能让人费解,要做到的是就是在发现问题的时候,第一时间抛出,能够更清晰的反应问题。这个就是 throw early。这样,根据 throw early 就可以将代码改造如下:
public void readPreferences(String flename) { Objects. requireNonNull(flename); //...perform other operations... InputStream in = new FileInputStream(flename); //...read the preferences fle... }
catch late
捕获异常后,要怎么处理,要么生吞异常。要么可以选择保留异常的cause 信息,直接再抛出去或者创建新的异常,抛出去,这样更高层面,往往可以更清楚处理方式是什么。
如何自定义异常
- 是否需要定义成 Checked Exception ,因为这种类型设计是为了从异常中恢复出来。
- 保证诊断信息足够的同时,也要考虑包含敏感信息,因为那样可能存在安全问题,比如包含机器 IP 端口,用户数据等,一定要小心,不要输出到日志。
参考资料