我们该如何良好的实践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日志并进行多维度分析。
目录
相关文章
|
6天前
|
XML 安全 Java
Java反射机制:解锁代码的无限可能
Java 反射(Reflection)是Java 的特征之一,它允许程序在运行时动态地访问和操作类的信息,包括类的属性、方法和构造函数。 反射机制能够使程序具备更大的灵活性和扩展性
17 5
Java反射机制:解锁代码的无限可能
|
7天前
|
存储 安全 Java
系统安全架构的深度解析与实践:Java代码实现
【11月更文挑战第1天】系统安全架构是保护信息系统免受各种威胁和攻击的关键。作为系统架构师,设计一套完善的系统安全架构不仅需要对各种安全威胁有深入理解,还需要熟练掌握各种安全技术和工具。
33 10
|
1天前
|
Java 程序员 数据库连接
Java中的异常处理:理解与实践
【10月更文挑战第29天】在Java编程的世界里,异常像是不请自来的客人,它们可能在任何时候闯入我们的程序宴会。了解如何妥善处理这些意外访客,不仅能够保持我们程序的优雅和稳健,还能确保它不会因为一个小小的失误而全盘崩溃。本文将通过浅显易懂的方式,带领读者深入异常处理的核心概念,并通过实际示例展现如何在Java代码中实现有效的异常管理策略。
|
3天前
|
Java 网络安全 Maven
Exception in thread "main" java.lang.NoSuchMethodError: okhttp3.OkHttpClient$Builder.sslSocketFactory(Ljavax/net/ssl/SSLSocketFactory;Ljavax/net/ssl/X509TrustManager;)Lokhttp3/OkHttpClient$Builder; 问题处理
【10月更文挑战第26天】Exception in thread "main" java.lang.NoSuchMethodError: okhttp3.OkHttpClient$Builder.sslSocketFactory(Ljavax/net/ssl/SSLSocketFactory;Ljavax/net/ssl/X509TrustManager;)Lokhttp3/OkHttpClient$Builder; 问题处理
27 2
|
5天前
|
存储 缓存 安全
🌟Java零基础:深入解析Java序列化机制
【10月更文挑战第20天】本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
14 3
|
5天前
|
缓存 Java 调度
Java中的多线程编程:从基础到实践
【10月更文挑战第24天】 本文旨在为读者提供一个关于Java多线程编程的全面指南。我们将从多线程的基本概念开始,逐步深入到Java中实现多线程的方法,包括继承Thread类、实现Runnable接口以及使用Executor框架。此外,我们还将探讨多线程编程中的常见问题和最佳实践,帮助读者在实际项目中更好地应用多线程技术。
11 3
|
7天前
|
监控 安全 Java
Java多线程编程的艺术与实践
【10月更文挑战第22天】 在现代软件开发中,多线程编程是一项不可或缺的技能。本文将深入探讨Java多线程编程的核心概念、常见问题以及最佳实践,帮助开发者掌握这一强大的工具。我们将从基础概念入手,逐步深入到高级主题,包括线程的创建与管理、同步机制、线程池的使用等。通过实际案例分析,本文旨在提供一种系统化的学习方法,使读者能够在实际项目中灵活运用多线程技术。
|
5天前
|
缓存 安全 Java
Java中的多线程编程:从基础到实践
【10月更文挑战第24天】 本文将深入探讨Java中的多线程编程,包括其基本原理、实现方式以及常见问题。我们将从简单的线程创建开始,逐步深入了解线程的生命周期、同步机制、并发工具类等高级主题。通过实际案例和代码示例,帮助读者掌握多线程编程的核心概念和技术,提高程序的性能和可靠性。
8 2
|
5天前
|
安全 Java UED
深入理解Java中的异常处理机制
【10月更文挑战第25天】在编程世界中,错误和意外是不可避免的。Java作为一种广泛使用的编程语言,其异常处理机制是确保程序健壮性和可靠性的关键。本文通过浅显易懂的语言和实际示例,引导读者了解Java异常处理的基本概念、分类以及如何有效地使用try-catch-finally语句来处理异常情况。我们将从一个简单的例子开始,逐步深入到异常处理的最佳实践,旨在帮助初学者和有经验的开发者更好地掌握这一重要技能。
13 2
|
6天前
|
Java
Java中的多线程编程:从基础到实践
本文深入探讨Java多线程编程,首先介绍多线程的基本概念和重要性,接着详细讲解如何在Java中创建和管理线程,最后通过实例演示多线程的实际应用。文章旨在帮助读者理解多线程的核心原理,掌握基本的多线程操作,并能够在实际项目中灵活运用多线程技术。