背景

最近公司一直在执行sonar扫描代码bug、漏洞及异味,但发现了很对异常处理的问题,大多数是对Java异常处理不正确导致的,那本文就谈谈Java的异常是什么?设计者的初衷又是什么?

Exception 介绍

ExceptionError都是继承了Throwable类,在Java中只有Throwable类型的实例才可以被抛出(throw)或者捕获(catch),它是异常处理机制的基本组成类型。

ExceptionError体现了Java平台设计者对不同异常情况的分类。Exception是程序正常运行中,可以预料的意外情况,可能并且应该被捕获,进行相应处理。

Error是指在正常情况下,不大可能出现的情况,绝大部分的 Error 都会导致程序(比如虚拟机自身)处于非正常的、不可恢复状态。既然是不正常情况,所以不便于也不需要捕获,常见的比如OutOfMemoryError之类,都是Error的子类。

Exception异常本身又分为可检查(checkd)异常和不可检查(uncheckd)异常。

可检查异常在源代码里必须显式地进行捕获处理,这是编译期检查的一部分。不可检查的ErrorThrowable,而不是Exception,通常我在编码过程中编译器会提示如何处理异常,类似于我们常见的try catch或者继续throw

不检查异常就是所谓的运行时异常,类似NullPointerException、ArrayIndexOutOfBoundsException之类,通常是可以编码过程中避免的代码逻辑错误,具体根据需要来判断是否需要捕获,并不会在编译期强制要求捕获此类异常。

争议点

Checkd Exception(可检查异常)一直都是Java语言比较有争议的一个功能。

Java可检查异常的提倡者认为通过检查异常能够确保它们从异常中恢复;而反对者却认为因为这些错误都是常见的错误,所有它们根本无法从异常中恢复。

同时,Java8lambdas已经问世一段时间。在它们的世界中Checkd Exception是如何使用的呢?

设计者意图

90年代中期,Sun公司的James Gosling提出了一种新的语言(Java)Java这门编程语言本身是一门面向服务端长期运行的编程语言,借鉴并弥补了C、C++的不足,当然异常处理也不例外。

C语言的异常处理机制,因为C本身是单返回值,异常信息通常通过一个int值来表示成功还是失败

C++弥补了C的不足,出现异常时可以发送错误信号,即引入了Exception机制,出现异常、抛出异常。但C++同时带来了另外一个问题,调用的任何一个函数都可能出现异常,即异常信息不确定。

Java设计者吸取了C++异常设计的经验教训,他认为必须有更好的方法,并将异常的概念引入到Java中。并认为异常本身并不重要,而在于发生了什么异常。所以Java引入了Checkd ExceptionJava方法的所有者声明异常信息,方法调用者处理异常信息,这使得ExceptionJava中变成了司空见惯的事情。所以就导致了代码中经常出现catch(e Exception){//忽略},直接捕获并忽略异常信息,并不能使异常信息有效传递。

检查异常的目的是在本地标记并迫使开发人员处理可能的异常。已检查的异常必须在方法签名上声明或处理。

这是为了鼓励软件的可靠性和弹性。旨在从意外情况中恢复 – 除了成功以外的可预测结果,例如尝试付款时出现InsufficientFundsException。关于实际上需要进行什么恢复,目前尚没有明确答案。

运行时异常也包含在Java中。由于空指针、数据错误、非法状态、访问都可能在代码中的任何地方发生,因此将它们作为RuntimeException的子类。这种异常也就是类似于C++不可检查异常。

运行时异常可以在任何地方抛出,而无需声明,并且更加方便。但是直接使用它们是否正确?

优缺点

这里的关键点是运行时和检查异常在功能上是等效的。但已检查异常可以执行的处理或恢复,而运行时异常则无法做到。

反对检查异常的最大论点是,大多数异常无法修复。一个简单的事实是,我们的子系统都是正常的,我们看不到具体实现逻辑,我们对此不负责,也无法修复其中的异常,所以不要往上层抛出可检查异常。

尤其问题是JDBC(SQLException)EJB RMI(RemoteException)。这些强迫性普遍存在的系统可靠性问题(实际上不是可修复的)不是按照原始的可检查异常概念来确定可修复的突发事件,而是要广泛声明。

对于任何方法,失败的可能性都包括它调用的所有子方法。潜在的故障会累积在调用链中。在方法签名上声明这些异常,并且不再为开发人员提供一个特定的和局部的返回值,让开发人员检查在调用链中传播的受检查异常。

大多数EJB开发人员都经历过这种情况–整个层或整个代码库的方法都需要声明异常。调用具有不同异常的方法需要调整许多方法。

许多开发人员被告知要捕获底层代码的异常,然后将它们重新抛出为更高级别(应用程序级别)的已检查异常。这需要一定的工作量(每个项目最多2000个)非功能性的抛雪球块。

于是Java开发人员吞下异常、隐藏原因、重复记录日志、返回null,未初始化的数据都变得很普遍。大多数项目因为异常问题可能会算出上百个错误编码或完全错误。

最终,开发人员对大量的catch块产生了反感,这些块本身已经成为错误的根源。

Checked Exception - 与功能代码不兼容

然后我们来看看Java8,它具有新的编程范式-例如lambda、Streams功能组合。

这些特性是建立在泛型之上的——参数和返回类型被泛化,这样迭代和流操作(forEach、map、flatMap)可以被编写来执行一个公共操作,而不考虑对象类型。

但是,与数据类型不同,声明的异常无法泛化。

Java中没有提供流操作(例如Stream.map)可检查异常,该操作需要一个lambda来声明某些已检查的异常,并透明地将相同的已检查的异常传递给周围的代码。

这一直是反对检查异常的主要要点–抛出和接收catch块之间的所有代码逻辑都必须意识到异常。

解决方法是在RuntimeException包装它,它隐藏了异常的原始类型,使得原始概念中设想的特定于异常的catch块变得毫无用处。

最后,我们可以简单地理解Java的新理念,注意到Java8中没有一个新的函数接口声明checked异常。

可检查异常使用中注意事项

  • 所有的方法尽量不要定义可检查异常,而是通过返回错误信息。
  • 尽量不要尝试捕获最顶级的Exception,尽量捕获具体的Exception,因为代码本身是写给人看的,机器只是顺便执行,我们应该尽量通过代码显示直观的信息,而不是只是Exception,因为Exception恰恰隐藏的真正的异常信息。
  • try/catch范围尽可能小,因为它本身需要创建堆栈信息,会产生额外的性能开销。所以只需要捕获需要的代码片段,尽量不要使用一个大的try包住整个代码块。
  • 不要生吞异常。这是异常处理过程中需要特别注意,因为它可能会使出现问题后难以诊断。有时我们的主要精力都放在了主要逻辑上面,往往对异常信息疏忽或者认为该异常不会出现,我们千万不要做这种假设,我们以为的不可能出现的细节问题,往往会无限放大。

例如下面片段,这个片段导致的问题在于没有异常输出也没有日志打印,更没有抛出来:

try{//业务逻辑}catch(Exception e){//没有任何逻辑}

下面这段代码的问题在于直接标准输出异常,通过这种方式难以判断该日志如何和出现问题的逻辑结合起来,导致难以诊断问题所在,正确的姿势应该详细把错误信息输出到日志中。

try{//业务逻辑}catch(Exception e){    e.printStackTrace()}

结论

与以前的语言相比,Java异常在可靠性和错误处理方面提供了主要优势。Java支持可靠的服务器和商业软件,这是C/C ++无法做到的。

可检查异常以其原始形式是试图处理突发事件而不是失败。值得称赞的目标是突出显示特定的可预测点(无法连接、找不到文件等)并确保开发人员能够处理这些点。

Java异常最初的概念中从未包括的是,大量系统性和不可恢复的故障。这些失败从未被声明为受检查异常,这也就导致Java倡导者认为Java可检查异常出现问题,根本原因在于开发者的使用方式存在问题。

通常,代码中可能会发生故障,而EJB、Web、Swing/AWT容器已经通过提供最外部的失败请求异常处理程序来解决此问题。最基本的正确策略是回滚事务并返回错误。

运行时异常允许对捕获的异常进行任何可能的异常处理,但要避免限制性的编码。使用Java异常过程中要遵循早期抛出、延迟捕获(最外层)的最佳实践,通过这些可以简化编码。

一些领先的和有影响力的Java框架现在已经明确地摆脱了检查异常。Spring、Hibernate和现代Java框架/供应商仅使用运行时异常,而这种便利性是它们流行的主要因素。

诸如Josh Bloch(Java Collections框架)、Rod JohnsonAnders Hejlsberg(C#之父)、Gavin KingStephen Colebourn(JodaTime)等人都反对检查异常。

现在,在Java8中,lambda是向前迈出的基本一步。这些语言特性将控制流从内部的功能操作中抽象出来。正如我们所看到的,这使得检查异常成为过去,即立即声明或处理的要求。

对于开发人员而言,始终必须注意可靠性并诊断可能的故障点(突发事件),例如打开文件、数据库连接等,这一点始终很重要。如果此时提供了良好的错误消息,我们将创建自诊断软件–工程成就的巅峰之作。

但是,我们应该使用未经检查的异常来执行此操作,并且如果必须重新抛出,则应始终使用RuntimeException或特定于应用程序的子类。

正如史蒂芬·科尔本(Stephen Colebourn)所说,如果您的项目仍在使用或提倡检查异常,则您的技能已过期5-10年。Java本身已经在前进了。

最后一点对于Java的可检查异常也不必要矫枉过正,因为Java的可检查异常已经遍布于大大小小的各种组件和系统中,对于一些分布式系统,比如出现网络等问题时,确实可以通过异常信息进行恢复,通过这种方式使我们可以构建出高质量的软件系统。

参考资料

http://literatejava.com/exceptions/checked-exceptions-javas-biggest-mistake/

https://www.oracle.com/technical-resources/articles/enterprise-architecture/effective-exceptions-part1.html

https://testing.googleblog.com/2009/09/checked-exceptions-i-love-you-but-you.html

原创不易,随手关注或者”在看“,诚挚感谢!

java 判断exception类型_Checked Exception | Java语言设计者的失误?相关推荐

  1. java 判断网络类型_javaexcel判断类型

    1. java如何判断数据类型 给你一个封装好的方法,只要把excel中的cell放入就会返回对应的值,里面有类型检测 public static String getExcelCellValue(H ...

  2. java 判断文件类型是否是音频_用java流方式判断文件类型

    这个方法只能在有限的范围内有效.并不是万金油 比如 图片类型判断,音频文件格式判断,视频文件格式判断等这种肯定是2进制且专业性很强的文件类型判断. 下面给出完整版代码 首先是文件类型枚取 packag ...

  3. Java 判断操作系统类型(适用于各种操作系统)

    最近一段时间写一个授权的程序,需要获取很多信息来保证程序不能随意复制使用,必须经过授权才可以. 为了限制用户使用的操作系统,必须有统一的方法来获取才可以. 在JAVA中,通过System.getPro ...

  4. java 判断文件类型是否是音频_Android判断文件类型(视频、音频、图片等) | 学步园...

    MediaFile.java package com.jaycee.vplayer.util; import java.util.HashMap; import java.util.Iterator; ...

  5. JAVA判断各种类型数据是否为空(亲测)

    1.判断list是否为空(Map.Set同list) if(list != null && list.size() == 0){ }if(list != null && ...

  6. java 判断int类型为空

    int id = 10; if("0".equals(String.valueOf(id)) || "null".equals(String.valueOf(i ...

  7. Java判断浏览器类型

    /** * 判断浏览器类型是否是IE,是则返回true,不是返回false * ServletActionContext是struts2上下文对象 * @author 李英夫(2010-6-20 上午 ...

  8. java判断浏览器类型_判断浏览器类型

    一.判断是否为IE 以前判断是否IE浏览器用的是window.navigator.userAgent,跟踪这个信息,发现在开发环境,识别为IE10,但访问服务器则识别为IE11,但IE11的userA ...

  9. 用java判断三角形类型_判断三角形类型

    假期无聊,继续九度OJ刷题,每天几道题,强制编程人啊! 三角形判断的方法: 直角三角形:勾股定理 锐角和钝角三角形:余玄定理 题目描述:给定三角形的三条边,a,b,c.判断该三角形类型. 输入:测试数 ...

最新文章

  1. 我学Delphi心得与笔记-------在控件上如何禁用Ctrl+V
  2. 折线分割平面(hdoj 2050,动态规划递推)
  3. Zookeeper详细参数解析zoo.cfg文件
  4. 【Python学习系列七】Windows下部署Python推荐系统recsys
  5. 自定义注解和拦截器,实现接口限流防刷
  6. c 内嵌php 韩天峰,PHP-X系列教程之内置函数的使用示例
  7. ABP Framework:移除 EF Core Migrations 项目,统一数据上下文
  8. 看贴回帖 感受与感动
  9. 五大最主流浏览器综合性能测试
  10. 初识 asp.net mvc(二)
  11. Atitit 知识图谱 RDF、RDFS和OWL数据模型 目录 1. 知识图谱 1 2. 二、知识图谱的前世今生 2 2.1. 五六十年代所提出的一种知识表示形式——语义网络(Semantic N
  12. hdu 4911 “Inversion”——逆序对问题
  13. 【python数据处理】替代Excel三维地图依据经纬度坐标的绘制热力地图的方式
  14. sql和mysql 语法区别吗_sql和mysql语法有什么不同
  15. 【5G系列】MAC (Medium Access Control)协议详解
  16. edem颗粒替换_EDEM离散元软件中颗粒替换与填充编程模版
  17. Lodop,前端自定义打印
  18. Revisiting Local Descriptor based Image-to-Class Measure for Few-shot Learning阅读笔记
  19. qq服务器只保留7天文件吗,qq离线文件服务器上的离线文件能保留几天(一般7天)?...
  20. SDRAM内存的接口和设计方法(并行输送接口)

热门文章

  1. JavaSE之ClassLoader
  2. 计算机架构及开机过程
  3. jst获取不到springmvc中model数据
  4. JavaScript中的this妙用
  5. 本地连接虚拟机的redis
  6. caffe 一些网络参数
  7. 重写系统中的UINavigationController返回按钮的事件
  8. CSS3--幽灵按钮特效(实例)
  9. vxworks 学习和windows azure 学习
  10. lua c语言混合编程入门