java异常处理机制
- 当程序中抛出一个异常后,程序从程序中导致异常的代码处跳出,java虚拟机检测寻找和try关键字匹配的处理该异常的catch块,如果找到,将控制权交到catch块中的代码,然后继续往下执行程序,try块中发生异常的代码不会被重新执行。如果没有找到处理该异常的catch块,在所有的finally块代码被执行和当前线程的所属的ThreadGroup的uncaughtException方法被调用后,遇到异常的当前线程被中止。比如手机app的闪退。
- java异常结构中定义有Throwable类,Exception和Error是其派生的两个子类。其中Exception表示由于网络故障、文件损坏、设备错误、用户输入非法等情况导致的异常;而Error表示java运行时环境出现的错误,例如:JVM内存资源耗尽等。
tyr-catch块
-
语法定义:
try{ 代码片段 }catch(Exception e){ 代码段 }catch(){ }...
-
每个try语句块可以定义多个catch,当不同的异常我们有不同的处理手段时,可以分别捕获这些异常并处理。
-
最后一个catch处理Exception,可以避免因为一个未捕获的异常导致程序中断
finally
- finally块是异常处理机制的最后一块,可以直接跟在try块之后,或者最后一个catch之后
- finally是必走的程序。只要代码执行到try当中,无论try语句块中的内容是否出现异常,finally块的代码必定执行。
- 通常在finally语句中可以进行资源的释放工作,如关闭打开的文件、删除临时文件等。
比如IO操作完的关闭就可以放在finally中,以确保执行
public static void main(String[] args) {
FileOutputStream fos = null;//finally和try是两块作用域,若声明在try里finally里调用fos.close会报错,所以声明在外面
try {
fos = new FileOutputStream("fos.txt");
fos.write(1);
}catch(IOException e) {
System.out.println("出错了");
}finally {
try {//这里调用close也有可能出异常,所以也要写在异常处理块中
if(fos!=null)//因为最开始fos赋值为空,如果fos赋值的时候出错了,fos值是null
fos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
-
JDK7出现后,有一个特性:自动关闭。在try后面可以跟一个小括号,里面放的一般是继承了closeable的类(也就是有close功能的),不必再写finally代码段,这样编译时会自动添加。
-
下面这段代码编译后会自动补全和上面的一样
public static void main(String[] args) {
try (
FileOutputStream fos = new FileOutputStream("fos.txt");
){
fos.write(1);
}catch(IOException e){
System.out.println("出错了");
}
}
小题目
下面代码的运行结果是什么?
public static void main(String[] args) {
System.out.println(test("0")+","+test(null)+","+test(""));
}
public static int test(String str) {
try {
return str.charAt(0)-'0';
}catch(NullPointerException e) {
return 1;
}catch(Exception e) {
return 2;
}finally {
return 3;
}
}
返回结果3,3,3
return的执行过程分两步:
- 1,jvm在执行有返回值的方法时,会自动声明一个相应类型的变量来储存返回值,这里就是int,当return 时,先把后面的值赋给int变量.
- 2,第二步跳过后面的语句结束方法,但是return在try块里,结束前需要走一遍finally,所以finally里的return重新给int变量赋了个值
- 3.finally必走,finally里一旦写了return,之前的return都不会生效,这是要注意的
throw
- 当程序发生错误而无法处理的时候,会抛出对应的异常对象,除此之外,再某些时刻,我们可能会想要自行抛出异常,例如在异常处理结束后,再将异常抛出,让下一层异常处理块来捕捉,若想要自行抛出异常,可以使用throw关键字,并生成指定的异常对象后抛出
throw new ArithmeticException(String message);
- new 后面是异常名字,括号里是异常时的提示信息。
-子类重写父类含有的throws声明异常抛出的方法时对该方法的重写规则:
throws
- 当我们调用一个含有throws声明异常抛出的方法时,编译器要求我们必须处理这个异常,否则编译不通过。
- 处理异常的方式有两种:
- 使用try——catch主动处理这个异常
- 再当前方法上继续使用throws声明抛出。
具体使用哪种处理方式取决于实际情况,看该异常应当由谁解决。
比如在某个方法中可能因某些错误而引发异常,但是不希望直接在这个方法中处理这些异常,而希望调用它的这个方法来统一处理,这时候就可以使用throws
- 当一个方法中使用throw抛出一个异常时,就要在当前方法声明的同时使用throws声明该异常的抛出,只有抛出RuntimeException及其子类异常时不强制要求,否则编译不通过
-子类重写父类含有的throws声明异常抛出的方法时对该方法的重写规则:- 可以什么都不抛
- 可以抛父类异常的其中几个
- 可以抛出父类方法抛出异常的子类型异常
- 不允许抛出额外异常(与父类异常不同,且没有继承关系)
- 允许抛出父类方法抛出异常的子类异常,但是不可以抛父类异常的父类异常
例如:
public class ThrowsDemo2 {
public void dosome()throws IOException ,AWTException{
}
}
class Son extends ThrowsDemo2{
可以什么都不抛
public void dosome() {
}
可以抛父类异常的其中几个
public void dosome()throws IOException {
}
可以抛出父类方法抛出异常的子类型异常
public void dosome()throws FileNotFoundException {
}
不允许抛出额外异常(与父类异常不同,且没有继承关系)
public void dosome()throws SQLException {
}
允许抛出父类方法抛出异常的子类异常,但是不可以抛父类异常的父类异常
public void dosome() throws Exception{
}
}
JAVA异常API
java异常可以分为可检测异常,非检测异常:
- 可检测异常: 可检测异常经编译器验证,对于声明抛出异常的任何方法,编译器将强制执行处理或声明规则,不捕捉这个异常,编译器就不通过,不允许编译。
- 非检测异常:非检测异常不遵循处理或声明规则。在产生此类异常时,不一定非要采取任何适当操作,编译器不会检查是否已经解决了这样一个异常。非检测异常只有一个:RuntimeException
RuntimeException
- RuntimeException类属于非检测异常,因为普通JVM操作引起的运行时异常随时可能发生 ,此类异常一般是由特定操作引发。但这些操作在java应用程序中会频繁出现。因此他们不受编译器检查与处理或声明规则的限制。
- 常见的RuntimeException
- IllegalArgumentException
抛出的异常表明向方法传递了一个不合法或不正确的参数。 - NullPointerException
- ArrayIndexOutOfBoundsException
- ClassCastException
当试图将对象强制转换为不是实例的子类时,抛出该异常 - NumberFormatException
- IllegalArgumentException
Exception常用API
- printStackTrace()跟踪异常事件发生时堆栈中执行的内容
- 定义:
void printStackTrace() try{}catch(){ e.printStactTrace();//输出执行堆栈信息 }
- getMessage()得到有关异常事件的信息
- getCause
- 很多时候,当一个异常由另一个异常导致异常而被抛出的时后,java库和开放源代码会将一种异常包装成另一种异常。这是,日志记录和打印根异常就变得非常重要。java异常类提供了getCause()方法来检索导致异常的原因,这些可以对异常根层次的原因提供更多的信息。
该java实践对代码的调试或故障排除有很大的帮助。另外,如果要把一个异常包装成另一种异常,构造一个新异常就要传递原异常。
- 很多时候,当一个异常由另一个异常导致异常而被抛出的时后,java库和开放源代码会将一种异常包装成另一种异常。这是,日志记录和打印根异常就变得非常重要。java异常类提供了getCause()方法来检索导致异常的原因,这些可以对异常根层次的原因提供更多的信息。
自定义异常:
通常使用自定义异常来定义业务逻辑问题
- 语法:class[自定义的异常类名] extends Exception{}
- 自定义异常是一个类
- 自定义异常类必须继承一个已存在的异常类
- 类中最好有父类的所有构造函数(eclipse-source可以快捷生成),以方便设计,比如设计错误提示信息message