异常(exception)和执行失败有什么区别?
例如一个User Class 的 add 方法,在成功的情况下返回用户对象实例,在失败的情况返回False并可以通过getError方法获取失败原因字符串........
说到这里,我好像明白了,难道add方法总是应该返回用户对象,否则抛出异常吗?
但是这样的话,他们的代码量没什么区别的啊。问题在于即使调用add方法处没有捕捉异常,该异常也能进一步向上抛出直至被处理或引发进程崩溃?可是说到底,这和程序自然崩溃有什么区别呢?
---- 以上为自言自语,下面是问题 ----
抛出异常和返回false的区别是什么,两者在什么场景下使用?
回复内容:
首先,这是一个好问题。深入理解二者的区别是突破中等程度程序员的标志之一。先澄清一个误区,那就是异常非常慢。异常的实现需要保存异常抛出点到异常捕获点的必要信息,这是人们诟病异常性能的主要依据。但是这是错误的。第一,手工按照返回错误的风格取得行为等价于异常的实现性能绝大多数时候比不上语言内置的异常的性能。对于支持原生异常的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 { ... }
<span class="k">if</span><span class="p">(</span><span class="n">jsonObj</span><span class="p">.</span><span class="n">has</span><span class="p">(</span><span class="s">"xxx"</span><span class="p">))</span> <span class="p">{</span> <span class="k">if</span><span class="p">(</span><span class="n">jsonObj</span><span class="p">.</span><span class="n">has</span><span class="p">(</span><span class="s">"xxxxx"</span><span class="p">))</span> <span class="p">{</span> <span class="c1">//......... </span> <span class="k">return</span> <span class="nb">true</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
异常有两类,一类是『我知道这个地方可能出错,调用方必须明确知道出错的风险』,另一类是『这就是个错误,没啥挽救余地了崩溃拉倒』。这两类异常放Java里就是checked exception和unchecked exception的区别。
异常并不仅限于错误处理,很多时候是用来分开处理normal scenario和edge case的。比如说读取一个文件,把文件中每个词出现次数统计一下。这里用Python语法:
<span class="n">word_count</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">()</span> <span class="k">for</span> <span class="n">word</span> <span class="ow">in</span> <span class="n">words</span><span class="p">:</span> <span class="k">try</span><span class="p">:</span> <span class="n">word_count</span><span class="p">[</span><span class="n">word</span><span class="p">]</span> <span class="o">+=</span> <span class="mi">1</span> <span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span> <span class="n">word_count</span><span class="p">[</span><span class="n">word</span><span class="p">]</span> <span class="o">=</span> <span class="mi">1</span>
设计成返回bool或指针的函数,就不需要异常。
也就是说异常和返回值是永远不同时存在的(当然这是我个人的理解)。
(我觉得,你问题中的add函数如果有可能失败,那么就不抛异常,如果正常情况下不应该失败,那么可以抛异常。但并不一定需要在每一个add函数的地方都加try。加不加得看你的程序逻辑能否以及应该恢复..(这时你也可以写一个辅助函数在其内部调用try add..其他地方使用这个辅助函数:),继续跑。否则就在程序入口加个try,然后退出即可)
好像还没答全你的这个好问题,明天电脑上再讨论讨论。 能返回Either或者Maybe就方便了, 像Either这样错误信息够丰富的情况下根本不需要异常 从WINDOWS实现上来讲一个分支语句,一个是SEH或者全局回调处理,后者可以很好的防止不再预料的情况时程序奔溃! 第一,异常都是可以预见的,所以它有可预知性。第二,异常不关心发生的条件而关心如何善后。第三,异常是被定义过的错误,比较容易定位问题。第四,异常可以传递。
执行失败的结果就是直接退出。。。。 假设你对某个异常(或者说执行错误)需要这样处理:
——在用户界面显示一条错误信息。
但是这个异常是由底层的API,比如数据库调用抛出的,而客户端没有直接调用这个API的权限。
这时候,你可以选择:
- 抛出异常,一层一层抛出
- 定义错误代码,一层一层返回
- 发现每一个函数都增加了一个叫err的传入参数,这个参数并没有什么卵用,因为它会被不加处理地返回给上一层,直达UI。
- 这并没有什么问题,但是有一天你被告知某个底层改动导致这个API会抛出一种新的错误,老板要求你对这种错误在调用栈里的某一层处理掉。于是你增加了对异常类型的判断。
- 后来,另一个新的模块需要复用你在上次处理的那个module里写的异常信息,你为了不c&p代码,把异常信息封装成了一个Error类,每个从Error继承来的子类都保存着自己独有的异常信息。
- 你对自己造的轮子很满意,然后用到了现在维护的模块中——现在你的代码看起来是这样:跨函数error code。 强制错误检查。 happen path 简化。 最后一个,你得会写异常安全的代码。
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)
登录后复制

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

热门话题

类和方法的概念和实例类(Class):用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。方法:类中定义的函数。类的构造方法__init__():类有一个名为init()的特殊方法(构造方法),该方法在类实例化时会自动调用。实例变量:在类的声明中,属性是用变量来表示的,这种变量就称为实例变量,实例变量就是一个用self修饰的变量。实例化:创建一个类的实例,类的具体对象。继承:即一个派生类(derivedclass)继承基类(baseclass)的

jQuery是一种经典的JavaScript库,被广泛应用于网页开发中,它简化了在网页上处理事件、操作DOM元素和执行动画等操作。在使用jQuery时,经常会遇到需要替换元素的class名的情况,本文将介绍一些实用的方法,以及具体的代码示例。1.使用removeClass()和addClass()方法jQuery提供了removeClass()方法用于删除

class是python中的一个关键字,用来定义一个类,定义类的方法:class后面加一个空格然后加类名;类名规则:首字母大写,如果多个单词用驼峰命名法,如【class Dog()】。

在玩GrandTheftAutoV等游戏时,许多玩家都喜欢使用Mod来增加游戏的乐趣和可玩性。而OpenIV作为一个知名的Mod管理工具,可以简化Mod的安装和管理过程。那么,接下来我们就来了解一下OpenIV如何加入Mod吧。首先,你需要确保已经下载并安装了OpenIV。在OpenIV的官方网站(https://openiv.com/)上可以找到

在编写PHP代码时,使用类(Class)是一个非常常见的做法。通过使用类,我们可以将相关的功能和数据封装在一个单独的单元中,使代码更加清晰、易读和易维护。本文将详细介绍PHPClass的用法,并提供具体的代码示例,帮助读者更好地理解如何在实际项目中应用类来优化代码。1.创建和使用类在PHP中,可以使用关键字class来定义一个类,并在类中定义属性和方法。

Vue报错:无法正确使用v-bind绑定class和style,怎样解决?在Vue开发中,我们经常会用到v-bind指令来动态绑定class和style,但是有时候我们可能会遇到一些问题,如无法正确使用v-bind绑定class和style。在本篇文章中,我将为你解释这个问题的原因,并提供解决方案。首先,让我们先了解一下v-bind指令。v-bind用于将V

jquery判断元素是否有class的方法:1、通过“hasClass('classname')”方法判断元素是否具有某个class;2、通过“is('.classname')”方法判断元素是否具有某个class。

背景最近针对公司框架进行关键业务代码进行加密处理,防止通过jd-gui等反编译工具能够轻松还原工程代码,相关混淆方案配置使用比较复杂且针对springboot项目问题较多,所以针对class文件加密再通过自定义的classloder进行解密加载,此方案并不是绝对安全,只是加大反编译的困难程度,防君子不防小人,整体加密保护流程图如下图所示maven插件加密使用自定义maven插件对编译后指定的class文件进行加密,加密后的class文件拷贝到指定路径,这里是保存到resource/corecla
