0

0

异常(exception)和执行失败有什么区别?

php中文网

php中文网

发布时间:2016-06-06 16:11:08

|

1758人浏览过

|

来源于php中文网

原创

例如一个user class 的 add 方法,在成功的情况下返回用户对象实例,在失败的情况返回false并可以通过geterror方法获取失败原因字符串........

说到这里,我好像明白了,难道add方法总是应该返回用户对象,否则抛出异常吗?
但是这样的话,他们的代码量没什么区别的啊。问题在于即使调用add方法处没有捕捉异常,该异常也能进一步向上抛出直至被处理或引发进程崩溃?可是说到底,这和程序自然崩溃有什么区别呢?

---- 以上为自言自语,下面是问题 ----

抛出异常和返回false的区别是什么,两者在什么场景下使用?

畅图
畅图

AI可视化工具

下载

回复内容:

首先,这是一个好问题。深入理解二者的区别是突破中等程度程序员的标志之一。

先澄清一个误区,那就是异常非常慢。异常的实现需要保存异常抛出点到异常捕获点的必要信息,这是人们诟病异常性能的主要依据。但是这是错误的。第一,手工按照返回错误的风格取得行为等价于异常的实现性能绝大多数时候比不上语言内置的异常的性能。对于支持原生异常的OS,如Windows,这个差距尤其明显;第二,对于非基于栈的调用实现,异常和返回错误性能几无差别,只是风格不同而已。

回到问题,异常和返回错误的本质区别是什么?答:异常是强类型的,类型安全的分支处理技术;而返回错误是其弱化的,不安全的版本。

先看返回错误的特征。一般来说,需要用特别返回值来标识错误,意味着需要重载特定值得含义。假设一个操作会返回一个int型值,因为其不可能返回负数(如求绝对值),我们一般可以使用-1来作为操作失败(如函数库调用加载错误)的指示。-1在int类型中并无特别含义,然而我们这里强制施加了语义,而这种语义是弱的。没办法通过类型系统检查来确保正确性。

一个增强的办法是同时返回两种值。有的语言内置支持,没有内置支持的可以通过定义一个含有分别表征正确返回类型和错误类型的结构来模拟。这样的好处是传递信息的通道被分离了。

无论如何返回错误,它存在着一个显著的缺点:调用方必须显式检查错误值。当错误发生,必须终止当前操作,返回错误的时候,一般需要定义全新的错误类型。这导致操作不可任意简单组合,如模块B调用模块A发生错误的时候需要返回B定义的错误,而模块C调用A错误时则需要返回C定义的错误,而这两类错误往往是不兼容的。组合相关代码必须加入大量的粘连代码,这导致了代码膨胀,而且非常容易出错。对于接口定义明确的系统,一般定义一套通用的错误码来简化其复杂性。OS内核就是一个例子,它提供了一个适配所有OS调用的错误码表。然而其内部使用的错误码要多得多,并且往往在接口把内部错误码映射到外部错误码。

异常是一种安全的分支处理技术。因为它和错误处理密切相关,人们容易直接把异常看成是错误处理,连名字“异常”都带着浓浓的错误的含义。但是这是一个误解。异常的理论基础是假设某些分支处理中,一个分支和其它分支比起来发生的非常不频繁。与其平等地针对常见和极其罕见的情形进行处理(想一下,正常处理代码和错误处理代码往往一样多,大部分情况下后者其实更多),不如仅仅处理正常的情形,把不常见的情形归于一处统一处理。这样我们书写代码的时候仅仅关注正常情形就可以了。发生错误的时候,特别的流程会帮助程序员直接回到定义了错误处理的地方。

这样说有点过分简化。一般情况下,不能直接跳回。因为在异常抛出点到异常处理点之间,可能存在处于非一致状态的值,等待释放的资源等。所以一般情况下,特殊流程要回溯调用栈,确保执行的事务性。但是仅仅这个特殊流程是不够的,必须有合适的配套代码才行。这导致了额外的复杂性。C++引入的RAII概念是一个创举,然而依然没能全部消除程序员的工作。

因为异常是一个具体的类型,一个函数的signature就不仅仅是输入参数列表,输出参数加函数名,还要包含可能抛出异常列表。因为各个模块定义的异常层次结构迥异,这导致了额外的组合困难。然而和返回错误值的组合困难不同,前者导致的是编译时类型错误,后者则是运行时错误。一般来看,错误暴露的越早越好,我们倾向于前者。

理想情况下,一个异常结构完备的系统不会有运行时错误(大概如此,不展开)。然而因为上面提到的原因,在现有的异常支持的情况下,代码同样复杂,冗余,难以维护。

上面就是异常和错误的基本区别。

第二部分预告:有解决上述问题的优雅方法吗?

我的观点是有。但是因为现有能力还不到不借助任何参考直接澄清此问题,我回留待仔细斟酌,沐浴焚香,换到非手机输入的情况下给出,尽请期待。 都是错误处理的策略,OO语言更倾向异常,相比较判断一个函数可能的错误状况来说,“抛出”状态完整的异常对象被认为是更好的封装 ;你说的情况下,错误即false是最简单的情况,这种情况下,异常未必有优势;一般认为用异常可以简化复杂的错误处理代码,并且只要程序不需要终止执行,try catch比if else的代码更可读(错误处理的策略明显,状态可以更丰富,虽然这不是绝对的)。

result = func();
if (result === ERR_NO_1) {
  ... 
} else if (result === ERR_NO_2) {
  
} else {
  ...
}
一个很简单的例子,你要解析json的请求回包,只返回false的代码可能是
if(jsonObj.has("xxx"))
{
    if(jsonObj.has("xxxxx"))
    {
        //......... 
        return true;
    }
}

return false;
写了一些,但是觉得没什么好说的。很多疑惑的问题,见得多了写得多了自然就知道。很多时候没有孰优孰劣,只有风格上的统一。例如C++这样的支持exception的语言,在很多代码库里是完全exception free,而用类C的形式来处理异常。

异常有两类,一类是『我知道这个地方可能出错,调用方必须明确知道出错的风险』,另一类是『这就是个错误,没啥挽救余地了崩溃拉倒』。这两类异常放Java里就是checked exception和unchecked exception的区别。

异常并不仅限于错误处理,很多时候是用来分开处理normal scenario和edge case的。比如说读取一个文件,把文件中每个词出现次数统计一下。这里用Python语法:

word_count = dict()
for word in words:
    try:
        word_count[word] += 1
    except KeyError:
        word_count[word] = 1
抛出异常的函数,就不需要返回bool。
设计成返回bool或指针的函数,就不需要异常。

也就是说异常和返回值是永远不同时存在的(当然这是我个人的理解)。

(我觉得,你问题中的add函数如果有可能失败,那么就不抛异常,如果正常情况下不应该失败,那么可以抛异常。但并不一定需要在每一个add函数的地方都加try。加不加得看你的程序逻辑能否以及应该恢复..(这时你也可以写一个辅助函数在其内部调用try add..其他地方使用这个辅助函数:),继续跑。否则就在程序入口加个try,然后退出即可)

好像还没答全你的这个好问题,明天电脑上再讨论讨论。 能返回Either或者Maybe就方便了, 像Either这样错误信息够丰富的情况下根本不需要异常 从WINDOWS实现上来讲一个分支语句,一个是SEH或者全局回调处理,后者可以很好的防止不再预料的情况时程序奔溃! 第一,异常都是可以预见的,所以它有可预知性。第二,异常不关心发生的条件而关心如何善后。第三,异常是被定义过的错误,比较容易定位问题。第四,异常可以传递。

执行失败的结果就是直接退出。。。。 假设你对某个异常(或者说执行错误)需要这样处理:
——在用户界面显示一条错误信息。

但是这个异常是由底层的API,比如数据库调用抛出的,而客户端没有直接调用这个API的权限。
这时候,你可以选择:
  1. 抛出异常,一层一层抛出
  2. 定义错误代码,一层一层返回
你选2,
  1. 发现每一个函数都增加了一个叫err的传入参数,这个参数并没有什么卵用,因为它会被不加处理地返回给上一层,直达UI。
  2. 这并没有什么问题,但是有一天你被告知某个底层改动导致这个API会抛出一种新的错误,老板要求你对这种错误在调用栈里的某一层处理掉。于是你增加了对异常类型的判断。
  3. 后来,另一个新的模块需要复用你在上次处理的那个module里写的异常信息,你为了不c&p代码,把异常信息封装成了一个Error类,每个从Error继承来的子类都保存着自己独有的异常信息。
  4. 你对自己造的轮子很满意,然后用到了现在维护的模块中——现在你的代码看起来是这样:
    def f(err, **kwargs):
       result = None
       if(err.code == 1):
          handle_this_err(err)
       elif(err.code == 2):
          return (err, result)
       else:
          result = handle_data(**kwargs)
       return (None, result)
    
    跨函数error code。 强制错误检查。 happen path 简化。 最后一个,你得会写异常安全的代码。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
pixiv网页版官网登录与阅读指南_pixiv官网直达入口与在线访问方法
pixiv网页版官网登录与阅读指南_pixiv官网直达入口与在线访问方法

本专题系统整理pixiv网页版官网入口及登录访问方式,涵盖官网登录页面直达路径、在线阅读入口及快速进入方法说明,帮助用户高效找到pixiv官方网站,实现便捷、安全的网页端浏览与账号登录体验。

286

2026.02.13

微博网页版主页入口与登录指南_官方网页端快速访问方法
微博网页版主页入口与登录指南_官方网页端快速访问方法

本专题系统整理微博网页版官方入口及网页端登录方式,涵盖首页直达地址、账号登录流程与常见访问问题说明,帮助用户快速找到微博官网主页,实现便捷、安全的网页端登录与内容浏览体验。

126

2026.02.13

Flutter跨平台开发与状态管理实战
Flutter跨平台开发与状态管理实战

本专题围绕Flutter框架展开,系统讲解跨平台UI构建原理与状态管理方案。内容涵盖Widget生命周期、路由管理、Provider与Bloc状态管理模式、网络请求封装及性能优化技巧。通过实战项目演示,帮助开发者构建流畅、可维护的跨平台移动应用。

42

2026.02.13

TypeScript工程化开发与Vite构建优化实践
TypeScript工程化开发与Vite构建优化实践

本专题面向前端开发者,深入讲解 TypeScript 类型系统与大型项目结构设计方法,并结合 Vite 构建工具优化前端工程化流程。内容包括模块化设计、类型声明管理、代码分割、热更新原理以及构建性能调优。通过完整项目示例,帮助开发者提升代码可维护性与开发效率。

19

2026.02.13

Redis高可用架构与分布式缓存实战
Redis高可用架构与分布式缓存实战

本专题围绕 Redis 在高并发系统中的应用展开,系统讲解主从复制、哨兵机制、Cluster 集群模式及数据分片原理。内容涵盖缓存穿透与雪崩解决方案、分布式锁实现、热点数据优化及持久化策略。通过真实业务场景演示,帮助开发者构建高可用、可扩展的分布式缓存系统。

23

2026.02.13

c语言 数据类型
c语言 数据类型

本专题整合了c语言数据类型相关内容,阅读专题下面的文章了解更多详细内容。

29

2026.02.12

雨课堂网页版登录入口与使用指南_官方在线教学平台访问方法
雨课堂网页版登录入口与使用指南_官方在线教学平台访问方法

本专题系统整理雨课堂网页版官方入口及在线登录方式,涵盖账号登录流程、官方直连入口及平台访问方法说明,帮助师生用户快速进入雨课堂在线教学平台,实现便捷、高效的课程学习与教学管理体验。

14

2026.02.12

豆包AI网页版入口与智能创作指南_官方在线写作与图片生成使用方法
豆包AI网页版入口与智能创作指南_官方在线写作与图片生成使用方法

本专题汇总豆包AI官方网页版入口及在线使用方式,涵盖智能写作工具、图片生成体验入口和官网登录方法,帮助用户快速直达豆包AI平台,高效完成文本创作与AI生图任务,实现便捷智能创作体验。

421

2026.02.12

PostgreSQL性能优化与索引调优实战
PostgreSQL性能优化与索引调优实战

本专题面向后端开发与数据库工程师,深入讲解 PostgreSQL 查询优化原理与索引机制。内容包括执行计划分析、常见索引类型对比、慢查询优化策略、事务隔离级别以及高并发场景下的性能调优技巧。通过实战案例解析,帮助开发者提升数据库响应速度与系统稳定性。

51

2026.02.12

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
深入剖析redis教程
深入剖析redis教程

共55课时 | 8.2万人学习

传智播客redis基础视频教程
传智播客redis基础视频教程

共13课时 | 5.7万人学习

前端系列快速入门课程
前端系列快速入门课程

共4课时 | 0.4万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号