编程语言的四种错误处理方法,你知道几种?
错误处理是编程的一个基本要素。除非你写的是“hello world”,否则就必须处理代码中的错误。在本文中,我将讨论各种编程语言在处理错误时使用的最常见的四种方法,并分析它们的优缺点。
关注不同设计方案的语法、代码可读性、演变过程、运行效率,将有助于我们写出更为优雅和健壮的代码。
返回错误代码
这是最古老的策略之一——如果一个函数可能会出错,它可以简单地返回一个错误代码——通常是负数或者null。例如,C 语言中经常使用:
FILE* fp = fopen("file.txt" , "w");if (!fp) {// 发生了错误}
这种方法非常简单,既易于实现,也易于理解。它的执行效率也非常高,因为它只需要进行标准的函数调用,并返回一个值,不需要有运行时支持或分配内存。但是,它也有一些缺点:
用户很容易忘记处理函数的错误。例如,在 C 中,printf 可能会出错,但我几乎没有见过程序检查它的返回值!
如果代码必须处理多个不同的错误(打开文件,写入文件,从另一个文件读取等),那么传递错误到调用堆栈会很麻烦。
除非你的编程语言支持多个返回值,否则如果必须返回一个有效值或一个错误,就很麻烦。这导致 C 和 C++ 中的许多函数必须通过指针来传递存储了“成功”返回值的地址空间,再由函数填充,类似于:
my_struct *success_result;int error_code = my_function(&success_result);if (!error_code) {// can use success_result}
众所周知,Go 选择了这种方法来处理错误,而且,由于它允许一个函数返回多个值,因此这种模式变得更加人性化,并且非常常见:
user, err = FindUser(username)if err != nil {return err}
Go 采用的方式简单而有效,会将错误传递到调用方。但是,我觉得它会造成很多重复,而且影响到了实际的业务逻辑。不过,我写的 Go 还不够多,不知道这种印象以后会不会改观!
异常
异常可能是最常用的错误处理模式。try/catch/finally 方法相当有效,而且使用简单。异常在上世纪 90 年代到 2000 年间非常流行,被许多语言所采用(例如 Java、C# 和 Python)。
与错误处理相比,异常具有以下优点:
它们自然地区分了“快乐路径”和错误处理路径
它们会自动从调用堆栈中冒泡出来
你不会忘记处理错误!
然而,它们也有一些缺点:需要一些特定的运行时支持,通常会带来相当大的性能开销。
此外,更重要的是,它们具有“深远”的影响——某些代码可能会抛出异常,但被调用堆栈中非常远的异常处理程序捕获,这会影响代码的可读性。
此外,仅凭查看函数的签名,无法确定它是否会抛出异常。
C++ 试图通过throws 关键字来解决这个问题,但它很少被使用,因此在 C++ 17 中已被弃用 ,并在 C++ 20 中被删除。此后,它一直试图引入noexcept 关键字,但我较少写现代 C++,不知道它的流行程度。
(注:throws 关键字很少使用,因为使用过于繁琐,需要在函数签名中指定抛出的异常类型,并且这种方法不能处理运行时发生的异常,有因为“未知异常”而导致程序退出的风险)
Java 曾试图使用“受检的异常(checked exceptions)”,即你必须将异常声明为函数签名的一部分——但是这种方法被认为是失败的,因此像 Spring 这种现代框架只使用“运行时异常”,而有些 JVM 语言(如 Kotlin)则完全抛弃了这个概念。这造成的结果是,你根本无法确定一个函数是否会抛出什么异常,最终只得到了一片混乱。
(注:Spring 不使用“受检的异常”,因为这需要在函数签名及调用函数中显式处理,会使得代码过于冗长而且造成不必要的耦合。使用“运行时异常”,代码间的依赖性降低了,也便于重构,但也造成了“异常源头”的混乱)
回调函数
另一种方法是在 JavaScript 领域非常常见的方法——使用回调,回调函数会在一个函数成功或失败时调用。这通常会与异步编程结合使用,其中 I/O 操作在后台进行,不会阻塞执行流。
例如,Node.JS 的 I/O 函数通常加上一个回调函数,后者使用两个参数(error,result),例如:
const fs = require('fs');fs.readFile('some_file.txt', (err, result) => {if (err) {console.error(err);return;}console.log(result);});
但是,这种方法经常会导致所谓的“回调地狱”问题,因为一个回调可能需要调用其它的异步 I/O,这可能又需要更多的回调,最终导致混乱且难以跟踪的代码。
现代的 JavaScript 版本试图通过引入promise 来提升代码的可读性:
fetch("https://example.com/profile", {method: "POST", // or 'PUT'}).then(response => response.json()).then(data => data['some_key']).catch(error => console.error("Error:", error));
promise 模式并不是最终方案,JavaScript 最后采用了由 C#推广开的 async/await 模式,它使异步 I/O 看起来非常像带有经典异常的同步代码:
async function fetchData() {try {const response = await fetch("my-url");if (!response.ok) {throw new Error("Network response was not OK");}return response.json()['some_property'];} catch (error) {console.error("There has been a problem with your fetch operation:", error);}}
使用回调进行错误处理是一种值得了解的重要模式,不仅仅在 JavaScript 中如此,人们在 C 语言中也使用了很多年。但是,它现在已经不太常见了,你很可能会用的是某种形式的async/await。
函数式语言的 Result
我最后想要讨论的一种模式起源于函数式语言,比如 Haskell,但是由于 Rust 的流行,它已经变得非常主流了。
它的创意是提供一个Result类型,例如:
enum Result<S, E> {Ok(S),Err(E)}
这是一个具有两种结果的类型,一种表示成功,另一种表示失败。返回结果的函数要么返回一个Ok 对象(可能包含有一些数据),要么返回一个Err 对象(包含一些错误详情)。函数的调用者通常会使用模式匹配来处理这两种情况。
为了在调用堆栈中抛出错误,通常会编写如下的代码:
let result = match my_fallible_function() {Err(e) => return Err(e),Ok(some_data) => some_data,};
由于这种模式非常常见,Rust 专门引入了一个操作符(即问号 ?) 来简化上面的代码:
let result = my_fallible_function()?; // 注意有个"?"号
这种方法的优点是它使错误处理既明显又类型安全,因为编译器会确保处理每个可能的结果。
在支持这种模式的编程语言中,Result 通常是一个 monad,它允许将可能失败的函数组合起来,而无需使用 try/catch 块或嵌套的 if 语句。
(注:函数式编程认为函数的输入和输出应该是纯粹的,不应该有任何副作用或状态变化。monad 是一个函数式编程的概念,它通过隔离副作用和状态来提高代码的可读性和可维护性,并允许组合多个操作来构建更复杂的操作)
根据你使用的编程语言和项目,你可能主要或仅仅使用其中一种错误处理的模式。
不过,我最喜欢的还是 Result 模式。当然,不仅是函数式语言采用了它,例如,在lastminute.com 中,我们在 Kotlin 中使用了 Arrow 库,它包含一个受 Haskell 强烈影响的类型Either。
--END--
相关文章:
编程语言的四种错误处理方法,你知道几种?
错误处理是编程的一个基本要素。除非你写的是“hello world”,否则就必须处理代码中的错误。在本文中,我将讨论各种编程语言在处理错误时使用的最常见的四种方法,并分析它们的优缺点。 关注不同设计方案的语法、代码可读性、演变过程、运行效…...
ContOS7单机安装Hadoop
安装Hadoop 1,准备环节 因为Hadoop是由java编写的,所以需要Java的环境支持,作为开发者我们需要安装jdk。 安装jdk的教程http://t.csdn.cn/6qJKg 下载Hadoop的安装包 Hadoop官网:http://hadoop.apache.org/ Hadoop版本下载地…...
抓取动态网页的数据的具体操作方法
抓取动态网页的数据的具体操作方法 动态网页是指在用户交互过程中,网页内容不断更新和变化的网页。抓取动态网页的数据需要了解以下具体操作方法: 使用浏览器开发者工具:在浏览器中打开目标网页后,按下F12键,打开开发…...
Windows 和 Linux 环境下 ProtoBuf 的安装
文章目录 一、ProtoBuf 在 Windows 环境中的安装二、ProtoBuf 在 Linux 环境中的安装 ProtoBuf在GitHub上的下载地址 一、ProtoBuf 在 Windows 环境中的安装 首先选择自己要下载的版本,我选择的是v21.11: 点进去在最下面选择Windows的版本࿰…...
商用密码应用安全性测评方案编制流程
密评方案编制的目标是完成测评准备活动中获取的信息系统相关资料整理,为现场测评活动提供最基本的文档和指导方案。 按照《GM-T 0116-2021 信息系统密码应用测评过程指南》标准,密评方案编制包括5项关键任务,简要汇总如下表。 编号任务输入文…...
Elasticsearch 集群部署插件管理及副本分片概念介绍
Elasticsearch 集群配置版本均为8以上 安装前准备 CPU 2C 内存4G或更多 操作系统: Ubuntu20.04,Ubuntu18.04,Rocky8.X,Centos 7.X 操作系统盘50G 主机名设置规则为nodeX.qingtong.org 生产环境建议准备单独的数据磁盘主机名 #各自服务器配置自己的主机名 hostnamectl set-ho…...
Liunx 套接字编程(2)TCP接口通信程序
1.TCP通信程序的编写 面向连接、可靠传输、提供字节流传输服务 客户端向服务器发送一个连接建立的请求流程,上图中服务端第三步详细流程 2.TCP接口 socket--创建套接字 int socket(int domain, int type, int protocol); bind---绑定 intbind(int sockfd, struct s…...
8年开发经验,浅谈 API 管理
随着信息化飞速增长的还有各信息系统中的应用接口(API),API作为信息系统内部及不同信息系统之间进行数据传输的渠道,其数量随着软件系统的不断庞大而呈指数型增长,如何管理这些API已经在业界变得越来越重要,…...
【软考备战·四月模考】希赛网四月模考软件设计师上午题
文章目录 一、成绩报告二、错题总结第一题第二题第三题第四题第五题第六题第七题第八题第九题第十题第十一题第十二题第十三题第十四题第十五题第十六题第十七题第十八题第十九题第二十题第二十一题第二十二题 三、知识查缺 题目及解析来源:2023上半年软考-模考大赛…...
MySQL中的@i:=@i+1用法详解
在MySQL中,i:i1是一个非常有用的表达式,用于在查询中生成一个递增的序列号。它可以帮助我们对结果进行编号,或者在需要连续的数字序列时提供便利。 我们先来了解一下MySQL中的用户变量。用户变量是一个用户定义的变量,其以开头。…...
web安全第一天 ,域名,dns
第一天 什么是域名?域名就是网络地址 在hhtp之后的就是域名 域名在哪里注册呢 国内注册商有很多,在网络上搜索一下阿里云万网就可以注册 什么是二级域名和多级域名 域名通常都是www.开头 ,而www.被称为顶级域名,在搜索的时候…...
【Linux】Linux编辑神器vim的使用
目录 一、Vim的基本概念 二、Vim的基本操作 1、进入vim 2、正常模式切换至插入模式 3、插入模式切换至正常模式 4、正常模式切换至底行模式 5、退出Vim编辑器 三、Vim正常模式命令集 1、移动光标 2、删除文字 3、复制 4、替换 5、撤销 四、Vim底行模式命令集 1、列出行号 2、光…...
vulnhub渗透测试靶场练习1
靶场介绍 靶场名:Medium_socialnetwork 下载地址:https://www.vulnhub.com/entry/boredhackerblog-social-network,454/ 环境搭建 靶机建议选择VM VirtualBox,我一开始尝试使用VMware时会报错,所以改用VM VirtualBox,攻击机使用…...
Uart,RS232,RS485串口通讯协议学习
目录 定义 UART(通常被称为串口,简单意味着使用广泛,具有普适性) RS232 RS232电平转换 RS485 -Recommended Standard (再推荐标准) 485和232的对比 RS485组网 总结 定义 串口是我们都很熟悉的,尤其是需要串口调试的时候,打印信息插…...
UML中的assembly关系
UML中的assembly关系 1.什么是Assembly关系 在UML(统一建模语言)中,"assembly"(组装)是一种表示组件之间关系的关联关系。组件是系统中可替换和独立的模块,可以通过组装来构建更大的系统。 当一…...
[Python]缓存cachetools与TTLCache简介
文章目录 cachetools缓存策略缓存操作 TTLCache cachetools是一个Python第三方库,提供了多种缓存算法的实现。 cachetools 使用前需要先安装pip install cachetools,说明文档参见https://cachetools.readthedocs.io/en/latest/。 cachetools提供了五种…...
现在的00后,真是卷死了呀,辞职信已经写好了·····
都说00后躺平了,但是有一说一,该卷的还是卷。这不,三月份春招我们公司来了个00后,工作没两年,跳槽到我们公司起薪23K,都快接近我了。 后来才知道人家是个卷王,从早干到晚就差搬张床到工位睡觉了…...
【wpf】列表类,用相对源时,如何绑定到子项
前言 在之前的一篇文章 :《【wpf】深度解析,Binding是如何寻找数据源的》https://blog.csdn.net/songhuangong123/article/details/126195727#:~:text%E3%80%90wpf%E3%80%91%E6%B7%B1%E5%BA%A6%E8%A7%A3%E6%9E%90%EF%BC%8CBinding%E6%98%AF%E5%A6%82%E4…...
头歌计算机组成原理实验—运算器设计(3)第3关:4位快速加法器设计
第3关:4位快速加法器设计 实验目的 帮助学生掌握快速加法器中先行进位的原理,能利用相关知识设计4位先行进位电路,并利用设计的4位先行进位电路构造4位快速加法器,能分析对应电路的时间延迟。 视频讲解 实验内容 利用前一步设…...
Java中synchronized的优化
本文介绍为了实现高效并发,虚拟机对 synchronized 做的一系列的锁优化措施 高效并发是从 JDK5 升级到 JDK6 后一项重要的改进项,HotSpot 虚拟机开发团队在 JDK6 这个版本上花费了大量的资源去实现各种锁优化技术,如适应性自旋(Ada…...
Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...
Java如何权衡是使用无序的数组还是有序的数组
在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...
06 Deep learning神经网络编程基础 激活函数 --吴恩达
深度学习激活函数详解 一、核心作用 引入非线性:使神经网络可学习复杂模式控制输出范围:如Sigmoid将输出限制在(0,1)梯度传递:影响反向传播的稳定性二、常见类型及数学表达 Sigmoid σ ( x ) = 1 1 +...
如何在最短时间内提升打ctf(web)的水平?
刚刚刷完2遍 bugku 的 web 题,前来答题。 每个人对刷题理解是不同,有的人是看了writeup就等于刷了,有的人是收藏了writeup就等于刷了,有的人是跟着writeup做了一遍就等于刷了,还有的人是独立思考做了一遍就等于刷了。…...
项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)
Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败,具体原因是客户端发送了密码认证请求,但Redis服务器未设置密码 1.为Redis设置密码(匹配客户端配置) 步骤: 1).修…...
蓝桥杯 冶炼金属
原题目链接 🔧 冶炼金属转换率推测题解 📜 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V,是一个正整数,表示每 V V V 个普通金属 O O O 可以冶炼出 …...
短视频矩阵系统文案创作功能开发实践,定制化开发
在短视频行业迅猛发展的当下,企业和个人创作者为了扩大影响力、提升传播效果,纷纷采用短视频矩阵运营策略,同时管理多个平台、多个账号的内容发布。然而,频繁的文案创作需求让运营者疲于应对,如何高效产出高质量文案成…...
Unity中的transform.up
2025年6月8日,周日下午 在Unity中,transform.up是Transform组件的一个属性,表示游戏对象在世界空间中的“上”方向(Y轴正方向),且会随对象旋转动态变化。以下是关键点解析: 基本定义 transfor…...
MySQL体系架构解析(三):MySQL目录与启动配置全解析
MySQL中的目录和文件 bin目录 在 MySQL 的安装目录下有一个特别重要的 bin 目录,这个目录下存放着许多可执行文件。与其他系统的可执行文件类似,这些可执行文件都是与服务器和客户端程序相关的。 启动MySQL服务器程序 在 UNIX 系统中,用…...
【QT控件】显示类控件
目录 一、Label 二、LCD Number 三、ProgressBar 四、Calendar Widget QT专栏:QT_uyeonashi的博客-CSDN博客 一、Label QLabel 可以用来显示文本和图片. 核心属性如下 代码示例: 显示不同格式的文本 1) 在界面上创建三个 QLabel 尺寸放大一些. objectName 分别…...
