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

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介:
首先,我先声明一点,我讨论的仅限于互联网数据产品,当然可能会涉及到一些其他的抽象,但是所有的结论不代表能复用到所有场景。

        几乎每个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,前三点可能都是错的,因为我自己也没有完全实践:)

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
10天前
|
存储 监控 小程序
Java中的线程池优化实践####
本文深入探讨了Java中线程池的工作原理,分析了常见的线程池类型及其适用场景,并通过实际案例展示了如何根据应用需求进行线程池的优化配置。文章首先介绍了线程池的基本概念和核心参数,随后详细阐述了几种常见的线程池实现(如FixedThreadPool、CachedThreadPool、ScheduledThreadPool等)的特点及使用场景。接着,通过一个电商系统订单处理的实际案例,分析了线程池参数设置不当导致的性能问题,并提出了相应的优化策略。最终,总结了线程池优化的最佳实践,旨在帮助开发者更好地利用Java线程池提升应用性能和稳定性。 ####
|
9天前
|
安全 Java 数据库连接
Java中的异常处理:理解与实践
在Java的世界里,异常处理是维护代码健壮性的守门人。本文将带你深入理解Java的异常机制,通过直观的例子展示如何优雅地处理错误和异常。我们将从基本的try-catch结构出发,探索更复杂的finally块、自定义异常类以及throw关键字的使用。文章旨在通过深入浅出的方式,帮助你构建一个更加稳定和可靠的应用程序。
21 5
|
7天前
|
Java 程序员
深入理解Java异常处理机制
Java的异常处理是编程中的一块基石,它不仅保障了代码的健壮性,还提升了程序的可读性和可维护性。本文将深入浅出地探讨Java异常处理的核心概念、分类、处理策略以及最佳实践,旨在帮助读者建立正确的异常处理观念,提升编程效率和质量。
|
8天前
|
Java 开发者 UED
深入探索Java中的异常处理机制##
本文将带你深入了解Java语言中的异常处理机制,包括异常的分类、异常的捕获与处理、自定义异常的创建以及最佳实践。通过具体实例和代码演示,帮助你更好地理解和运用Java中的异常处理,提高程序的健壮性和可维护性。 ##
26 2
|
8天前
|
Java 开发者
Java中的异常处理机制深度剖析####
本文深入探讨了Java语言中异常处理的重要性、核心机制及其在实际编程中的应用策略,旨在帮助开发者更有效地编写健壮的代码。通过实例分析,揭示了try-catch-finally结构的最佳实践,以及如何利用自定义异常提升程序的可读性和维护性。此外,还简要介绍了Java 7引入的多异常捕获特性,为读者提供了一个全面而实用的异常处理指南。 ####
25 2
|
9天前
|
安全 Java 程序员
Java内存模型的深入理解与实践
本文旨在深入探讨Java内存模型(JMM)的核心概念,包括原子性、可见性和有序性,并通过实例代码分析这些特性在实际编程中的应用。我们将从理论到实践,逐步揭示JMM在多线程编程中的重要性和复杂性,帮助读者构建更加健壮的并发程序。
|
12天前
|
缓存 Java 开发者
Java多线程并发编程:同步机制与实践应用
本文深入探讨Java多线程中的同步机制,分析了多线程并发带来的数据不一致等问题,详细介绍了`synchronized`关键字、`ReentrantLock`显式锁及`ReentrantReadWriteLock`读写锁的应用,结合代码示例展示了如何有效解决竞态条件,提升程序性能与稳定性。
37 5
|
11天前
|
Java 程序员 UED
深入理解Java中的异常处理机制
本文旨在揭示Java异常处理的奥秘,从基础概念到高级应用,逐步引导读者掌握如何优雅地管理程序中的错误。我们将探讨异常类型、捕获流程,以及如何在代码中有效利用try-catch语句。通过实例分析,我们将展示异常处理在提升代码质量方面的关键作用。
24 3
|
11天前
|
Java 数据库连接 开发者
Java中的异常处理机制:深入解析与最佳实践####
本文旨在为Java开发者提供一份关于异常处理机制的全面指南,从基础概念到高级技巧,涵盖try-catch结构、自定义异常、异常链分析以及最佳实践策略。不同于传统的摘要概述,本文将以一个实际项目案例为线索,逐步揭示如何高效地管理运行时错误,提升代码的健壮性和可维护性。通过对比常见误区与优化方案,读者将获得编写更加健壮Java应用程序的实用知识。 --- ####
|
12天前
|
运维 Java 编译器
Java 异常处理:机制、策略与最佳实践
Java异常处理是确保程序稳定运行的关键。本文介绍Java异常处理的机制,包括异常类层次结构、try-catch-finally语句的使用,并探讨常见策略及最佳实践,帮助开发者有效管理错误和异常情况。
41 4