异常
- 定义:
程序运行过程中发生的期望之外的事件,阻止了程序的正常执行,这就是异常。此处的异常宏观上包括 Exception 和 Error ,他们都继承自 Throwable 异常类。
- 异常体系图:
异常分为 Exception 和 Error
1、Exception
- Exception分类:
1、检查性异常(checked exception):也叫非运行时异常,除 Error 和 RuntimeException 之外的其它异常。javac 强制要求程序员为这样的异常做预备处理工作(使用 try…catch…finally 或者 throws )。在方法中要么用 try-catch 语句捕获它并处理,要么用 throws 子句声明抛出它,否则编译不会通过。
处理:这样的异常一般是由程序的运行环境导致的。因为程序可能被运行在各种未知的环境下,而程序员无法干预用户如何使用他编写的程序,于是程序员就应该为这样的异常时刻准备着。如SQLException , IOException,ClassNotFoundException 等。
2、非检查性异常(unckecked exception):也叫运行时异常,RuntimeException、Error 以及它们的子类。在编译时,不会提示和发现这样的异常,不要求在程序处理这些异常。运行时才可能发现这样的异常。
处理:对于这些异常,我们应该修正代码,而不是去通过异常处理器处理 。这样的异常发生的原因多半是代码写的有问题。如算术异常 ArithmeticException,强制类型转换异常 ClassCastException,数组索引越界ArrayIndexOutOfBoundsException,空指针异常 NullPointerException 等等。
3、Error: 错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如栈溢出,在编译时也检查不到的。
- 异常注意:
1、当子类重写父类使用 throws 声明的函数时,其 throws 声明的异常必须在父类异常的范围内(应该是一个包含关系,子类声明的异常被父类声明的异常所包含),用于处理父类的 throws 方法的异常处理器,必须也适用于子类的带 throws 的方法 。例如,父类方法 throws 的是2个异常(指2个类型的异常 ),子类就不能 throws 3个(指3个类型)及以上的异常。父类 throws IOException,子类就必须 throws IOException 或者 IOException 的子类。
2、Java程序可以是多线程的,并且线程独立。如果程序只有一个线程,那么没有被任何代码处理的异常会导致程序终止。如果是多线程的,那么没有被任何代码处理的异常仅仅会导致异常所在的线程结束。
- 说明:
1、Throwable 类是 Exception 类和 Error 类的子类。
2、所有的异常类都是 Exception 类的子类。
3、所有的错误都是 Error 类的子类。
4、Java 程序不捕获错误。错误属于是严重故障,它们在Java程序处理的范畴之外。
5、Error 用来指示运行时环境发生的错误。例如JVM 内存溢出。
- Exception的处理:
-
1、使用 try 和 catch 捕获异常。
语法如下:
try{ //try语句块中放可能发生异常的代码。 //如果执行完try且不发生异常,则接着去执行finally块和finally后面的代码(如果有的话)。 //如果发生异常,异常会被依次抛给后面的 catch 语句块,该异常的类型与哪个 catch 语句块中的 ExceptionType 匹配,它就在哪个 catch 语句块中被捕获,这个异常最后可能是被捕获或者通过所有的 catch 语句块。 }catch(ExceptionType1 异常名){ //每一个catch块用于捕获并处理一个特定的异常。比较特殊的是Java7中可以将多个异常声明在一个catch中。 //在catch块中可以使用这个块的异常参数来获取异常的相关信息。异常参数是这个catch块中的局部变量,其它块不能访问。 //如果当前try块中发生的异常在后续的所有catch中都没捕获到,则执行finally,然后到这个函数的外部函数调用者caller中去匹配异常处理器。 //如果try中没有发生异常,则所有的catch块将被忽略,执行finally块。 }catch(ExceptionType2 异常名){ //... }catch(ExceptionType3 异常名){ //... }finally{ //finally代码块通常是可选的,可以选择使用也可以选择不用,如果选择使用那么通常出现在 try/catch 代码块的最后。 //无论异常是否发生,以及是被否匹配处理,即使发生了内存溢出异常也会执行,finally都会执行。 //一个try至少要有一个catch块,至少要有1个finally块。但是finally不是用来处理异常的,finally不会捕获异常。 //finally主要做一些清理工作,如流的关闭,数据库连接的关闭等。 一般在try块中打开资源,在finally块中清理释放资源。 }
注意:try/catch/finally 中,catch 和 finally 块都是可选的,但是至少要有其中一个。
1、try 块中的局部变量和 catch 块中的局部变量(包括异常变量),以及 finally 中的局部变量,他们之间不可共享使用(作用域不同)。
2、每一个 catch 块用于处理一个异常。异常匹配是按照 catch 块的顺序从上往下依次匹配的,只有第一个匹配的 catch 会得到执行。匹配时,不仅运行精确匹配,也支持父类匹配,因此,如果同一个try块下的多个 catch 异常类型有父子关系,应该将子类异常放在前面,父类异常放在后面,这样保证每个 catch 块都有存在的意义。
3、java中,异常处理的任务就是将执行控制流从异常发生的地方转移到能够处理这种异常的地方去。也就是说:当一个函数的某条语句发生异常时,这条语句的后面的语句不会再执行,它失去了焦点。执行流跳转到最近的匹配的异常处理catch代码块去执行,异常被处理完后,执行流会接着在“处理这个异常的catch代码块”后面接着执行。
有的编程语言当异常被处理后,控制流会恢复到异常抛出点接着执行,这种策略叫做:resumption model of exception handling(恢复式异常处理模式 )而Java则是让执行流恢复到处理了异常的catch块后接着执行,这种策略叫做:termination model of exception handling(终结式异常处理模式) -
2、throws/throw 关键字抛出异常
(1)throws
语法格式:修饰符 返回值类型 方法名(参数) throws 异常类名1,异常类名2 ... { //本方法可以抛出 异常类名1,异常类名2等 类型的异常,或者他们的子类的异常对象。 }
如果一个方法内部的代码会抛出检查异常(checked exception),而方法自己又没有完全处理掉,那么你必须使用 throws 关键字声明这些可能抛出的异常,否则编译不通过。
throws 关键字放在方法签名的尾部。
不同于 try…catch…finally 处理异常,throws 仅仅是将函数中可能出现的异常抛出给调用者,由调用者去处理,自己不具体处理。(2)throw
语法格式:
throw 关键字用在方法里。throw new 异常类名(参数);
例:
public void merry(String name) { if(name == null) throw new IllegalArgumentException("名字为空"); }
说明:
1、我们可以通过 throw 语句手动显式抛出一个异常。throw语句的后面必须是一个异常对象。
2、throw用在方法内,用来抛出一个异常对象,将这个异常对象传递给调用者,同时结束当前方法的执行。
3、如果需要在程序中自行抛出异常,则应使用 throw 语句,throw 语句可以单独使用,throw 语句抛出的不是异常类,而是一个异常实例,而且每次只能抛出一个异常实例(因为当 throw 抛出异常时,当前方法就已经停止执行)。
4、当 Java 运行时接收到开发者自行抛出的异常时,会中止当前的执行流,跳到该异常对应的 catch 块来处理该异常。即不管是系统自动抛出的异常还是程序员手动抛出的异常,Java 运行时环境对异常的处理没有任何差异。
5、如果 throw 语句抛出的异常是 Checked 异常,则该 throw 语句要么处于 try 块里,显式捕获该异常,要么其所在方法使用 throws 声明,即把该异常交给该方法的调用者处理;
6、如果 throw 语句抛出的异常是 Runtime 异常,则该语句无须放在 try 块里,也无须放在带 throws 声明抛出的方法中;程序既可以显式使用 try…catch来捕获并处理该异常,也可以完全不理会该异常,把该异常交给该方法调用者处理。 -
3、自定义异常类
语法格式:
自定义异常类,只要继承 Exception 类。public class ziDingYiException extends Exception { String name = ”自定义异常“; public IOException() { super(); } public IOException(String s) { super(s); } public IOException(String s, Throwable t) { super(s, t); } public IOException(Throwable t) { super(t); } }
说明:
自定义异常类,只要继承 Exception 类就行。
自定义的异常包含如下的构造函数:
一个无参构造函数
一个带有 String 参数的构造函数,并将参数传递给父类的构造函数。
一个带有 String 参数和 Throwable 参数的构造函数,并将参数传递给父类构造函数
一个带有 Throwable 参数的构造函数,并将参数传递给父类的构造函数。
2、Error
- Error 都属于运行时异常。
- Error 类对象由java 虚拟机生成并抛出,大多数错误与代码编写者所执行的操作无关。
- Error 通常是致命的灾难性错误,程序无法控制和处理。一般出现 Error 时,JVM会选择终止 Error 所在线程。
总结:
- 处理运行时异常时,代码逻辑中要合理规避并且使用try/catch捕捉。
- 多重catch块的最后要有一个catch(Exception)来处理前面的catch没有处理的异常
- 尽量再try/catch块后添加finally块去释放资源(数据库连接等)