参考资料
- 《java编程的逻辑》
- 异常(一)
- 异常(二)(http://wiki.jikexueyuan.com/project/java-enhancement/java-seventeen.html)
基本定义:
首先我们可以明确一点就是异常的处理机制可以确保我们程序的健壮性,提高系统可用率。
非正常情况在Java中统一被认为是异常,Java使用异常机制来统一处理,尽管 Java 有异常处理机制,但是我们不能以“正常”的眼光来看待异常,异常处理机制的原因就是告诉你:这里可能会或者已经产生了错误,您的程序出现了不正常的情况,可能会导致程序失败!
抛出异常后:
- 使用 new 创建一个异常对象,然后在产生异常的位置终止程序,并且从当前环境中弹出对异常对象的引用;
- 异常处理机制接管程序,并开始寻找一个恰当的地方来继续执行程序;
- 这个恰当的地方就是异常处理程序,它的任务就是将程序从错误状态恢复,以使程序要么换一种方法执行,要么继续执行下去。
体系类:
其中 Error 为错误,是程序无法处理的,如 OutOfMemoryError、ThreadDeath 等,出现这种情况交由 JVM 来处理,不过 JVM 在大多数情况下会选择终止线程;
而 Exception 是程序可以处理的异常。它又分为两种 CheckedException(受捡异常),一种是 UncheckedException(不受检异常)。
其中 CheckException 发生在编译阶段,必须要使用 try…catch(或者throws)否则编译不通过。
而 UncheckedException 发生在运行期,具有不确定性,主要是由于程序的逻辑问题所引起的,难以排查,我们一般都需要纵观全局才能够发现这类的异常错误,所以在程序设计中我们需要认真考虑,好好写代码,尽量处理异常,即使产生了异常,也能尽量保证程序朝着有利方向发展;
所以:对于可恢复的条件使用被检查的异常(CheckedException),对于程序错误(言外之意不可恢复,大错已经酿成)使用运行时异常(RuntimeException)。
语法结构:
try{
//可能抛出异常
}catch(Exception e){
//捕获异常
}finally{
//不管有无异常都执行
}
- 如果没有异常发生,在try内的代码执行结束后执行;
- 如果有异常发生且被catch捕获,在catch内的代码执行结束后执行;
- 如果有异常发生但没被捕获,则在异常被抛给上层之前执行。
所以 finally 用于释放资源是再适合不过了,它一般用于释放资源,如数据库连接、文件流等。
ex.printStackTrace()表示在命令行打印异常信息在程序中出错的位置及原因。
使用举例:
try/catch执行过程,try后面会包含可能抛出异常的代码,catch后括号包含能捕获的异常和处理代码.捕获异常之后,try语句异常点之后的代码就不会再执行了,并且执行完catch后,程序会继续执行catch花括号之外的代码。
public class ExceptionTest {
public static void main(String[] args) {
if(args.length<1){
System.out.println("请输入数字");
return;
}
try{
public class ExceptionTest {
public static void main(String[] args) {
if(args.length<1){
System.out.println("请输入数字");
return;
}
try{
int num = Integer.parseInt(args[0]);
System.out.println(num);
}catch(NumberFormatException e){
System.err.println("参数" + args[0] + "不是有效的数字,请输入数字");
}
}
}
自定义异常
一般是继承Exception或者它的某个子类。如果父类是RuntimeException或它的某个子类,则自定义异常也是未受检异常;如果是Exception或Exception的其他子类,则自定义异常是受检异常。
Java 自定义异常的使用要经历如下四个步骤:
- 定义一个类继承 Throwable 或其子类;
- 添加构造方法(当然也可以不用添加,使用默认构造方法);
- 在某个方法类抛出该异常;
- 捕捉该异常。
/** 自定义异常 继承Exception类 **/
public class MyException extends Exception{
public MyException(){
}
public MyException(String message){
super(message);
}
}
class Test {
public void display(int i) throws MyException{
if(i == 0){
throw new MyException("该值不能为0,应为其他有效值");
}
else{
System.out.println( i / 2);
}
}
public static void main(String[] args) {
Test test = new Test();
try {
test.display(0);
System.out.println ("正常结果输出此条记录");
} catch (MyException e) {
e.printStackTrace();
}
}
}
输出结果如下:
MyException: 该值不能为0,应为其他有效值
at Test.display(MyException.java:21)
at Test.main(MyException.java:30)
正常值输出结果:
2
正常结果输出此条记录
异常链
try…catch 的 catch 块我们可以不需要做任何处理,仅仅只用 throw 这个关键字将我们封装异常信息主动抛出来。然后在通过关键字 throws 继续抛出该方法异常。它的上层也可以做这样的处理,以此类推就会产生一条由异常构成的异常链。
同理,我们有时候在捕获一个异常后抛出另一个异常信息,并且希望将原始的异常信息也保持起来,这个时候也需要使用异常链。
在异常链的使用中,throw 抛出的是一个新的异常信息,这样势必会导致原有的异常信息丢失,如何保持?在 Throwable 及其子类中的构造器中都可以接受一个 cause 参数,该参数保存了原有的异常信息,通过 getCause ()就可以获取该原始异常信息。
语法:
public void test() throws XxxException{
try {
//do something:可能抛出异常信息的代码块
} catch (Exception e) {
throw new XxxException(e);
}
}
用例:
public class Main {
public static void main (String args[])throws Exception {
int n=20,result=0;
try{
result=n/0;
System.out.println("结果为"+result);
}
catch(ArithmeticException ex){
System.out.println("算术异常: "+ex);
try {
throw new NumberFormatException();
}
catch(NumberFormatException ex1) {
System.out.println("手动抛出链试异常 : "+ex1);
}
}
}
}
抛出异常结果:
发算术异常: java.lang.ArithmeticException: / by zero
手动抛出链试异常 : java.lang.NumberFormatException
关键字throws
throws 是方法抛出异常,这个声明的含义是,这个方法内可能抛出这些异常,且没有对这些异常进行处理,至少没有处理完,调用者必须进行处理。如果一个方法会有异常发生时,但是又不想处理或者没有能力处理,就使用 throws抛出。
如果一个方法内调用了另一个声明抛出受检异常的方法,则必须处理这些受检异常,处理的方式既可以是catch,也可以是继续使用throws,如下所示:
public void tester() throws AppException {
try {
test();
} catch(SQLException e) {
e.printStackTrace();
}
}
而 throw 是语句抛出异常。它不可以单独使用,要么与 try…catch 配套使用,要么与 throws 配套使用
使用异常注意事项
- 用于且仅用于异常情况## 标题
异常不能代替正常的条件判断。
循环处理数组元素的时候,应该先检查索引是否有效再进行处理,而不是等着抛出索引异常再结束循环。
对于一个引用变量,如果正常情况下它的值也可能为null,那就应该先检查是不是null,不为null的情况下再进行调用。
另一方面,真正出现异常的时候,应该抛出异常,而不是返回特殊值。 - 异常处理目标
分三种来源:
用户,指用户的输入有问题;
程序员,是指编程错;
第三方用户是,泛指其他情况,如I/O错误、网络、数据库、第三方服务等。
每种异常都应该进行适当的处理。
处理目标为恢复和报告:对用户,如果用户输入不对,可以提示用户具体哪里输入不对,如果是编程错误,可以提示用户系统错误、建议联系客服,如果是第三方连接问题,可以提示用户稍后重试。 - 异常处理的一般逻辑
总有一层代码需要为异常负责,可能是知道如何处理该异常的代码,可能是面对用户的代码,也可能是主程序。如果异常不能自动解决,对于用户,应该根据异常信息提供用户能理解和对用户有帮助的信息;对运维和开发人员,则应该输出详细的异常链和异常栈到日志。