异常类层次结构图:
在 Java 中,所有的异常都有一个共同的祖先 Throwable(可抛出)。
Throwable:
有两个重要的子类:Exception(异常)和 Error(错误),二者都是 Java 异常处理的重要子类,各自都有好多子类。
Error(错误):
是程序无法处理的错误,表示运行应用程序中较严重问题。
Exception(异常):
是程序本身可以处理的异常。
常用异常
(1、)OutOfMemoryError 内存溢出异常
(2、)ArrayIndexOutOfBoundsException 数组越界异常
(3、)NullPointerException 空指针异常
(4、)ArithmeticException 数字异常
(5、)ClassCastException 类型转换异常类
(6、)SQLException 操作数据库异常类
(7、)NoSuchMethodException 方法未找到抛出的异常
(8、)IOException 操作输入流和输出流时可能出现的异常
(9、)ClassNotFoundException 找不到类异常
Exception 这种异常分两大类运行时异常和非运行时异常(编译异常)。程序中应当尽可能去处理这些异常。
运行时异常:都是RuntimeException类及其子类异常,如NullPointerException(空指针异常)、IndexOutOfBoundsException(下标越界异常)等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。
运行时异常的特点:
是Java编译器不会检查它,也就是说,当程序中可能出现这类异常,即使没有用try-catch语句捕获它,也没有用throws子句声明抛出它,也会编译通过。
非运行时异常 (编译异常):
是RuntimeException以外的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如IOException、SQLException等以及用户自定义的Exception异常,一般情况下不自定义检查异常。
处理异常机制
在 Java 应用程序中,异常处理机制为:抛出异常,捕捉异常。
异常通过try-catch语句捕获。其一般语法形式为:
try {
// 可能会发生异常的程序代码
} catch (Type1 id1){
// 捕获并处置try抛出的异常类型Type1
}
catch (Type2 id2){
//捕获并处置try抛出的异常类型Type2
}
把可能发生错误的代码放在关键词try后面的大括号中,运行时系统试图寻找匹配的catch子句以捕获异常。若有匹配的catch子句,则运行其异常处理代码,try-catch语句结束。
匹配的原则是:如果抛出的异常对象属于catch子句的异常类,或者属于该异常类的子类,则认为生成的异常对象与catch块捕获的异常类型相匹配。
try {
div(4,0);
System.out.println("执行可能存在问题的代码");
} catch (Exception e) {
e.printStackTrace();//打印异常 没有的话会自动执行下面的程序这个异常会滤过
System.out.println(e.getMessage());//这个是输出异常信息(是什么错误)
System.out.println("捕获异常");
}
throw 是声明这个方法的调用存在异常情况,调用者需采取捕获处理或继续向外抛的操作才能过编译
public static int div(int a, int b) throws Exception {// 为了告诉我这个方法有问题 提前抛出异常
1. 当这段代码出现问题时,jvm会创建一个异常对象并抛给调用者 (这里指Main函数)
2. 调用者接受到异常之后不能处理的话,继续往上抛,直到抛给jvm,最终将异常信息输出到控制台
if (b == 0){
throw new ArithmeticException("除以0了,逗比");
}
return a / b;//throw new ArithmeticException(); 算数异常 这里运行到b时,发现b是0,就自动抛出异常
}
多个异常捕获,异常对象存在父子关系时,要求父类异常类型必须写在下方
多个异常可以合并在同一个catch块
try {
computer.setStatus("进水");
}catch (DownException e){
e.printStackTrace();
}catch (Exception e){
e.printStackTrace();
}finally {//不管出现任何异常的情况下都会走这个代码块
}
}
小结:
try 块:用于捕获异常。其后可接零个或多个catch块,如果没有catch块,则必须跟一个finally块。
catch 块:用于处理try捕获到的异常。
finally 块:无论是否捕获或处理异常,finally块里的语句都会被执行。当在try块或catch块中遇到return语句时,finally语句块将在方法返回之前被执行。在以下4种特殊情况下,finally块不会被执行:
1)在finally语句块中发生了异常。
2)在前面的代码中用了System.exit()退出程序。
3)程序所在的线程死亡。
4)关闭CPU。
try、catch、finally语句块的执行顺序:
1)当try没有捕获到异常时:try语句块中的语句逐一被执行,程序将跳过catch语句块,执行finally语句块和其后的语句;
2)当try捕获到异常,catch语句块里没有处理此异常的情况:当try语句块里的某条语句出现异常时,而没有处理此异常的catch语句块时,此异常将会抛给JVM处理,finally语句块里的语句还是会被执行,但finally语句块后的语句不会被执行;
3)当try捕获到异常,catch语句块里有处理此异常的情况:在try语句块中是按照顺序来执行的,当执行到某一条语句出现异常时,程序将跳到catch语句块,并与catch语句块逐一匹配,找到与之对应的处理程序,其他的catch语句块将不会被执行,而try语句块中,出现异常之后的语句也不会被执行,catch语句块执行完后,执行finally语句块里的语句,最后执行finally语句块后的语句;
Throws抛出异常的规则:
1) 如果是不可查异常(unchecked exception),即Error、RuntimeException或它们的子类,那么可以不使用throws关键字来声明要抛出的异常,编译仍能顺利通过,但在运行时会被系统抛出。
2)必须声明方法可抛出的任何可查异常(checked exception)。即如果一个方法可能出现受可查异常,要么用try-catch语句捕获,要么用throws子句声明将它抛出,否则会导致编译错误
3)仅当抛出了异常,该方法的调用者才必须处理或者重新抛出该异常。当方法的调用者无力处理该异常的时候,应该继续抛出,而不是囫囵吞枣。
4)调用方法必须遵循任何可查异常的处理和声明规则。若覆盖一个方法,则不能声明与覆盖方法不同的异常。声明的任何异常必须是被覆盖方法所声明异常的同类或子类。
例如:
void method1() throws IOException{} //合法
//编译错误,必须捕获或声明抛出IOException
void method2(){
method1();
}
//合法,声明抛出IOException
void method3()throws IOException {
method1();
}
//合法,声明抛出Exception,IOException是Exception的子类
void method4()throws Exception {
method1();
}
//合法,捕获IOException
void method5(){
try{
method1();
}catch(IOException e){…}
}
//编译错误,必须捕获或声明抛出Exception
void method6(){
try{
method1();
}catch(IOException e){throw new Exception();}
}
//合法,声明抛出Exception
void method7()throws Exception{
try{
method1();
}catch(IOException e){throw new Exception();}
}