java异常处理

程序的实际运行的过程中经常会遇到异常情况的发生,导致正常流程的改变,造成恶劣的后果。

为了减少损失,应该事先充分预计所有可能出现的异常,然后采取对应的措施。

java异常处理机制

传统的编程语并没有异常处理机制,通常用方法特定的返回值来表示异常情况,并且正常流程和异常流程都采用相同的流程控制语句。

传统异常处理的缺点在于:

java异常处理的优点在于:

java虚拟机的方法调用栈

采用方法调用栈(method invocation stack)来跟踪每个线程中一系列的方法调用过程。

该堆栈保存了每个调用方法的本地信息(如方法的局部变量)。

每个线程都有一个独立的方法调用栈。

对于java主线程,堆栈底部是程序的入口方法main()。

当一个新方法被调用时,java就会把描述该方法的栈结构置入栈顶。

如果方法的代码块中出现异常,处理方法有两种:

(1)在当前方法中通过try...catch语句捕获并处理异常

(2)在方法的声明处通过throws语句声明抛出异常

当一个方法正常执行完毕,java虚拟机会从调用栈中弹出该方法的栈结构,然后继续处理前一个方法。

如果执行时抛出异常,则java虚拟机必须能找到捕获异常的catch代码块。

首先查看当前方法中是否存在这样的catch代码块

如果存在,那么就执行该catch代码块;否则,java虚拟机会从调用栈中弹出该方法的栈结构,继续到前一个方法中查找合适的catch代码块。

在回溯过程中,如果java虚拟机在某个方法中找到了处理改异常的代码块,则该方法的栈结构将成为栈顶元素,程序流程将转到该方法的异常处理代码部分继续执行。

当java虚拟机追溯到调用栈底部方法时,如果仍然没有找到处理该异常的代码,则会:

(1)调用异常对象的printStackTrack()方法,打印来自方法调用栈的异常信息。

(2)如果该线程不是主线程,那么终止这个线程,其他线程继续正常运行。如果该线程是主线程(即方法调用栈的底部为main()方法),那么整个应用程序被终止。

异常处理对性能的影响

抛出异常和处理异常的代码块位于同一个方法中,对性能影响小;

如果java虚拟机必须搜索调用栈来寻找处理异常的代码块,对性能影响就比较大,尤其是在调用栈底部时。

运用java异常处理机制

try...catch语句:捕获异常

try...catch的格式如下:

finally语句:任何情况下都会执行的代码

finally代码块能保证特定的操作总是会被执行。

不管try代码块是否出现异常,finally都会执行。

 

如果直接将finally中的语句拿出来不用finally结构,而是直接放在try...catch后面虽然也可以执行但是优缺点:

(1)与try代码的操作孤立开来,使得程序结构松散,可读性差

(2)影响程序健壮性,如果catch代码继续抛出异常,就会执行后面的语句了。

throws子句用来声明可能出现的异常

如果一个方法可能会出现异常,但是没有能力处理这种异常,可以在方法的声明处用throws子句抛出异常。

一个方法也可以声明多个异常。

throw用于抛出异常

throw抛出的异常必须是java.lang.Throwable类或者其子类的实例。


异常处理的语法规则

(1)try代码块不能脱离catch代码块或finally代码块而单独存在,try代码块后面至少有一个catch代码块或finally代码块。

(2)try代码块后面可以有零个或多个catch代码块,还可以有零个或者至多一个finally代码块。如果catch代码块和finally代码块并存,finally代码块必须在catch代码块之后。

(3)try代码块后面可以只跟finally代码块。

(4)try代码块中定义的变量的作用域为try代码块,在catch代码块和finally代码块中不能访问该变量。

如果希望在catch或者finally中访问,则必须把变量定义在try之外。     

(5)当try代码块后面有多个catch代码块时,java虚拟机会把实际抛出的异常对象依次和各个catch代码块声明的异常类型匹配,如果异常对象为某个异常类型或其子类的实例,就执行这个代码快,而不会去执行其他的代码块。如果有catch代码块永远不会执行,则会报编译错误!

(6)如果一个方法可能出现异常,要么用try...catch捕获异常,要么用throws子句声明将它抛出,否则编译错误。

判断一个方法可能出现异常的依据如下:

方法中有throw语句。

调用了其他方法,其他方法用throws子句声明抛出某种异常。

(7)throw语句后面不允许紧跟其他语句,因为这些语句永远不会被执行。


异常流程的运行过程

try...catch...finally还有return和System.exit()

(1)finally语句不被执行的唯一情况是先执行了用于终止程序的System.exit()方法。java.lang.System类的静态方法exit()用于终止当前的java虚拟机进程,java虚拟机所执行的java程序也随之终止。

此外,如果在执行try代码块时,突然关掉电源,所有的进程都终止运行,也不会执行finally语句(java虚拟机都自身难保)

对异常进行响应之后还可以继续抛出异常,死死生生

(2)return语句用于退出本方法。在执行try或catch代码块中的return语句时,假如有finally代码块,会先执行finally代码块。

(3)finally虽然在return之前被执行,但finally不能通过重新给变量赋值的方式来改变return语句的返回值。

(4)建议不要在finally代码块中使用return,潜在错误包括覆盖try或catch语句和丢失异常,本来要捕获的,结果一个return过来,啥都没了。


Throwable类有两个直接子类:

Error类——表示仅靠程序本身无法恢复的严重错误

Exception类——表示程序本身可以处理的异常

IOException:操作输入流和输出流时可能出现的异常

ArithmeticException:数学异常

NullPointerException:空指针异常

IndexOutOfBoundsException:下标越界异常

ClassCastException:类型转换异常

IllegalArgumentException:非法参数异常

Exception类还可分为两种:运行时异常和受检查异常

运行时异常

RuntimeException类及其子类都被称为运行时异常,这种异常的特点是Java编译器不会检查它,也就是说,当程序中可能出现这类异常时,即使没有用try...catch语句捕获也没有用throws抛出,还是会通过编译。

受检查异常

除了RuntimeException及其子类外,其他的Exception类及其子类都属于受检查异常(Checked Exception)。

出现这类异常要么try...catch要么throws抛出。

区分受检查和运行时异常

受检查异常表示程序可以处理的异常。

运行时的异常表示无法让程序恢复运行的异常。

区分运行时异常和错误

用户定义异常

在处理异常时,捕获原始异常,再抛出新的异常,这种处理异常的办法称为异常转译。

异常转译使得异常类型与抛出异常的对象的类型位于相同的抽象层。

异常链机制是指把原始异常包装为新的异常类,即在新的异常类中封装了原始异常类,有助于查找产生异常的根本原因。

处理多样化异常

本来是一次只能抛出一个异常的,现在是要一次抛出多个异常。


异常的处理原则

1.异常只能用于非正常情况

2.为异常提供说明文档

3.尽可能避免异常

4.保持异常的原子性

5.避免过于庞大的try代码块

6.在catch子句中指定

7.不要在catch代码块中忽略被捕获的异常

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值