Java的checked exception有意义吗?

2023-09-18 23:36:11

1 前言

这种异常必须在编译前就try/catch,又不一定会抛异常,小项目中不明显,大项目中,会造成不必要代码臃肿和可读性降低,完全可在编译出错时,通过单元测试和调试,得到正确代码。这设计还有啥意义?

Checked Exception初衷很好,但事实上是没啥卵用设计。

2 初衷很好

因为我们都知软件会有各种问题,严谨处理这些问题会很好提高健壮性。Checked Exception就是让一个方法指定自己一定会抛啥异常,调用者须决定一定要处理(catch),或明确声明继续向上抛(throws)。整个程序对异常处理就很明确,程序员也有章可循,UT,测试也能明确该处理啥错误。

3 现实骨感

若说较接近底层的系统还能相对设计出较完备严谨的异常体系,业务系统做这个严重吃力不讨好。

业务系统中,一个典型业务接口,有一个正常处理结果,但可能却有几十个不正常case。而Checked Exception要求须每层调用,要不挨个处理,要不挨个声明往上抛。

挨个处理

不现实,因为:

  • 大部分情况下,大多数异常都是极端情况,几乎不会出现
  • 即使出现,影响也不大

如一个基金详情,底层可能调用十几个不同基金信息数据源(基本信息,经理,基金公司,费率,净值,重仓股)。如因为意外少个1、2个,最好软处理。如须使用Checked Exception,很多人会这么干:

try {
  Fund fund = getFund(fundCode);
} catch (SomeNotImportantButCheckedException e) {
  // do nothing
}

用IDE自动产生catch block但就不处理。或干脆:

try {
  Fund fund = getFund(fundCode);
} catch (Exception e) {
  // do nothing
}

这比不catch,自动往上抛更垃圾。所以一般来讲,业务系统都会有收底的错误处理,这个处理可能在业务系统最高层。大部分不需要认真处理的异常往上抛。当真意识到某问题值得仔细处理,可能才专门为它仔细设计Exception和处理。

挨个明确[throws]也不现实。一般看throws后边能挂3~4种Exception。按上面假设,可能要搞几十种Exception,且随层级提高,数量越堆越多。Java这一般建议用类的体系来组织这些Exception,然后throws一个合适基类。但设计一个比较好的类体系很难。更何况大多数异常都不重要,直接收底处理的。

为不重要的事耗精力不值当,于是基本都简化处理,直接用 Exception、 RuntimeException (连throws也不用)或自己封装一个“BizException”包装错误码和相关信息。

4 业务比较精神上头呢?

上面这些还都是在设计时可定义所有异常的情况下遇到的问题。但业务剧烈变化时,不可能初始就预见所有可能问题。强行加Checked Exception对业务系统的接口,是不向前兼容的。

一旦真加了,所有调用方须被逼跟着改:

  • 一个系统内部,一个code base(代码库)可能还好处理
  • 对可能跨系统调用(如你写个jar,别人maven依赖),就可能灾难性。如某些组件因种种原因无法升级,就不能使用新代码!

因此,一个好的错误处理体系,最好满足:

  • 不会倒逼程序挨个处理无聊异常,允许程序员有选择将关注点放在哪些最关键问题
  • 如团队真要严谨错误处理,可提供一个有力支持,但这支持是团队根据开发的内容来决定使用,而非强制所有语言的使用者都遵循同一套“规则”
  • 允许错误处理渐进性的发展

5 Java异常最佳实践

5.1 基建团队

使用Checked Exception,并定义良好的异常继承体系,认真处理所有异常。有限度使用RuntimeException

5.2 业务团队

基于RuntimeException定义一个BizException描述各种业务问题,BizExcpetion包含错误码和错误描述信息;同时定义InteralServerError包装各种系统错误,如网络超时。输出http response时,二者输出不同,然后定义不同监控和告警机制

5.3 特定异常

由产研共同商讨设计:

  • 哪些可完全不管(如一个不关键数据拿不到),软处理
  • 哪些要前端用户知晓并处理(如登录时用户名或密码错误)
  • 哪些由程序尽量自己处理(如关注的某产品超时,后端要尝试重试几次)

6 其他语言处理异常

6.1 go

用err(大致等价错误码,但可包含一些数据信息),因此异常可【不捕获而往上抛】的好处就得不到。这可解释为啥很多搞底层开发的不觉go没Exception而难用,因为反正错误都要严谨处理,Exception那点优势不重要。但即便如此,go也不强制调用者必须处理写if err != nil 。

但给go加Exception的呼声越来越强烈,应该是 Java 转 Go 的业务开发越来越多。

6.2 js

也有Error,但不是很喜欢搞继承。因此javascript一般就只用“Error”,“TypeError”这种简单东西。因为动态语言,开发者可选择自己往Error里塞自己喜欢的东西,并用一些松散的约定解决问题。如:

throw Error("ERR_INVALID_PASSWORD");

简单的用字符串来定义错误。js主要场景在前端,这时出错:

  • 要不给用户展示错误信息

  • 要不用错误上报接口报给后端

复杂的异常体系也没用,更谈不上Checked exception。而服务端的NodeJS exception处理就能借鉴很多Java语法。

6.3 kotlin

直接砍掉Checked Exception,但保留其他常规异常语法(改成Expression,用起来爽很多)。文档提供两个论据:

6.4 swift

更有趣,认为[函数]的[异常模式]有两种:

  • 会抛出异常的,于是函数名后边要声明“throws”,但是不需要声明会抛啥异常
  • 肯定不会抛出异常的,所以实现中必须吃掉各种可能发生异常的情况

[编译器]会强制确保这个语义的正确。throws这种方式,大概等价于Java中直接throws Exception。

因此从工程角度和语言发展角度来讲,Checked Exception早已经被扔进了垃圾堆。在整个工程项目的错误处理体系里,它的作用已经越来越少。新的语言纷纷抛弃掉这个华而不实的设定。希望广大入场者只要知道Checked Exception是什么就好,实战时还是根据业务场景来设计错误处理。

7 结尾

也许还有人觉得Checked Exception是一种可以推进减少程序错误,提高健壮性的好措施。错的是懒惰的,不称职的程序员,而不是Checked Exception。但从我认为,如果一个措施不能有助于解决问题,反而加重问题,那就是无用的。不要把时间和精力浪费在无用的事物。

更多推荐

02-HTML常用标签

02-HTML常用标签2.1标签的构成标签由<、>、/、英文单词或字母组成。并且把标签中<>包括起来的英文单词或字母称为标签名常见标签由两部分组成,我们称之为:双标签。前部分叫开始标签,后部分叫结束标签,两部分之间包裹内容就是标签名。少数标签由一部分组成,我们称之为:单标签。自成一体,无法包裹内容(比如:<hr>、<b

Git 基本操作【本地仓库与远程仓库的推送、克隆和拉取】

文章目录一、Git简介二、Git的下载安装三、Git常规命令四、新建本地仓库五、本地分支操作六、Git远程仓库七、远程仓库克隆、抓取和拉取八、总结九、学习交流一、Git简介Git是分布式版本控制系统(DistributedVersionControlSystem,简称DVCS),分为两种类型的仓库:本地仓库和远程仓库工

51单片机1【单片机到底是什么】

1.从电路到集成电路1.电路发展变化的趋势(1)功率:电子设备·越来越省电,待机时间越来越长,工作电压越来越低。(2)体积:体积越来越小(3)功能:功能越来越强大2.微器件(1)电路的核心:开关控制,倍率控制(2)电子管,晶体管3.集成电路(IC,芯片)的出现(1)IC(integratedcircuit,集成电路),

[创业之路-76] - 创业公司如何在长期坚持中顺势而为?诚迈科技参观交流有感

目录一、创业环境1.1.VUCA乌卡时代:易变、复杂、不确定性、模糊的时代1.2.中国用了四十年的时间完成了三次工业革命:机械化、电气化、数字化1.3.中国正在经历着第四次工业革命:智能化、生态化、拟人化1.4国产替代:国产化1.5所有的赛道都挤满了人二、创业公司在长期坚持中顺势而为的路径2.1做自己的熟悉行业:对行业

annyang语音识别与语音合成库

*text是Annyang.js中的一个通配符,表示匹配任何语音输入中的文本,并将其作为参数传递给命令回调函数。例如,如果用户说“searchforcats”,则可以使用以下命令来捕获输入中的搜索词:constcommands={'searchfor*text':(text)=>{console.log('Search

Selenium和Requests搭配使用

Selenium和Requests搭配使用前要1.CDP2.通过requests控制浏览器2.1代码一2.2代码23.通过selenium获取cookie,requests携带cookie请求前要之前有提过,用selenium控制本地浏览器,提高拟人化,但是效率比较低,今天说一种selenium和requests搭配使

人类的生成式与机器的生成式

生成式是指一种基于模型的方法,通过给定的条件或输入,生成与之相符合的输出。在自然语言处理领域中,生成式模型通常用于生成文本、文章、对话等自然语言序列。生成式模型的训练主要涉及两个步骤:学习和生成。在学习阶段,模型通过对大量的训练数据进行学习和训练,以学习出模式、语法和语义等语言特征。在生成阶段,模型接受一个初始输入,并

【Redis】Redis的特性和应用场景 · 数据类型 · 持久化 · 数据淘汰 · 事务 · 多机部署

【Redis】Redis常见面试题(3)文章目录【Redis】Redis常见面试题(3)1.特性&应用场景1.1Redis能实现什么功能1.2Redis支持分布式的原理1.3为什么Redis这么快1.4Redis实现分布式锁1.5Redis作为缓存2.数据类型2.1Redis常用的数据类型有哪些2.2有序列表的底层是如

java 入门-使用eclipse、javaFX、SceneBuilder进行图形界面开发

个人是一直在开C#CS端开发,目前公司的软件基本都使用了java作开发。为了更好适应环境,我也只能再次学习这个陌生的开发工具。java的开发界面非常不友好,对于我这样的初学者只能是借助插件来进行界面与后台联动,上网度娘了好多资料,java的开发环境非常不易构建特别是需要辅助界面设计的。谨以我这么多天的摸索经历来作下小记

高通recovery流程分析(编译、界面、图片)

目录recovery界面菜单recovery界面操作recovery启动流程recovery编译makefilerecovery图片大小ramdisk、boot.img、recovery.img之间的关系authordaisy.skye的博客_CSDN博客-嵌入式,Qt,Linux领域博主recovery界面菜单rec

【QT开发笔记-基础篇】| 第四章 事件QEvent | 4.2 完成整体布局

本章要实现的整体效果如下:在讲解实际的事件之前,本节先把整体布局搭建好。布局整体包括左侧的导航和右侧的主窗体1.新建工程新建一个窗口类MainWidget,继承自QWidget,并且取消“Generateform”复选框也就是不使用UI设计师界面拖拽控件,而是纯代码来实现界面。最终新建工程如下:此时,直接运行是一个空白

热文推荐