Java异常的正确使用

41 篇文章 0 订阅
7 篇文章 0 订阅

在java中关于处理异常的方法基本可分为俩种,一种是try catch 捕捉 一种是throw 或throws 抛异常。

那么在写Java程序的时候经常遇到需要或者抛出或者捕获异常的情况,在什么情况下抛出,什么情况下捕获应该依照一个什么样的规则呢?针对这些问题我进行了一次针对性的复习 然后下面就是我的一些想法,希望对大家有所帮助

首先我们需要明白try catch与 throws的区别:
    try-catch : 在当前位置处理异常
    throws    :   (语法格式:   在方法签名之后:throws 异常类型)
                    向上抛出异常,可以无限向上抛出,直到抛给main方法,main方法就抛给JVM虚拟机去解决
                    即比如在3个类中,第3个类出现异常,可向上抛给第二个类,第二个类如果不想解决,可继续抛给第一个类中去解决,然后在第一个类中(调用第三个类中方法会出现异常)==》就使用try-catch解决异常
相当于throws可以向上指定抛到某一个类里面去集合解决异常(使用try-catch解决异常),而try-catch是直接处理异常

注意:throws后可以同时抛出多个异常类型  ex:public void 方法名() throws ArithmeticException,NumberFormatException{}

throws 原理:有throws的方法的是所有代码中 , 某行代码创建了异常对象,直接去跟throws后的异常类型匹配,如果匹配不上,则自动匹配下一个,匹配上为止。  
          i: 如果能匹配上则向上抛出成功,由调用方法的地方来处理
         ii: 如果所有的都匹配不上,则抛出失败,由jvm默认处理
 

简单的来说就是:我们在写代码的时候 方法或者接口中存在异常时,自己能解决或者调用方根本不关心异常时可以采用try catch方式 而如果存在异常自己无法解决或者调用方需要自己解决的时候 就采用抛异常的方式。

如果我们在项目中随意的处理异常的话会存在什么问题呢?

1. 代码可读性变差,业务逻辑难以理解

2. 代码健壮性变差,异常信息被随意捕捉,甚至被吃掉

3. 破坏架构的分层清晰,职责单一的原则,为系统扩展带来很大阻碍

那我们在实际编写代码的如何正确考虑异常的使用呢?

首先了解下Java异常的设计初衷。Exceptions are the customary way in Java to indicate to a calling method that an abnormal condition has occurred. 一个很重要的概念——不正常情形。Java异常旨在处理方法调用时不正常情形,但我们该如何理解“不正常情形”?看下图,JDK给我们定义了以下异常体系:

根节点是Throwable,代表Java内所有可以Catch的异常都继承此,向下有两类,Exception和Error,日常用到较多的都是Exception,Error一般留给JDK内部自己使用,比如内存溢出OutOfMemoryError,这类严重的问题,应用进程什么都做不了,只能终止。用户抓住此类Error,一般无法处理,尽快终止往往是最安全的方式,既然什么都干不了就没必要抓住了。Exception是应用代码要重点关心的,其下又分为运行时异常RuntimeException和编译时异常。大部分情况而言RuntimeException以及他的子类像空指针、数组越界之类的异常都是不需要捕捉的,因为这些都是可以直接人工避免的。而编译时异常就大部分时候都需要捕捉或者抛异常了比如IO异常。

了解异常的结构后,接下来解决两个重要问题,何时抛异常和抛什么异常,何时抓异常和抓什么异常 何时会有异常抛出,总结起来有以下三个典型的场景:

1. 调用方(Client)破坏了协议

说白了就是调用方法时没有按照约定好的规范来传参数,典型的比如参数是个非空集合却传入了空值。这种破坏协议的还可以细分两类,一类是调用方从接口形式上不易觉察的规则但需要在出现时给调用方些强提示,带些信息上去,这时异常就是特别好的方式;另一类是调用方可以明确看到的规则,正常情况会正常处理协议,不会产生破坏,但可能因为bug导致破坏协议。要求传入整数,但转换数字时出错,此时可抛出特定异常并附上提示信息

2. (Method)知道有问题,但自己处理不了

这里"有问题",可能是调用方破坏了协议,但是单独提出来是要从被调用方出发考虑,比如Method内部有读取文件操作,但发现文件并不存在,FileInputStream在创建时抛出了FileNotFoundException,显然出现该问题时FileInputStream是处理不了的。这个时候也是可以抛出去让调用方去try catch 处理

3. 预料不到的情形

 

空指针异常、数组越界是这种典型的场景,一般是由于有代码分支被忽略

了解了何时会出现异常,但是需要抛出异常时是选择编译时异常还是运行时异常呢? 很多人可能会说,很简单啊,需要调用方catch的就编译时,否则运行时。问题来了,什么时候需要调用方catch?

分析编译时和运行时对代码编写的影响,可以总结出来区分时考虑的点有:调用方能否处理、严重程度、出现的可能性。

调用方能处理->编译时

调用方不能处理->运行时

严重程度高->运行时

出现可能性低->运行时

过程如下: 

首先从调用方开始考虑,如果是调用方破坏了协议,则抛出运行时异常,这类异常一般出现可能性较低,调用方已知,所以没必要强制调用方抓此异常。

然后如果问题出现被调用方,无法正常执行完成工作,这时候考虑该问题调用方是否可以处理,如果能处理,比如文件找不到、网络超时,则抛出编译时异常,否则比如磁盘满,抛运行时异常

解决了何时抛异常和抛什么异常,接下来是调用这些有异常的代码时,何时catch和catch什么异常呢? 攻守不分离... 免不了俗,总结一下几点供大家探讨:

  1. 不要轻易抓Throwable,图省事可能会带来巨大的隐患
  2. 应该尽量只去抓关注的异常,明确catch的都是什么具体的异常

  3. 自己处理不了,不要抓
  4. 比如上文DB可能会有异常,在DAO层是处理不了这种问题的,交由上层处理。抓异常宜晚不宜早,抛异常宜早不宜迟。

  5. 切忌抓了,又把异常吞掉,不留下一丝痕迹
  6. 抓住异常,打行日志完事儿,不是一个好习惯。

  7. 切忌抓异常了将异常状态流和业务状态流混在一起,这样你算是彻底抛弃了Java的异常机制

最后就是大家处理异常的时候也可以参考一下阿里的代码规范中的异常处理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值