异常机制
异常的本质是由程序员通过编程来提高程序的鲁棒性(容错性)。
异常处理
基本写法
try {
// 程序逻辑
} catch (Exception1 ex) {
// 异常处理
} catch (Exception2 ex) {
// 异常处理
} finally {
// 异常处理
}
catch
语句可以有零个至多个,可以没有 finally
语句
Java 中的异常处理流程是:
- 抛出(throw) 异常
- 运行时系统在调用栈中 查找异常
- 从异常生成的方法开始 回溯,直到找到;
- 捕获(catch) 异常的代码
Java 中 Throwable
是所有异常的父类,它下面分成两类:
Error
:JVM 错误Exception
:异常
一般我们所说的异常是指 Exception
及其子类
Exception 类
构造方法:
public Exception();
public Exception(String message);
Exception(String message, Throwable cause);
方法:
getMessage()
getCause()
printStackTrace()
多异常处理
子类异常要排在父类异常前面。
finally
语句无论是否有异常都要执行,即使其中有 break
, return
等语句。在编译时,finally
部分的代码生成了多遍。
受检的异常
Java 中的异常分两种:
RuntimeException
及其子类,可以不明确处理(一般使用if
来语句判断)- 否则,称为 受检的异常(chected Exception),例如 IO 异常
受检的异常,要求 明确进行语法处理
- 要么捕获(catch)
- 要么抛出(throw):在方法签名的后面用
throws xxxx
来声明- 在子类中,如果要覆盖父类的一个方法,若父类的方法声明了
throws
异常,则子类的方法也可以throws
异常。 - 可以跑出子类异常(更具体的异常),但不能抛出更一般的异常。
- 在子类中,如果要覆盖父类的一个方法,若父类的方法声明了
一种语法糖(Compiler suger):try ... with ... resource
try (类型 变量 = new 类型() ) {
// ...
}
finally { 变量.close(); }
,无论是否出现异常都会执行。
类似于 Python 里面的 with
语句。
自定义异常类
继承自 Exception
及其子类,可以重载父类方法也可以自定义方法。
重抛异常及异常链接:
对于异常,优势光捕获是不够的,还需要将其进一步传递给调用者,以便让调用者也能感受到这个异常。这是可以在 catch
语句块或 finally
语句块中采取以下三种方式:
- 当前捕获的异常再次抛出:
throw e;
- 重新生成并抛出一个异常:
throw new Exception("Some message");
- 重新生成并抛出一个异常,该异常中包含了当前异常的信息,如:
可以用
throw new ExceptioN("Some message", e);
e.getCause()
来得到内部异常。
断言(assertion)
assert
的格式是:
assert
表达式;assert
表达式: 信息;
在调试程序时,如果表达式的值不为 true
,则程序会产生异常,并输出错误信息。
assert 的编译和运行
编译:只有在 JDK1.4 及以上版本中才能使用断言。
具体地说,在早期的 JDK 版本(1.4)中编译时,要通过 -source
选项来指明版本,如:
javac -deprecation -source 1.4 -classpath . XXX.java
运行:在运行时,要使 assert
语句起作用,需要加上 -ea / -enableassertions
选项,如:
java -ea -classpath . XXX
测试及 JUnit
程序的修改是经常要进行的过程,而测试保证了程序在每次修改之后的正确性。
在编写程序代码的同时,还编写测试代码来判断这些程序是正确的。
这个过程称为 测试驱动开发
从而保证了代码质量,减少了后期的查错和调试时间,所以实际上它大幅提高了程序的开发效率。
通常使用 JUnit 框架来进行 Java 程序的测试,现在大多数 IDE 都集成了对 JUnit 的支持。
- 用
@Test
来标注测试函数 - 在测试中常用的语句如下:
fail(信息);
:程序出错assertEqual(参数1, 参数2);
:程序要保证两参数相等assertNull(参数);
:表示参数要为null
@Test public void testSum() { // ... }
程序的调试
程序中的错误可以分成三大类:
- 语法错误(Syntax error):人在编辑的时候发现、编译器发现
- 运行错误(Runtime error):异常处理机制
- 逻辑错误(Logic error):调试(debug)、单元测试(UT)
调试(debug)的常用手段:断点(breakpoint)、跟踪(trace)、监视(watch)
当然,调用堆栈(call stack)也能给我们提供很多信息。