企业🤖AI Agent构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
## [后记:Exception Bizarro World](https://lingcoder.gitee.io/onjava8/#/book/15-Exceptions?id=%e5%90%8e%e8%ae%b0%ef%bc%9aexception-bizarro-world) (来自于 2011 年的一篇博文) 我的朋友 James Ward 正在尝试使用 JDBC 创建一些非常简单的教学示例,但不断被受检查的异常所挫败。他把 Howard Lewis Ship 的帖子“[被检查的异常的悲剧](http://tapestryjava.blogspot.com/2011/05/tragedy-of-checked-exceptions.html)”指给我看。让 James 尤其沮丧的是,即使做一些本来很简单的事情,也必须在一个个环里跳来跳去。即使在`finally`块中,他也不得不放入更多的`try-catch`子句,因为关闭连接也会导致异常。这些麻烦事的终点在哪里?本来只是做一些简单的事,但却被强制要求在一个个环里跳来跳去(注意,try-with-resources语句可以显著改善这种情况)。 我们开始讨论 Go 编程语言,我很着迷,因为Rob Pike等人。我们已经清楚地提出了许多关于语言设计的非常尖锐和基本的问题。基本上,他们已经采取了我们开始接受的有关语言的所有内容,并询问“为什么?”关于每一种语言。学习这门语言真的让你思考和怀疑。 我的印象是Go团队不做任何臆断,只有在明确一个特征是必须的时候才改进语言。他们似乎并不担心做出破坏旧代码的更改 ,因为他们创建了一个重写工具,当做出更改的时候,重写工具将为你重写代码。这使他们将语言变成一个前进的实验,以发现真正需要的东西,而不是做 Big Upfront Design。 他们做出的最有趣的决定之一是完全排除异常。你没有看错 —— 他们不只是遗漏了经过检查的异常情况。他们遗漏了所有异常情况。 替代方案非常简单,起初它几乎看起来像 C 一样。因为 Go 从一开始就包含了元组,所以你可以轻松地从函数调用中返回两个对象: ~~~ result, err := functionCall() ~~~ (`:=`告诉 Go 语言在这里定义`result`和`err`,并且推断它们的类型) 就是这样:对于每次调用,您都会获得结果对象和错误对象。您可以立即检查错误(这是典型的,因为如果某些操作失败,则不太可能继续下一步),或者稍后检查是否有效。 起初这看起来很原始,是向“古代”的回归。但到目前为止,我发现 Go 中的决定都经过了很好的考虑,值得深思。我的反应是因为我的大脑是异常的吗?这会如何影响 James 的问题? 它发生在我身上,我已经将异常处理视为一种并行执行路径。如果你遇到异常,你会跳出正常的路径进入这个并行执行路径,这是一种“奇异世界”,你不再做你写的东西,而是跳进 catch 和 finally 子句。正是这种替代执行路径的世界导致了 James 抱怨的问题。 James 创造了一个对象。理想的情况下。对象创建不会导致潜在的异常,因此你必须抓住它们。你必须通过 try-finally 跟踪创建以确保清理发生(Python团队意识到清理不是一个特殊的条件,而是一个单独的问题,所以他们创建了一个不同的语言构造 - 以便停止混淆二者)。任何导致异常的调用都会停止正常的执行路径并跳转(通过并行bizarro-world)到 catch 子句。 关于异常的一个基本假设是,我们通过在块结束时收集所有错误处理代码而不是在它们发生时处理错误来获益。在这两种情况下,我们都会停止正常执行,但是异常处理有一个自动机制,它会将你从正常的执行路径中抛出,跳转到你的并行异常世界,然后在正确的处理程序中再次弹出你。 跳入奇异的世界会给 James 带来问题,它为所有程序员增加了更多的工作:因为你无法知道什么时候会发生什么事(你可以随时进入奇怪的世界),你必须添加一些 try 块来确保没有任何东西从裂缝中滑落。您最终必须进行额外的编程以补偿异常机制(它似乎类似于补偿共享内存并发所需的额外工作)。 Go 团队采取了大胆的举动,质疑所有这些,并说,“让我们毫无例外地尝试它,看看会发生什么。”是的,这意味着你通常会在发生错误的地方处理错误,而不是最后将它们聚集在一起 try 块。但这也意味着关于一件事的代码是本地化的,也许这并不是那么糟糕。这也可能意味着您无法轻松组合常见的错误处理代码(除非您确定了常用代码并将其放入函数中,也不是那么糟糕)。但这绝对意味着您不必担心有多个可能的执行路径而且所有这些都需要。