关于异常(bug)

异常

异常是什么?(非正常)

正常情况下,小王每日开车上班,耗时大约30分钟。但是今天点背,堵车,撞车,三十分钟肯定到不了。或者老于偶尔查作业。电脑打开反复弹窗,页面出现问题。再例如,一个月两千多生活费,与爸妈约定时间,有时候爸妈工作忙,把这事给忘了。生活中异常很常见。

在程序运行过程中,出现的不正常情况叫做异常

在程序中阻止当前方法或作用域执行继续执行的问题,我们称之为异常。

没有异常的程序基本不存在。

异常的正确处理能够提示编程人员或者用户使本来中断的程序以适当的方式继续执行或者退出,并且能够保存用户的当前操作或者对数据进行回滚,最后再把占用的资源释放掉。

注意:

1、 相同的代码在运行的时候,根据输入的参数或者操作不同,有可能发生异常,有可能不发生异常。应该在写代码的过程中尽可能的保证代码的正确性,不要到处都是bug

2、 如果要解决代码中出现的异常,需要添加非常复杂的代码逻辑来进行判断,会使代码变得非常臃肿,不利于维护,可读性比较差。因此,推荐大家使用异常机制来处理程序运行过程中出现的问题

3、 程序在运行过程中如果出现了问题,会导致后面的代码无法正常执行,而使用异常机制之后,可以对异常情况进行处理,同时后续的代码会继续执行,不会中断整个程序。

4、 在异常的处理过程中,不要只是简单的输出错误,要尽可能的将详细的异常信息输出。e.printStackTrace():打印异常的堆栈信息,可以从异常信息的最后一行开始追踪,寻找自己编写的java类

异常信息:

1、 定位(很重要)倒着找。往上数个两三行基本就解决了。

2、 找到bug进行修改

异常信息的作用:

Soute.getMessage()

跟踪堆栈信息,可能造成异常的代码进行输出

e.rintStackTrace()

异常从外找起

父类:exception

调用父类,异常信息不完整

处理异常:捕获,声明,抛出异常

子类写前边后边写后边

Java的异常体系结构。

Java中的所有不正常类都继承Throwable类,Throwable类下又有Error类与Exception类这两个子类。Error类主要是指系统错误,一旦出现,程序就彻底挂了。所以我们所讨论的异常是指Exception。Exception类又分为RuntimeException(非检查异常)与其他异常(检查异常)。空指针异常,数组下标越界异常,类型转换异常和算术异常等都是运行时异常的子类。运行时异常会由JVM自动抛出并自动捕获。而绝大部分的运行时异常说明代码本身有问题,应该从逻辑上改进代码。检查异常会由很多原因造成,比如文件不存在了(文件异常IOException)或者连接错误(SQLException)以及各种想不到的各种奇葩原因,这就需要手动添加捕获以及处理语句。

处理异常的方式:

 捕获异常

 try{代码逻辑}catch(Exception e){异常处理逻辑}

 try{代码逻辑}catch(具体的异常Exception e){异常处理逻辑}catch(具体的异常):

             可以针对每一种具体的异常做相应的更丰富的处理

注意:当使用多重的catch的时候一定要注意相关异常的顺序,将子类 放在最前面的catch,父类放在后面的catch

执行过程中可能存在的情况:

1、 正常执行,只执行try中的代码

2、 遇到异常情况,会处理try中异常代码之前的逻辑,后面的逻辑不会执行,最后会执行catch中的代码

3、 使用多重catch的时候,会遇到异常子类不匹配的情况,此时依然会报错,因此建议在catch的最后将所有的异常的父类写上

throws:声明异常

1、在异常情况出现的时候,可以使用try…catch…finally的方式对异常进行处理,除此之外,可以将异常向外抛出,由外部的进行

在方法调用过程中,可以存在N多个方法之间的调用,此时假如每个方法中都包含了异常情况,那么就需要每个方法中都进行try…catch,另外一种比较简单的方式,就是在方法的最外层调用处理一次即可使用throws的方法,对所有执行过程中的所有方法出现的异常进行统一集中处理。

2、如何判断是使用throws还是使用try…catch…

最稳妥的方式是在每个方法中都进行异常处理

偷懒的方式是判断在整个调用的过程中,外层的调用方法是否对异常的处理,如果有,直接使用throws,如果没有,那么就要使用try…catch…

throw抛出异常

捕获到的异常,可以在当前方法的catch块中处理,也可以抛出给调用者去处理。

自定义异常:

       在Java的api中提供了非常丰富的异常类,但是在某些情况下不太满足我们的要求,此时需要自定义异常

步骤:

1、 继承Exception类

2、 自定义实现构造方法

3、 需要使用的时候,使用throw new 自定义异常的名称:

什么时候需要自定义异常?

一般情况下不需要

但是在公司要求明确,或者要求异常格式规范统一的时候必须要自己实现的

在Java中我们会用try-catch以及try-catch-finally来捕获并处理异常。

try抛出异常后,抛出异常的方法会中止执行,然后程序的控制权会交由catch块中的异常处理程序来进行处理。在try-catch语句块执行完之后,在catch块外面的其他语句会顺序执行。catch语句块可以不止有一个。我们应遵循先子类后父类的顺序编写多重catch语句块,当程序抛出异常的时候,异常处理系统会就近找匹配的异常处理程序,而子类继承于父类,针对于父类的异常处理程序对于子类也是适用的。当然,如果多重catch语句块的顺序写错了,编译器也会报错的。编译器在编译的时候是不会通过的,会直接报错。有时候用try-catch处理完异常问题之后,还会进行一些善后工作比如说关闭连接或者关闭一些已经打开的文件,这时候我们用finally语句块进行善后工作。另外,try语句块不可以独立存在,必须与catch或者finally块同存。

在程序执行过程中,如果处理异常的部分包含finally的处理,那么无论代码是否发生异常,finally中的代码总会执行。只有一种情况下finally语句块不执行,终止JVM。

Finally包含哪些处理逻辑?

1、 I/O流的关闭操作一般写在finally语句块中

2、 数据库连接的关闭操作一般写在finally语句块中

关于return

try语句在返回前,将其他所有的操作执行完,保留好要返回的值,而后转入执行finally中的与,而后分为以下三种情况:

情况一:如果finally中有return语句,则会将try中的return语句“覆盖”掉,直接执行finally中的return语句,得到返回值,这样便无法得到try之前保留好的返回值。

情况二:如果finally中没有return语句,也没有改变要返回的值,则执行完finally中的语句后,会接着执行try中的return语句,返回之前保留的值。

情况三:如果 finally中没有return语句,但是改变了要返回的值,这里有点类似于引用传递值和值传递的区别,分以下两种

(1) 如果return的数据是基本数据类型或文本字符串,则在finally中对该基本数据的改变不起作用,try中的return语句依然会返回进入finally块之前保留的值。

(2) 如果return的数据是引用数据类型,而在finally中对该引用数据雷龙星的属性值的改变起作用,try中的return语句返回的就老实在finally中改变后的该属性的值。

Java中的异常抛出

throw—将产生的异常抛出(具体动作)

throws—声明要抛出何种类型的异常(用来声明)

public void 方法名(参数列表)throws 异常列表{

//调用会抛出异常的方法或者:

throw new Exception();

}

如果某个方法调用到会抛出异常的方法,那么必须添加try-catch语句去尝试捕获这种异常或者添加throws声明来将异常抛出给更上一层的调用者去进行处理。

自定义异常

class 自定义异常 extends 异常类型{ }

Java中的异常链

把捕获的异常包装成一个新的异常,在新的异常中添加对原始的异常的引用,再把新异常抛出,就像是链式反应一样,一个导致另一个,在Java中这就叫异常链。

例:方法1用来抛出自定义异常

方法2调用方法1,捕获自定义异常,并且包装成运行时异常,继续抛出

main方法中,调用方法2并尝试捕获方法2抛出的异常。

public void test1() throws EatException{    //声明将要抛出的异常

    throw new EatException(“吃饭别乱开玩笑!”);     //调用被定义的含参构造器

}

public void test2(){

    try{

test1();

}catch(EatException e){

//调用RuntimeExecption的含参构造器

RuntimeException newExc = new RuntimeException(“吃饭乱笑,小心窒息”); 

//调用newExc的initCase方法,并且将捕获的EatException给传递进去

newExc.initCause(e);

//抛出这个新异常

throw newExc;  

}

创建实例后(new了以后)

try{

  对象.test2();

}catch(Exception e){

 e.printStackTrace();

}

执行后

异常提示信息先输出:吃饭乱笑,小心窒息(java.lang.RuntimeException)

再输出:吃饭别乱笑!(Cause by:xxx.xxxx.test.EatException)

在main方法中捕获了一个运行时异常,提示信息是:吃饭乱笑,小心窒息

往下看,Caused by由此句可以看出该运行时异常是由“吃嗨了”、异常引起的,而这个异常是在这里(下一行提示)被最先抛出来的。

新的异常中包含原始异常的所有信息,根据这点可以一行一行追溯最初异常发生的位置。这里我们通过调用新异常的initCause方法去引用了原始异常,从而实现了异常链的功能。

也可以不用调用initCause方法,在test2()中直接将捕获的异常e传递进去。

第一行输出捕获了一个运行异常,提示信息为:吃饭乱笑,小心窒息。该运行时异常由“吃嗨了”异常引起的。该异常最初发生的位置,看Cased by的下一行提示。以上两种方法是我们实现异常链的两种写法。

实际应用中的经验与总结

1、 处理运行时异常是,采用逻辑去合理规避同时辅助try-catch处理(可以基本做到没有漏网之鱼)

2、 在多重catch块后面,可以加一个catch(Exception)来处理可能会被遗漏的异常

3、 对于不确定的代码,也可以加上try-catch,处理潜在的异常

4、 尽量去处理异常,切忌只是简单的调用printStackTrace()去打印输出(最好加上其他操作,比如业务回滚等等)

5、 具体如何处理异常,要根据不同的业务需求和异常类型去决定

6、 尽量添加finally语句块去释放占用的资源

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值