1.概述:
异常处理:java中的异常处理是基于面向对象的一种运行态错误处理机制,通过对异常信息的封装实现对用户非法操作、参数设置异常、硬件系统异常,运行时网络状态更换等在运行态中可能出现的异常信息的处理机制。
Throwable
Throwable类是java中所有错误或异常的顶级父类,所有的异常都继承于这个类。
Error,Exception是Throwable类的两个直接子类。
Error
错误:Error类以及他的子类的实例,一般是编译或者系统性的错误,如OutOfMemorry内存溢出异常等。Error很少出现,我们也无法通过程序处理,所以主要学习Exception异常。
Exception
异常:Exception以及他的子类,代表程序运行时的各种不期望发生的事件。可以被Java异常处理机制使用,是异常处理的核心。
运行时异常(非检查异常)
Error 和 RuntimeException 以及他们的子类。javac在编译时,不会提示和发现这样的异常,不要求在程序处理这些异常。对于这些异常,我们应该修正代码,而不是去通过异常处理器处理 。这样的异常发生的原因多半是代码写的有问题。
常见异常:除0错误ArithmeticException,错误的强制类型转换错误ClassCastException,
数组索引越界ArrayIndexOutOfBoundsException,空指针NullPointerException等。
检查异常
除了Error 和 RuntimeException的其它异常。javac强制要求程序员为这样的异常做预备处理工作(使用try…catch…finally或者throws)否则编译不会通过。这样的异常一般是由程序的运行环境导致的。因为程序可能被运行在各种未知的环境下,而程序员无法干预用户如何使用他编写的程序,于是程序员就应该为这样的异常时刻准备着。
常见异常:SQLException , IOException,ClassNotFoundException 等。
2.异常处理:
2.0java内部异常处理机制
java内部已经提供许多标准异常类,当程序出现异常时,如果自己没有处理,java会自动处理异常,但是仅仅是抛出异常,而且在程序异常处停止程序运行,并不会继续执行之后的代码。
demo:
public class Demo {
public static void main(String[] args) {
int a = 1;
System.out.println(a/0);
System.out.println(a);
}
}
可以看到程序执行1/0异常后抛出ArithmeticException异常后即终止,没有执行下面的输出a命令。
2.1try-catch-finally
try{
可能发生异常的代码块
}catch(可以捕获的异常1){
处理异常1的代码
}catch(可以捕获的异常2){
处理异常2的代码
}finally{
处理完所有异常后一定会执行的代码。
如果没有出现异常,最终也会执行这行代码
}
demo:
public class Demo {
public static void main(String[] args) {
int a = 1;
int b = 0;
try {
System.out.println(divide(a,b));
}catch(ArithmeticException ae) {
System.out.println("除数不能为0");
System.out.println(ae.getMessage()); // 异常信息/ by zero
System.out.println(ae.toString()); // 异常名称+异常信息java.lang.ArithmeticException: / by zero
ae.printStackTrace();
/*
异常名称、异常信息、异常出现位置 jvm默认异常处理机制就是在调用printStackTrace方法
java.lang.ArithmeticException: / by zero
at csdn.Demo.divide(Demo.java:21)
at csdn.Demo.main(Demo.java:8)
*/
}
System.out.println(a+1);
}
public static int divide(int a,int b) {
return a/b;
}
}
需要注意的地方
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(终结式异常处理模式)
finally语句块
finally块不管异常是否发生,只要对应的try执行了,则它一定也执行。只有一种方法让finally块不执行:System.exit()。因此finally块通常用来做资源释放操作:关闭文件,关闭数据库连接等等。
良好的编程习惯是:在try块中打开资源,在finally块中清理释放这些资源。
需要注意的地方:
1、finally块没有处理异常的能力。处理异常的只能是catch块。
2、在同一try…catch…finally块中 ,如果try中抛出异常,且有匹配的catch块,则先执行catch块,再执行finally块。如果没有catch块匹配,则先执行finally,然后去外面的调用者中寻找合适的catch块。
3、在同一try…catch…finally块中 ,try发生异常,且匹配的catch块中处理异常时也抛出异常,那么后面的finally也会执行:首先执行finally块,然后去外围调用者中寻找合适的catch块。
2.2throws函数声明
throws声明:如果一个方法内部的代码会抛出检查异常(checked exception),而方法自己又没有完全处理掉,则javac保证你必须在方法的签名上使用throws关键字声明这些可能抛出的异常,否则编译不通过。
throws是另一种处理异常的方式,它直接将异常抛出。对于编译时期的异常如果不做异常处理编译就无法通过,执行该异常处理后,当发生异常时会将异常类显示在控制台上。
采取这种异常处理的原因可能是:方法本身不知道如何处理这样的异常,或者说让调用者处理更好,调用者需要为可能发生的异常负责。
public class Demo {
public static void main(String[] args) throws Exception {
divide(1,0);
}
public static int divide(int a,int b) {
return a/b;
}
}
2.3自定义异常
java中提供的标准异常无法充分描述问题,或者是项目特有的异常,需要我们自定义异常类。
如果要自定义异常类,则扩展Exception类即可,因此这样的自定义异常都属于检查异常(checked exception)。如果要自定义非检查异常,则扩展自RuntimeException。
按照国际惯例,自定义的异常应该总是包含如下的构造函数:
- 一个无参构造函数
- 一个带有String参数的构造函数,并传递给父类的构造函数。
- 一个带有String参数和Throwable参数,并都传递给父类构造函数
- 一个带有Throwable 参数的构造函数,并传递给父类的构造函数。
先看一下IOException可以借鉴下
public class IOException extends Exception
{
static final long serialVersionUID = 7818375828146090155L;
public IOException()
{
super();
}
public IOException(String message)
{
super(message);
}
public IOException(String message, Throwable cause)
{
super(message, cause);
}
public IOException(Throwable cause)
{
super(cause);
}
}
demo:
public class TestMyException {
public static void main(String[] args) {
Person p = new Person();
p.setAge(-5);
}
}
//自定义不合法年龄异常
class IllegalAgeException extends Exception{
//默认构造器
public IllegalAgeException() {
super();
}
//带有详细信息的构造器
public IllegalAgeException(String message) {
super(message);
}
}
class Person{
private String name;
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
if(age<0||age>150) {
try {
throw new IllegalAgeException("年龄不合法");
} catch (IllegalAgeException e) {
e.printStackTrace();
}
}
this.age = age;
}
}
throws和throw的区别:
最后使用异常机制的建议
1.要避免使用异常处理代替错误处理,这样会降低程序的清晰性,并且效率低下。
2.处理异常不可以代替简单测试---只在异常情况下使用异常机制。
3.不要进行小粒度的异常处理---应该将整个任务包装在一个try语句块中。
4.异常往往在高层处理(先了解)