JAVA异常处理的设计思路
java的设计思想里是尽量在代码运行前就手动检查代码(try、throw等工具)所有可能错误,也就是检查异常
(区分编译错误,那个由程序编译时自动检查,是不一样的东西)
但是有些异常是没法在运行前检查的,如RuntimeException
(比如其子类异常:除0错,越界错),所以没办法,才是非检查异常(且这些错误全部都检查的话程序也太臃肿了)(Error也属于非检查异常)非检查异常属于JVM会自动默认直接抛出,不处理的
不过RuntimeException
也可以自己手动检查异常,一切语句都可以自己设置检查运行时错误(一切语句运行起来的时候都是运行时),而其它检查异常就不能随便乱抛了,得满足该异常条件
Error和Exception的区别
Error:由 JVM 所侦测到的无法预期的错误,由于这是属于 JVM 层次的严重错误,导致 JVM 无法继续执行,因此,这是不可捕捉到的,无法采取任何恢复的操作,顶多只能显示错误信息(如系统崩溃,内存不足,堆栈溢出等)。
Exception:可以在程序运行中进行捕获并处理,继续执行
JAVA异常操作分两个环节:抛出异常和处理异常
抛出异常
抛出异常有两种方式:
代码设计者手动抛出异常
使用throw Throwable对象
来手动抛出异常
throw new RuntimeException();
JVM自动捕获到异常并抛出
将需要JVM自动捕获抛出异常的代码写在try
块里
处理(抛出的)异常
异常链
无论是手动抛出,还是自动抛出,抛出的【异常对象都会自动抛向方法调用者】,当方法调用者即为主程序且不catch处理,那么程序就终止,并打印异常信息;若被处理,则按try catch处理异常
举个例子,方法B抛出异常,如果是主程序直接调用该方法B,则直接抛出,程序终止,打印异常信息(假定未catch处理);如果是方法A调用抛出异常的方法B,那该异常就抛给A,接着A向上抛,如果有try catch
则直接按照对应方式处理,没有就程序终止,打印异常信息
抛出的检查异常要么用throws
声明,要么用try catch
捕获,非检查异常(Error
、RuntimeException
)不用(非检查异常本质是所有方法都默认throws RuntimeException
了,Error同)
(以下代码也包含抛出异常部分)
异常抛出后,可以直接向上抛(如果还能)不处理(但要throws声明);也可以用catch处理。(如果是JVM自动抛出的异常则不能用throws声明)
throws声明:只标识该方法可能抛出的异常列表,不处理
throws
声明方式
class test
{
void method() throws Exception
{
throw new Exception(); //手动抛出异常
}
}
异常链(还有上层调用者,则继续向上抛出):
class newTest
{
void fatherMethod() throws Exception //异常对象通过异常链传递到该方法后,该方法的父类调用者已是主程序,且无catch处理,则程序终止,打印异常信息
{
method();
}
void method() throws Exception
{
throw new Exception();
}
}
上层调用者也可以是catch方式
class newTest
{
void fatherMethod()
{
try{
method();
}catch (Exception e){
System.out.println("father knows it");
}
}
void method() throws Exception
{
throw new Exception();
}
}
catch:处理抛出的异常
自动捕获的异常只能通过try catch
来处理
void testMethod()
{
try{
method(); //try代码块,在该代码块里由JVM自动抛出异常
}catch (Exception e){ //catch捕获try代码块中抛出的异常对象
System.out.println("i know it"); //如果捕获到异常对象,处理代码
}
}
catch也可以同时捕获多个异常,写多个catch即可
JVM自动捕获异常后再手动抛出封装异常
try catch
结合手动throw
有时我们会从 catch 中手动抛出一个异常,目的是为了改变异常的类型(比如抛出一个我自己定义的异常MyException)。多用于在多系统集成时,当某个子系统故障,异常类型可能有多种,可以用统一的异常类型向外暴露,不需暴露太多内部异常细节。
class MyException extends Exception //自定义异常
{
String name;
Exception e;
MyException(String name)
{
this.name = name;
}
void initCause(Exception e) //传入导致错误的异常对象
{
this.e = e;
}
//other functton
}
class test
{
void testMethod() throws MyException
{
try{
//doSth
}catch(RuntimeException e) //这里捕获到了RuntimeException后,手动抛出我自己封装好的Exception类
{
MyException ex = new MyException("xxx is wrong!");
ex.initCause(e);
throw ex;
}
}
}
自定义异常
见上面JVM自动捕获异常后再手动抛出封装异常部分的例子
MORE
原理1:JVM如何捕获到异常并抛出?
①当一个方法中发生异常时,这个方法会创建该异常的一个异常对象(即try块,自动捕获异常)
②也可以手动自己创建一个异常对象(throw)
将异常对象交给JVM的过程就叫抛出异常
原理2:JVM是如何处理异常的?
JVM 会顺着调用栈去查找看是否有可以处理异常的代码(catch)(也就构成了前面所说的异常链)。
如果有,JVM将该异常对象传递给它,由处理代码块进行处理;
如果到了栈底还没有(即前面所说的上层调用者已是主程序),JVM 就会将该异常转交给默认的异常处理器,该默认异常处理器打印出异常信息并终止应用程序。
NoClassDefFoundError 和 ClassNotFoundException
NoClassDefFoundError 是Error,发生在加载某类时在内存中找不到该类的定义
ClassNotFoundException是检查异常,使用反射的时候,通过传入的类路径参数没有找到该类
异常不要拿来做流程控制
finally,总有意想不到的惊喜等着你。。。
异常就是单纯处理异常的
关于return 和 finally
return和finally执行顺序问题:Java finally语句到底是在return之前还是之后执行?
给出结论:
1.finally语句是在try的return语句执行之后,return返回之前执行(return的语句执行后不返回,再继续执行finally中的语句,finally执行完后再返回)
2.finally块中的return语句会覆盖try块中的return返回。
3.inally里的修改语句可能影响也可能不影响try或catch中 return已经确定的返回值
以及一些finally语句执行问题的结论:
(1)try语句没有被执行到,如在try语句之前就返回了,这样finally语句就不会执行,这也说明了finally语句被执行的必要而非充分条件是:相应的try语句一定被执行到。
(2)在try块中有System.exit(0);这样的语句,System.exit(0);是终止Java虚拟机JVM的,连JVM都停止了,所有都结束了,当然finally语句也不会被执行到。