异常处理

异常机制可以使程序中的异常处理代码和政策业务代码分离,保证代码更加优雅,提高程序的健壮性。

java异常分为checked异常和runtime异常。checked异常在编译阶段由编译器处理,告知程序员进行修改。runtime异常无须处理,由异常处理机制捕获。

有try,catch,finally,throw,throws组成。

try catch捕获异常。

执行try块中代码时出现异常,系统自动生成一个异常对象,该异常对象被提交给java运行时环境,这个过程叫做抛出异常。

java运行时环境收到异常对象时,会寻找能处理该异常对象的catch,找到则将该异常对象交给catch块处理,这个过程叫做捕获异常。

如果java运行时环境找不到捕获异常的catch块,则运行时环境终止,java程序退出。

 

 

 上图是Exception类的所有异常

如果java运行时环境受到异常后,会依次判断该对象是否是catch块后异常类或其子类的实例,如果是,将调用catch块的子类,如果不是则比较下一个catch块。程序会将系统生成的异常对象ex传给catch块的异常形参。从而获得更多信息。

try块以后可以有多个catch块,针对不同的异常类型提供不同的异常处理方式。

一般情况下,catch块只有一个会执行。

try,catch的花括号不能省略,即使只有一行代码。

try中的局部变量在catch中不能访问。

java的所有非正常情况分为两种,exception和error,都继承自throwable类。其中error类指与虚拟机相关问题,如系统崩溃,虚拟机错误,动态链接失败等等。这个是不能捕获的。将导致应用程序中断。

如上图的exception类,注意些catch时,一定要将子异常类写在前面,父异常类写在后面否则子类异常将永远捕获不到异常,因此程序往往将exception类写在最后。

java7以后,一个catch块可以捕获多个异常,多个异常类型之间用|隔开。

捕获多个类型异常时,异常变量隐式final修饰,不能对其赋值。捕获一个异常类型时,则可以。

所有异常对象都包含以下常用方法:

getMessage():返回该异常的详细描述字符串。

printStackTrace():将该异常的跟踪栈信息输出到标准错误输出。

printStackTrace(PrintStream s):将该异常的跟踪栈信息输出到指定输出流。

getStackTrace():返回该异常的跟踪栈信息。

使用finally回收资源。

java的垃圾回收机制只会回收堆内存中对象所占用的内存,不会回收任何物理资源(如数据库连接,网络连接,磁盘文件等等)如果在try块里打开一些物理资源,则必须显示回收。

为了回收物理资源,提供了finaly块,不管try中是否异常,或者那个catch执行,甚至在try中有retrun语句,finally也都会执行。但是如果是用system..exit(1)则退出虚拟机,finally将不会得到执行。

异常处理语法,try是必须有的,catch和finally是可选的,但是二者必须有一个。finally永远是放在最后的。

不要在finally中使用return,throw等导致方法终止的语句。这会造成try,catch中的return,trhow失效。

因为程序时按照 先执行try,catch,如果其中有return或者throw,则向下寻找finally,如果没有则执行return或者throw,如果有,则先执行finally,如果此时finally中没有return和throw,则执行完之后再去执行try和catch中的throw和return,如果finally中有这两个词,则执行完return或者throw后,将终止方法,不会跳去执行try,catch中的任何代码。

可以在try,catch,finally中嵌套异常处理代码。通常不超过两层。

java7,将程序结束后必须关闭的资源,写在try后的括号里声明或者初始化这些资源。,这样,就不用再finally中关闭。但是这些资源必须实现AutoCloseable或者closeable接口。后者是前者的字即可,后者里声明了IOexception,前者只声明了Exception。因此后者的实现类里只用IOexception。

java9不需要在圆括号里声明或初始化创建资源。只需要自动关闭的资源有final或者有效的final修饰即可。只要不对某个变量赋值,就是有效的final,即使没有声明final。

 runtrimecxception异常为runtime异常,不是前者类型的异常称为Checked异常。

java认为,checked异常都是可以被处理(修复)的异常,所以程序必须显式处理Checked异常。如果没有,则无法通过编译。

没有完善错误处理的代码根本不会被执行。

如果知道如何处理该异常,例如用户输入格式有误等,则应该在try catch中处理。

如果当前方法不知道如何处理该异常,应该在定义该方法时,声明抛出异常。

Runtime异常则更加灵活,无须显示声明抛出,如果需要捕获,则用trycatch实现。

使用throws声明抛异常

当前方法不知道如何处理该异常,使用throws声明,将该异常抛给调用者来处理,如果调用者也不知道,则也可以声明throws抛异常给jvm,这时,jvm就打印异常的跟踪栈信息,程序终止。

如果方法签名后面声明了throws,可以声明多个异常,用,隔开。一旦使用了throws就无须使用trycatch了。因为抛给了上一级。上一级要么使用throws抛出异常,要么使用trycatch捕获异常。

方法重写时 ,抛出异常,子类的异常只能是父类方法异常的相同或者子类。

推荐使用runtime异常,因为checked异常会带来编程的繁琐。runtime异常则无需在方法中声明抛出异常。

使用throw抛出异常。

当与业务逻辑不符时,必须由程序员来决定抛出,系统无法抛出这种异常。这种异常需要throw抛出,他是抛出一个异常实例,每次只能抛出一个。

若throw抛出的是checked异常,要么处于try中要么放在一个带throws声明抛出的方法中。

如果是runtime异常,则可以完全不理会,交个该方法的调用者处理。

 

程序应该有自定义异常类。

自定义异常类,继承与Exception,如果想要Runtime异常,则继承RuntimeException,自定义异常一般需要一个无参构造函数和一个带一个字符串参数的构造函数。

处理异常方式有两种:

给异常出现的方法内捕获异常,并处理,方法的调用者不能再次捕获。

该方法签名中声明抛出该异常,将该异常完全交给方法调用者处理。

但是有的时候,需要方法与方法调用者一起处理异常。这时就需要在catch中加throw语句。

这时如果是checked异常,则需要在方法签名中添加throws声明。

一般·企业级应用都需要这么做。

第一个应用后台需要通过日志来记录异常发生的情况。

第二应用还需要向使用者传达提示。

其余级应用程序中,在业务逻辑层和数据持久化层之间发生的异常,不能直接抛给用户,第一用户不关心,第二,恶意用户而言,不安全。

因此需要先捕获,再抛出一个新的业务异常。可以保证底层异常不会扩散到表现层。这种把原始异常信息保存下来并抛出一个异常的做法是异常链。

java1.4以后,所有的throwable子类在构造器中都可以接受一个cause对象作为参数。这个参数可以表示原始异常。这样可以将原始异常传递给抛出的新的异常。可以通过异常链追踪到发生异常的位置。

异常应该是处理不可预期的异常运行。不要使用异常处理机制去处理正常的业务逻辑或者代替正常的流程控制。因为trycatch的效率不如正常的流程控制代码。

不要使用过于庞大的try

不要忽略捕获到的异常。

处理异常,将异常进行处理,或者通过计算代替异常结果,或者提示用户操作等等。。

重新抛出异常,吧当前的运行环境下能做的事情做完,然后抛出异常给上层调用者。

在合适的层处理异常。如果当前层不清楚如何处理,则不要在当前catch语句中捕获异常,使用throws声明抛出该异常。让上层调用者去处理该异常,

posted @ 2020-03-03 12:25  小甲点点  阅读(207)  评论(0编辑  收藏  举报