该如何良好的实践Java中的Exception机制

标签: 何良 实践 java | 发表时间:2015-02-26 15:19 | 作者:changedi
出处:http://www.blogjava.net/

首先,我先声明一点,我讨论的仅限于互联网数据产品,当然可能会涉及到一些其他的抽象,但是所有的结论不代表能复用到所有场景。

 

        几乎每个Java程序员都清楚知道Java的异常和错误机制,无论是在面试过程中,还是在学习中,你看到Exception,无非就是了解一下继承关系、子类、和Error的关系等等。当然这些知识点是基础,那么在实践中,用到了吗?你确定你使用Exception时没有偷懒?我的经验告诉我,良好的使用Exception能让你的程序bug更少,或者至少能保证你的程序更容易被理解和跟踪。

 

        先回到老的知识点吧——Java的异常机制,我们知道Java里的异常是完全继承Throwable的,正如java doc里注释的,无论你throw的还是JVM throw的,抑或是你想catch的,都必须继承Throwable。我这里帮助大家纠正的第一个点就是:java.lang.Throwable是一个class,不是一个interface,千万别被名字欺骗。优秀的程序员有好的命名习惯,当Java程序员默认认为带有-able后缀的都该是接口时, Josh Bloch给大家上了一课——Throwable就是类。回到正题,Throwable有两大子类,一个是java.lang.Error,一个是java.lang.Excpetion,Error是错误,一般多用于系统异常和底层错误,Error抛出就会导致程序终止;而Exception是异常,有程序引起,又分为受检的checked和非受检的unchecked(不知道哪本书这么翻译的来着),受检的异常是普通异常,就是你需要catch的来打日志或者补救的(这种做法叫吞掉异常),非受检的多数是RuntimeException,就是运行时异常,这类异常不建议catch,因为这个是程序bug,需要被人发现,所以建议抛出引起程序终止。Blah~blah~ 这些都是老生常谈的话题,需要深入的点是有几个:

 

        第一,Error是建议到系统级别的错误的,包括虚拟机的,我们常见的就是java.lang.VirtualMachineError,这是一个JVM级别的抽象Error,如果你觉得没见过?那么不用奇怪,它的两个儿子你肯定见过:OutOfMemoryError和StackOverflowError。Error其实真没好说的,一般情况不建议捕获,程序员也用的较少,但是你看很多基础框架或者系统软件,都是有自定义Error的,所以当你的工作层次或者范围你能确定比较底层时,其实是可以自定义一些Error来控制程序的错误的。我这样说可能也不是很好理解,换个简单的话:你的架构设计中需要考虑到异常的处理,那么首先要对异常定级别,如果有可能有偏底层的异常时,或者是本不该出现且不建议用户(多数是依赖你的库进行开发的其他程序员)捕获时,定义为Error是个不错的选择。当然也不是说做上层开发的程序员就不能使用Error,只要你设计合理,你可以在必要时抛出Error来终止程序——比如提醒你的老板再不加薪就Error到死:)

 

        第二,Exception分两类这事几乎人人皆知,受检的异常往往是web后端开发或者服务开发自定义的业务异常比如BizServiceException或者常见的DAOException,这些异常在开发定义时总是直接extends Exception,而忽视了究竟这些异常我们对它们的期望是什么,这里我想强调一点,我们在业务系统架构中考虑到异常机制,自定义的异常不是为了有异常而定义异常,一定对它本身是有期望的。我们对异常的一个基本期望是异常究竟该被谁捕获,如果被你的服务下游捕获,那么这必须是一个受检的异常,如果是系统自身需要,那么这个我个人认为是分阶段设计的,在初期,也就是未发布状态,这些Exception应该总是被抛出的,因为这样可以快速的让测试和质量控制人员发现系统崩溃的点。在发布阶段,异常可能需要被内部消化,这时受检异常就要提供给业务系统,让业务开发自行捕获异常。当然,好的系统架构可能会把Exception作为一个内部可见外部不可见的内容,而基于此完全封装一套error code对外,这应该算是比较友好的做法了,也是很多API设计时的标准规范。毕竟对外部透明,不要让用户看到你的Exception,这是非常友好的做法。

 

        第三,关于catch,就针对上面的第二点讲,吞异常这事不是没人干过,我们往往担心系统错误而一个try catch 捕获所有Exception,有的甚至不够,还升一级,捕获Throwable,这应该是最糟糕的代码设计(但不幸的是在我现在开发的系统和曾经开发过的业务系统中,这类代码非常普遍)。开发人员不应该因为时间紧、赶进度等接口而忽视Exception,就拿最上层的业务开发举例,开发可能会调用各类服务、访问数据库、访问缓存和文件系统等等,而这些服务必然包含了各种异常,而catch一个Exception,试图通过吞噬异常保护系统或者页面正常访问,而打日志到后台,通过分析日志来偷偷的解决bug……说起来真是汗毛倒竖。我的观点:如果有错误,那么让它尽早暴露出来,我们应该通过尽量多的测试和优化来避免错误,而不是偷偷的隐藏。事实也证明,日志里大量的NPE或者其他RuntimeException存在,但是无人问津,“系统不是好好的吗?”,“页面不是没问题吗”这样的说辞可以让开发看起来毫无责任,但是这为系统长期的维护和后续的扩展都带来了无尽的烦恼和坑。

 

         综上,我个人的经验告诉我几点对待Exception的方法:

        1,花时间了解涉及到的每个服务和方法所可能抛出的异常。事实往往是理解异常的关系和机制其实不花时间,了解后再开发,你会比别人知道更多的异常点,这能保证你程序的健壮性;

        2,无论你在服务开发还是服务使用层级,都要尝试或者想到封装异常,提供友好错误设计的方案,最简单的是自定义一个业务Exception来封装。

        3,不要在你的方法开始try,结束时catch,这防御性太强了,很美品位。

        4,前三点可能都是错的,因为我自己也没有完全实践:)



changedi 2015-02-26 15:19 发表评论

相关 [何良 实践 java] 推荐:

该如何良好的实践Java中的Exception机制

- - BlogJava-首页技术区
首先,我先声明一点,我讨论的仅限于互联网数据产品,当然可能会涉及到一些其他的抽象,但是所有的结论不代表能复用到所有场景.         几乎每个Java程序员都清楚知道Java的异常和错误机制,无论是在面试过程中,还是在学习中,你看到Exception,无非就是了解一下继承关系、子类、和Error的关系等等.

使用 Java Native Interface 的最佳实践

- - 博客 - 伯乐在线
简介: Java™ 本机接口(Java Native Interface,JNI)是一个标准的 Java API,它支持将 Java 代码与使用其他 编程语言编写的代码相集成. 如果您希望利用已有的代码资源,那么可以使用 JNI 作为您工具包中的关键组件 —— 比如在面向服务架构(SOA)和基于云的系统中.

Java Web 服务性能优化实践

- - 博客 - 伯乐在线
来源: IBM developerworks. 简介: 本文介绍如何提升 Java Web 服务性能,主要介绍了三种方法:一是采用 Web 服务的异步调用,二是引入 Web 服务批处理模式,三是压缩 SOAP 消息. 重点介绍在编程过程中如何使用异步 Web 服务以及异步调用和同步调用的差异点. 本文还示范了如何在项目中使用以上三种方法,以及各种方法所适合的应用场景.

Java 异步编程最佳实践

- - 鸟窝
最近异步编程非常流行, 主要是它能够在多核系统上提高吞吐率. 异步编程是一种编程方式,可以提高对UI的快速响应. Java中的异步编程模型提供了一致性的编程模型, 可以用来在程序中支持异步. 本文讨论了在使用Java执行异步操作应该遵循的最佳实践. 原文: Best Practices of Asynchronous Programming With Java.

10个精妙的Java编码最佳实践

- - ImportNew
这是一个比Josh Bloch的 Effective Java规则更精妙的10条Java编码实践的列表. 和Josh Bloch的列表容易学习并且关注日常情况相比,这个列表将包含涉及API/SPI设计中不常见的情况,可能有很大影响. 我在编写和维护jOOQ(Java中内部DSL建模的SQL)时遇到过这些.

java 内存移到堆外!!! Jvm gcih 淘宝优化JVM实践

- - CSDN博客互联网推荐文章
出自Jvm  GC-Invisible Heap. GC-Invisible Heap,简称GCIH,是一种将Java对象从Java堆内移动到堆外,并且可以在JVM间共享这些对象的技术. GCIH顾名思义就是GC访问不到的堆,它是对JVM内存管理机制的一个有益的补充. 在某些特殊的应用中有大量生命周期很长的对象,在应用运行的整个过程中它们都存在,不需要被GC回收.

java并发编程实践学习笔记

- - zzm
    原子操作:原子为不可再分操作.    Violation :可见关键字.    Synchronized:内部隐示锁 .    ReentrantLock:显示锁 .    ReentrantReadWriteLock:读写锁 . jmm(java内存模型):. 线程对所有变量的操作都是在工作内存中进行,线程之间无法相互直接访问,变量传递 均需要通过主存完成.

[译]Java中9个处理Exception的最佳实践

- - 后端技术杂谈 | 飒然Hang
在Java中处理异常并不是一个简单的事情. 不仅仅初学者很难理解,即使一些有经验的开发者也需要花费很多时间来思考如何处理异常,包括需要处理哪些异常,怎样处理等等. 这也是绝大多数开发团队都会制定一些规则来规范对异常的处理的原因. 而团队之间的这些规范往往是截然不同的. 本文给出几个被很多团队使用的异常处理最佳实践.

编写高性能 Java 代码的最佳实践

- - 码农网 » JAVA开发
摘要:本文首先介绍了负载测试、基于APM工具的应用程序和服务器监控,随后介绍了编写高性能Java代码的一些最佳实践. 最后研究了JVM特定的调优技巧、数据库端的优化和架构方面的调整. 在这篇文章中,我们将讨论几个有助于提升Java应用程序性能的方法. 我们首先将介绍如何定义可度量的性能指标,然后看看有哪些工具可以用来度量和监控应用程序性能,以及确定性能瓶颈.

高吞吐、低延迟 Java 应用的 GC 优化实践

- - IT瘾-dev
“以下信息节选自涤生的翻译内容”. 本篇原文作者是 LinkedIn 的 Swapnil Ghike,这篇文章讲述了 LinkedIn 的 Feed 产品的 GC 优化过程,虽然文章写作于 April 8, 2014,但其中的很多内容和知识点非常有学习和参考意义. 高性能应用构成了现代网络的支柱. LinkedIn 内部有许多高吞吐量服务来满足每秒成千上万的用户请求.