Java的异常处理

1,

1.1 在Java的异常处理机制中,什么时候应该抛出异常,什么时候捕获异常?

 RuntimeException 子异常,有:数组越界异常、空指针异常、0作除数异常

非RuntimeException 异常有:Socket异常、IO异常等(try-with-resources可以关闭实现closeable接口的流)

对比一下我们就会发现,RuntimeException 是在程序中可以完全避免的,比如数组越界异常,只要我在程序里作个判断,如果要访问的数组元素下标和数组的长度作一下比较就知道会不会越界,再比如空指针异常,如果在访问对象时判断一下对象的变量是否为空就可以了。

而非RuntimeException 则是程序无法避免的,比如IO异常,你的程序正在读一个文件,而这个文件所在磁盘出现了坏道,这就必然会引发IOException,这是不是靠编程高手编写完美的程序就可以法避免得了的,程序所能做的只有出现异常之后怎么处理的问题。
 

最近在公司里写代码的时候也在考虑这个问题:到底该如何捕获异常,如何处理异常日志?
下面简要谈下个人的经验。以MVC框架为例,首先controller层必须捕获异常,一般情况下不允许将系统内部的异常不做任何封装处理直接抛给客户端,这样对系统来说会暴露过多信息(异常栈信息都抛给客户端,可能会把SQL结构都抛出去),这是不安全因素;同时,这对用户来说也是不友好的,让一些不搞计算机的同学看得一头雾水,并且怀疑网站的正规性与可靠性,可能会直接影响未来的业务。因此,通常需要在controller层封装一些错误码、用户友好语句(系统维护中等等欺骗用户的语句)。当然这也不是铁律,有的时候需要后台进行较检,可能需要抛出一些非公用的异常信息到界面上,比如之前车贷项目中用到过的上传附件检查,就是用后台异常提示用户的。还有就是传入参数有效性较检,也需要抛出自定义异常(用@NotNull或NotEmpty等注解抛出message信息),即不需要捕获后统一处理返回到用户界面。
接着说一下service层,业务层需要处理业务逻辑,当然这一层不需要考虑参数是否为空,需要考虑的仅仅是从dao查询相关的异常,比如ConnectionExcepion、SQLException、BadSQLGrammerException等异常,这些异常我通常会去捕获,因为这些异常涉及到业务逻辑是否能正常执行,确实是service该考虑的事。做法是try...catch...捕获所有Exception,然后在catch中用logger.error("message, e = {}", e.getMessage())记录日志信息,用于到时候定位error代码位置,然后根据实际业务情况,看这个异常捕获后是否需要用一个异常封装一下信息抛给控制层,然后由控制层统一处理也好或者直接返回给用户)。注意,业务层异常不是用于捕获NullPointerException等无聊的代码健壮性问题,这些都是业务层代码逻辑没控制好,查询值需要做一下非空判断。
至于dao层,我想没必要说了吧,一般我也不写实现类,持久层的mybatis3.X都用动态代理来实现了。
BTW,try...catch最好有针对性,不要把所有美容都放到try里面检查是否有异常,一般来说总有些无关异常的语句可以提到异常块之外,反正能提就提。

1.2 知道哪些异常?

异常类分两大类型:Error类代表了编译和系统的错误,不允许捕获;Exception类代表了标准Java库方法所激发的异常。Exception类还包含运行异常类Runtime_Exception和非运行异常类Non_RuntimeException这两个直接的子类

1.3 异常处理(exception handling)和错误处理(error handling)有什么区别?

异常和返回错误的本质区别是什么?答:异常是强类型的,类型安全的分支处理技术;而返回错误是其弱化的,不安全的版本。

要做好异常处理就必须了解 Exception 和 Error 的区别,它们主要有以下异同:

  • 首先 Exception 和 Error 都是继承于 Throwable 类,在 Java 中只有 Throwable 类型的实例才可以被抛出(throw)或者捕获(catch),Exception 和 Error 体现了JAVA 这门语言对于异常处理的两种方式。
  • Exception 是 Java 程序运行中可预料的异常情况,我们可以获取到这种异常,并且对这种异常进行业务外的处理。它分为检查性异常和非检查性(RuntimeException)异常。两个根本的区别在于,检查性异常必须在编写代码时,使用 try catch 捕获(比如:IOException异常)。非检查性异常 在代码编写时,可以忽略捕获操作(比如:ArrayIndexOutOfBoundsException),这种异常是在代码编写或者使用过程中通过规范可以避免发生的。
  • Error 是 Java 程序运行中不可预料的异常情况,这种异常发生以后,会直接导致 JVM 不可处理或者不可恢复的情况。所以这种异常不可能抓取到,比如 OutOfMemoryError、NoClassDefFoundError等。

1.4 例子:NoClassDefFoundError 和 ClassNotFoundException 有什么区别?

ClassNotFoundException 产生的原因是:

Java 支持使用反射方式在运行时动态加载类,例如使用 Class.forName 方法来动态地加载类时,可以将类名作为参数传递给上述方法从而将指定类加载到 JVM 内存中,如果这个类在类路径中没有被找到,那么此时就会在运行时抛出ClassNotFoundException 异常。

原因:

  • 常见问题在于类名书写错误。
  • 当一个类已经被某个类加载器加载到内存中了,此时另一个类加载器又尝试着动态地从同一个包中加载这个类。通过控制动态类加载过程,可以避免上述情况发生。
  • 打包过程漏掉了部分类。
  • jar包出现损坏或者篡改。

1.5 异常和错误形象理解

preview

举个形象的例子帮助你理解。

假如你开车上山,车坏了,你拿出工具箱修一修,修好继续上路(Exception 被捕获,从异常中恢复,继续程序的运行),车坏了,你不知道怎么修,打电话告诉修车行是什么问题,要车行过来修。(在当前的逻辑背景下,你不知道是怎么样的处理逻辑,把异常抛出去到更高的业务层来处理)。你打电话的时候,要尽量具体,不能只说我车动不了了。那修车行很难定位你的问题。(要捕获特定的异常,不能捕获类似Exception 的通用异常)。还有一种情况是,你开车上山,山塌了,这你还能修吗?(Error:导致你的运行环境进入不正常的状态,很难恢复)

2. java8新特性

  • Lambda 表达式 − Lambda 允许把函数作为一个方法的参数(函数作为参数传递到方法中)。

  • 方法引用 − 方法引用提供了非常有用的语法,可以直接引用已有Java类或对象(实例)的方法或构造器。与lambda联合使用,方法引用可以使语言的构造更紧凑简洁,减少冗余代码。

  • 默认方法 − 默认方法就是一个在接口里面有了一个实现的方法。

  • 新工具 − 新的编译工具,如:Nashorn引擎 jjs、 类依赖分析器jdeps。

  • Stream API −新添加的Stream API(java.util.stream) 把真正的函数式编程风格引入到Java中。

  • Date Time API − 加强对日期与时间的处理。

  • Optional 类 − Optional 类已经成为 Java 8 类库的一部分,用来解决空指针异常。

  • Nashorn, JavaScript 引擎 − Java 8提供了一个新的Nashorn javascript引擎,它允许我们在JVM上运行特定的javascript应用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

洋气月

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值