Java 入门指南:异常处理的实践规范

news2024/11/13 15:21:19

在 Java 中处理异常并不是一个简单的事情。需要花费很多时间来思考如何处理异常,包括需要处理哪些异常,怎样处理等等。

抛出或捕获异常的时候,有很多不同的情况需要考虑,而且大部分事情都是为了改善代码的可读性或者 API 的可用性。

异常不仅仅是一个错误控制机制,也是一个通信媒介。因此,为了和同事更好的合作,一个团队必须要制定出一个最佳实践和规则,只有这样,团队成员才能理解这些通用概念,同时在工作中使用它。

异常处理 - 阿里巴巴Java开发手册 (oschina.io)

只针对不正常的情况才使用异常

异常只应该被用于不正常的条件,它们永远不应该被用于正常的控制流。 Java 类库中定义的可以通过预检查方式规避的 RuntimeException 异常不应该通过 catch 的方式来处理,比如:NullPointerExceptionIndexOutOfBoundsException 等等。

比如,在解析字符串形式的数字时,可能存在数字格式错误,不应该通过 catch Exception 来实现

  • 异常机制的设计初衷是用于不正常的情况,所以很少会有JVM实现试图对它们的性能进行优化。所以,创建、抛出和捕获异常的开销是很昂贵的

  • 把代码放在 try-catch 中返回阻止了 JVM 实现本来可能要执行的某些特定的优化。

  • 对数组进行遍历的标准模式并不会导致冗余的检查,有些现代的 JVM 实现会将它们优化掉。

在 finally 块中清理资源或者使用 try-with-resource 语句

使用类似 InputStream 这种需要使用后关闭的资源时,需要在 finally 语句块中手动 .close 掉资源;或是在实现了 AutoClosable/Closable 接口的资源中使用 try-with-source 语法自动关闭资源。

而不是将 close 方法写在 try 语句块中,若其中发生了异常,异常之后的语句就不会执行,可能导致资源没有关闭。

尽量使用标准的异常

代码重用是值得提倡的,这是一条通用规则,异常也不例外。

在编写代码时,可以 利用现有的异常类来匹配某些特定的错误情况,而不是每次都创建新的自定义异常类

重用现有的异常有几个好处:

  • 通过重用现有的异常类,可以减少不必要的代码量,使代码更加简洁清晰。

  • 对于用到这些 API 的程序而言,使用现有的异常类可以让代码在出现相似错误时具有统一的异常处理逻辑,提高代码的可维护性和可读性。

  • 异常类越少,意味着内存占用越小,并且转载这些类的时间开销也越小。

  • 使用已有异常类可以传达更准确的错误信息,以及与特定错误类型相关的上下文信息。

Java标准异常中有几个是经常被使用的异常。如下表格:

异常使用场合
IllegalArgumentException参数的值不合适
IllegalStateException参数的状态不合适
NullPointerException在null被禁止的情况下参数值为null
IndexOutOfBoundsException下标越界
ConcurrentModificationException在禁止并发修改的情况下,对象检测到并发修改
UnsupportedOperationException对象不支持客户请求的方法

虽然它们是 Java 平台库迄今为止最常被重用的异常,但是,在许可的条件下,其它的异常也可以被重用。

例如,如果要实现诸如复数或者矩阵之类的算术对象,那么重用ArithmeticExceptionNumberFormatException 将是非常合适的。

如果一个异常满足需要,可以直接使用,但一定要确保抛出异常的条件与该异常的文档中描述的条件一致。这种重用必须建立在语义的基础上,而不是名字的基础上

选择重用哪一种异常并没有必须遵循的规则

例如,考虑纸牌对象的情形,假设有一个用于发牌操作的方法,它的参数(handSize)是发一手牌的纸牌张数。

假设调用者在这个参数中传递的值大于整副牌的剩余张数。那么这种情形既可以被解释为 IllegalArgumentException(handSize的值太大),也可以被解释为IllegalStateException(相对客户的请求而言,纸牌对象的纸牌太少)。

也可以通过继承已有异常类,可以利用已有异常的属性和方法,创建自定义异常类,并扩展自现有的异常类

对异常进行文档说明

当在方法上声明抛出异常时,也需要进行文档说明。目的是为了给调用者提供尽可能多的信息,从而可以更好地避免或处理异常。

在 添加 @throws 声明,并且描述抛出异常的场景:

/*Method description
  @throws MyBusinessException - businuess exception    
  description
*/
public void doSomething(String input) throws MyBusinessException {
   // ...
}

在抛出 MyBusinessException 异常时,需要尽可能精确地描述问题和相关信息,这样无论是打印到日志中还是在监控工具中,都能够更容易被人阅读,从而可以更好地定位具体错误信息、错误的严重程度等。

优先捕获最具体的异常

大多数 IDE 都可以帮助我们实现这个最佳实践。当尝试首先捕获较不具体的异常时,它们会报告无法访问的代码块。

匹配异常的第一个 catch 块会被执行。例如:如果首先捕获 IllegalArgumentException ,则永远不会到达应该处理更具体的 NumberFormatException 的 catch 块,因为它是 IllegalArgumentException 的子类。

应该优先捕获最具体的异常类,并将不太具体的 catch 块添加到列表的末尾。 第一个 catch 块处理所有 NumberFormatException 异常,第二个处理所有非 NumberFormatException 异常的 IllegalArgumentException 异常:

 try {
        doSomething("A message");
    } catch (NumberFormatException e) {
        log.error(e);
    } catch (IllegalArgumentException e) {
        log.error(e)
    }

不要捕获 Throwable 类

Throwable 是所有异常和错误的超类。可以在 catch 子句中使用它,但是永远不应该这样做!

如果在 catch 子句中使用 Throwable ,它不仅会捕获所有异常,也将捕获所有的错误。JVM 抛出错误,指出不应该由应用程序处理的严重问题。

典型的例子是 OutOfMemoryError 或者 StackOverflowError
两者都是由应用程序控制之外的情况引起的,无法处理。

不要忽略异常

很多时候,开发者很有自信不会抛出异常,因此写了一个 catch 块,但是没有做任何处理或者记录日志。

现实中经常会出现无法预料的异常,或者无法确定这里的代码未来是不是会改动(删除了阻止异常抛出的代码),而此时由于异常被捕获,使得无法拿到足够的错误信息来定位问题。

合理的做法是至少要记录异常的信息:

try {
        // do something
    } catch (NumberFormatException e) {
        log.error("This should never happen: " + e); 
    }

不要记录并抛出异常

很多代码甚至类库中都会有捕获异常、记录日志并再次抛出的逻辑:

try {
    new Long("xyz");
} catch (NumberFormatException e) {
    log.error(e);
    throw e;
}

这个处理逻辑看着是合理的。但这经常会给同一个异常输出多条日志:

![[dont log and throw exception.png]]

要提供更加有用的信息,那么可以将异常包装为自定义异常。仅当想要处理异常时才去捕获,否则只需要在方法签名中声明让调用者去处理。

包装异常时不要抛弃原始的异常

捕获标准异常并包装为自定义异常是一个很常见的做法。这样可以添加更为具体的异常信息并能够做针对的异常处理。

这样做时,要确保将原始异常设置为原因(使用 链式异常 中的 initCause (Throwable Cause)方法)否则,将会丢失堆栈跟踪和原始异常的消息,这将会使分析导致异常的异常事件变得困难。

不要使用异常控制程序的流程

异常不要用来做流程控制,条件控制,因为异常的处理效率比条件分支低

例如,本应该使用if语句进行条件判断的情况下,却使用异常处理,这会严重影响应用的性能。

不要在finally块中使用return。

try 块中的 return 语句执行成功后,并不马上返回,而是继续执行 finally 块中的语句,如果此处存在 return 语句,则在此直接返回,会丢弃掉 try 块中的返回点。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2075230.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

捏蛋糕修牛蹄类型的解压视频素材去哪里找?

今天我们聊聊在哪里能找到制作捏蛋糕、修牛蹄等解压视频的素材。这类视频看起来心情就变好,特别解压。如果你也有兴趣制作这种视频,以下是一些优质的素材网站推荐,助你轻松找到所需素材。 蛙学网 开始我们的推荐列表是蛙学网。这是一个综合性…

npm国内源设置

一、背景 在国内使用npm时,由于网络问题,经常会遇到速度慢或无法访问的问题。为了提高效率,可以将npm的源设置为国内的镜像源。以下是一些常用的国内npm镜像源以及如何设置它们的方法。 二、国内可用源 2.1 淘宝npm源 https://registry.np…

SOLIDWORKS 2025全新功能解读:界面优化

准备好在SOLIDWORKS 2025中探索了吗?新版本,可帮助您简化和加速从概念到制造的产品开发流程,鑫辰科技带您抢先体验SOLIDWORKS 2025的亮点,深入了解新版本所增添的独特功能。 一:指定 Z-向上模板 在早期版本中,SOLID…

手算神经网络MAC和FLOP

在本文中,我们将深入探讨神经网络背景下的 MAC(乘法累加运算)和 FLOP(浮点运算)概念。通过学习如何使用笔和纸手动计算这些内容,你将对各种网络结构的计算复杂性和效率有基本的了解。 这是 colab 笔记本中…

使用 Python 和 SQL 自动将 ETL 传输到 SFTP 服务器

了解如何在 Windows 上自动执行从 PostgreSQL 数据库到远程服务器的日常数据传输过程 欢迎来到雲闪世界。将文件从一个位置传输到另一个位置的过程显然是自动化的完美选择。重复执行这项工作可能令人望而生畏,尤其是当您必须对几组数据执行整个 ETL(提取…

神经网络模型剪枝快速指南

模型剪枝(Model Pruning)是指从深度学习神经网络模型中删除不重要的参数,以减小模型大小并实现更高效的模型推理。通常,只剪枝参数的权重,而不影响偏差。偏差的剪枝往往有更明显的缺点。 非结构化剪枝期间权重如何归零…

书生.浦江大模型实战训练营——(十)Lagent 自定义你的 Agent 智能体

最近在学习书生.浦江大模型实战训练营,所有课程都免费,以关卡的形式学习,也比较有意思,提供免费的算力实战,真的很不错(无广)!欢迎大家一起学习,打开LLM探索大门&#xf…

【9月持续更新】国内ChatGPT-4中文镜像网站整理~

以前我也是通过官网使用,但是经常被封号,就非常不方便,后来有朋友推荐国内工具,用了一阵之后,发现:稳定方便,用着也挺好的。 最新的 GPT-4o、4o mini,可搭配使用~ 1、 最新模型科普&…

遗传算法整合talib技术分析算子做因子挖掘,比如ADX, 阿隆指标等

“ 原创内容第631篇,专注量化投资、个人成长与财富自由” 七年实现财富自由 七年,经过十万小时刻意练习,足矣在任何领域成为专家。 七年,成为自己的财富管理专家。 七年,实现财富自由。 1512篇原创内容 公众号 星球…

怎样恢复微信聊天记录?4个巧妙方法,速来学习!

微信不仅是我们的通讯工具,更是情感的载体,每一句“早安”与“晚安”都藏着不为人知的温柔。但有时候这些珍贵的聊天记录却会离家出走。怎么恢复微信聊天记录?就成为我们需要解答的难题。 别担心,今天,小编我将化身为…

PostgresSQL--基于Kubernetes部署PostgresSQL

基于docker 拉取镜像,这个镜像是我自己的阿里云镜像,拉取的国外的镜像。 docker pull registry.cn-hangzhou.aliyuncs.com/qiluo-images/postgres:latest创建 dolphinscheduler 命名空间,本文命名空间是使用的dolphinscheduler 使用 kubectl…

基于元神操作系统编写(FPU)数学计算程序

1. 背景 数学计算已经成为计算机的主要工作之一,尤其是实数运算,在人工智能时代更是普遍存在,神经网络中的绝大部分参数都用的实数。 2. 方法 (1)FPU运算 计算机中的实数运算是通过数学协处理器FPU完成的&#xff…

黑神话悟空配置要求:CPU/内存/显卡/存储和系统最低限制

玩《黑神话:悟空》对电脑配置有什么要求?至少需要i5处理器、16G内存、GTX 1060显卡、130G空闲磁盘空间,没有高配电脑怎么办?码笔记整理详细配置如下: CPU处理器:64位处理器,CPU选择Intel Core …

数据防泄密知识集锦丨八个实用数据防泄露软件,你知道吗

数据已成为企业的核心资产。 然而,随着网络威胁的日益严峻,数据泄露事件频发,给企业带来了巨大的经济损失和声誉风险。 为了有效保护企业数据的安全性和保密性,各种数据防泄露软件应运而生。 本文将为您介绍八个实用的数据防泄露…

ROS机器人专用云台相机防抖摄像头

【告别模糊】机器人专用摄像头,为您的视觉算法保驾护航 产品概述 Autolabor C1专为机器人设计的高性能摄像头,即使在没有减震装置或不平坦的路面上,也能提供清晰稳定的图像。它拥有先进的主动式机械防抖和数字ISP防抖技术,图像效…

基于太阳能供电的水情监测站设计(论文+源码+图纸)

1.总体方案设计 根据水情监测站系统的实际应用需求,从硬件电路以及软件程序两个方面展开系统设计。按照系统设计功能以及功能选型的结果,制定了如图2.11所示的系统总体框图。系统采用STM32单片机作为控制器,在传感器检测模块中包括的DS18B20…

netty编程之使用ChannelOutboundHandler对write出去的消息做不同处理

写在前面 源码 。 在进行网络编程的时候,不可避免的需要对write出去的消息做一些处理,比如脱敏,增加统一数据等。而netty提供了ChannelOutboundHandler来允许我们拦截消息从而可以对消息进行处理。对应的接口是io.netty.channel.ChannelHand…

Python:win10下一种不用编译,直接下载二进制依赖的方法

python依赖的安装,在win环境下, 有些包还是比较麻烦, 经常编译失败, 我曾发帖讨论过多次,有帖为证!点此进入! https://blog.csdn.net/weixin_62598385/article/details/135945383 win下的Pyth…

基于vue.js和node.js的酒坊销售网站的设计与实现---附源码98047

目 录 摘要 1 绪论 1.1研究背景与意义 1.3研究内容 1.4论文结构与章节安排 2 酒坊销售网站分析 2.1 可行性分析 2.2系统流程分析 2.2.1 数据增加流程 2.2.2 数据修改流程 2.2.3 数据删除流程 2.3 系统功能分析 2.3.1 功能性分析 2.3.2 非功能性分析 2.4 系统用例…

实战分享:利用两大在线平台实现自动化数据采集的技巧

本文将深入探讨如何运用两大主流在线平台,通过实战案例分享,揭示自动化数据采集的高效技巧。无需编程基础,也能快速掌握跨平台数据抓取秘籍,助力企业和个人提升市场竞争力与决策效率。 正文 在大数据时代背景下,信息…