如何处理异常

最近,我与一个朋友进行了讨论,他是一个相对初级但很聪明的软件开发人员。 她问我有关异常处理的问题。 这些问题指出了一种技巧和窍门,肯定有它们的清单。 但是我坚信我们编写软件的方式背后的背景和动机,因此我决定从这种角度写关于异常的想法。
编程中的异常(使用Java作为故事的舞台)用于通知我们在执行代码期间发生了问题。 异常是类的特殊类别。 使它们与众不同的是,它们扩展了Exception类,而后者又扩展了Throwable类。 作为Throwable的实现,我们可以在必要时“抛出”它们。 那么,如何发生异常? 异常类的实例从JVM或使用throw语句在代码段中引发。 那是怎么回事,但是为什么呢?

我敢肯定,当我们看到异常发生时,我们大多数人都会畏缩,但这是使我们受益的工具。 在引发异常之前,返回了特殊值或错误代码,以使我们知道操作没有成功。 忘记(或不知道)检查此类错误代码,可能会导致我们的应用程序发生不可预测的行为。 所以对

在我撰写以上内容时,我想到了两件事。 异常是一个不好的事件,因为创建异常时我们知道发生了问题。 异常是一种有用的构造,因为异常可以为我们提供有关发生错误的有价值的信息,并允许我们针对每种情况采取适当的行为。

试图散布此设计问题的实质: 触发方法/请求执行某项操作,但它可能会失败–我们如何最好地通知调用方它失败了? 我们如何传达有关发生的情况的信息? 我们如何帮助客户决定下一步该怎么做? 使用异常的问题在于我们“放弃了”,而不仅仅是“放弃”。 我们以“爆炸性”的方式来做,我们服务的客户/呼叫者必须处理混乱

因此,关于异常是我的第一个建议,因为它们是一件坏事,请尽量避免 。 在您所控制的软件部分中,实施难以出错的设计。 您可以使用支持此行为的语言功能。 我相信Java中最常见的异常是NullPointerException,而Optionals可以帮助我们避免它们。 让我们考虑一下我们想要检索具有指定id的雇员:

public Optional<Employee> tryGetEmployee(String employeeId) {return Optional.ofNullable(employeeService.getEmployee(employeeId));
}

现在好多了。 但是除了语言的功能之外,我们还可以通过某种方式设计代码,以免发生错误。 如果我们考虑一种只能接收正整数作为输入的方法,则可以设置代码,这样客户端就极不可能错误地传递无效输入。 首先,我们创建一个PositiveInteger类:

public class PositiveInteger {private Integer integerValue;public PositiveInteger(Integer inputValue) {if(inputValue <= 0) {throw new IllegalArgumentException("PositiveInteger instances can only be created out of positive integers");}this.integerValue = inputValue;}public Integer getIntegerValue() {return integerValue;}
}

然后,对于只能使用正整数作为输入的方法:

public void setNumberOfWinners(PositiveInteger numberOfWinners) { … }

这些当然是简单的例子,我确实认为,问题的核心是偶尔发生问题,然后我们必须告知客户发生的情况。 假设我们从外部后端系统检索了一个员工列表,那么事情可能会出错。 如何处理呢?
我们可以将响应对象设置为GetEmployeesResponse,如下所示:

public class GetEmployeesResponse {private Ok ok;private Error error;…class Ok {private List<Employee> employeeList;...}class Error {private String errorMessage;...}
}

但是,让我们成为现实主义者,您无法控制代码库的每个部分,也不会更改所有内容。 确实会发生异常,因此,让我们从它们的简要背景信息开始。

如前所述,Exception类扩展了Throwable类。 所有异常都是异常类的子类。 可以将异常分为已检查和未检查的异常。 这仅表示某些异常(已检查的异常)要求我们在编译时指定在异常发生时应用程序的行为方式。 未检查的异常不要求我们进行编译时间处理。 要创建此类异常,您可以扩展RuntimeException类,该类是Exception的直接子类。 关于已检查与未检查的较旧且常见的指导原则是,运行时异常用于表示应用程序通常无法预期或从中恢复的情况,而已检查异常是编写良好的应用程序应从中预期并从中恢复的情况。

好吧,我主张仅使用运行时异常 。 而且,如果我使用的库具有带有检查异常的方法,那么我将创建一个包装器方法,将其转换为运行时。 那为什么不检查异常呢? Bob叔叔在他的“ Clean Code”一书中指出, 它们违反了Open / Closed原理 ,因为使用新的throws声明更改签名可能会对调用该方法的程序的各个级别产生影响。

现在,无论是检查还是未检查,由于异常是一种结构,可让我们洞悉出了什么问题,因此应该对所发生的事情尽可能地具体和丰富 。 因此, 尝试使用标准异常,其他人将更容易理解发生的情况。 看到NullPointerException时,任何人都清楚原因。 如果您自己设置例外,请使其明智且具体。 例如,ValidationException使我知道某个验证失败,AgeValidationException使我指出特定的验证失败。 具体而言,既可以更轻松地诊断发生了什么,又可以根据发生的事情(异常类型)指定不同的行为。 这就是为什么您必须始终首先捕获最具体的异常的原因! 因此,这里出现了另一个常见建议,指示不要抓住“异常”。 这是一个有效的建议,我偶尔不遵循。 在我的api的边界(比如说我的REST服务的端点)中,我总是有通用的catch Exception子句。 我不希望任何意外,以及我在代码中无法预测或防范的事情,可能会向外界揭示事物。

具有描述性,但也可以根据抽象级别提供例外 。 考虑创建一个异常层次结构,以不同的抽象级别提供语义信息。 如果从程序的较低层引发了异常,例如与数据库相关的异常,则不必向API的调用者提供详细信息。 捕获异常并引发一个更抽象的异常,该异常仅通知调用者其尝试的操作失败。 似乎这与“仅在可能的情况下才捕获”的常见方法背道而驰,但事实并非如此。 只是在这种情况下,我们的“处理”是触发新异常。 在这些情况下,通过将原始异常传递给新异常的构造函数,可以使整个异常历史在抛出之间可用。

“手柄”一词已被多次使用。 这是什么意思? 当异常在我们熟悉的catch子句中被“捕获”时,被视为已处理。 引发异常时,首先它将在发生异常的代码中搜索异常处理,如果未找到异常,它将进入所包含方法的调用上下文,依此类推,直到找到异常处理程序或程序为止将终止。

我再次喜欢Bob叔叔的一件好事,就是try-catch-finally块定义了程序中的作用域。 除词汇范围外,我们还应考虑其概念范围,将try块视为事务 。 如果出问题了该怎么办? 我们如何确保使程序保持有效状态? 不要忽略例外! 我猜程序员对许多小时的不满是由无声异常引起的。 捕获并最终阻止是您进行清理的地方。 确保等待,直到掌握了正确处理异常的所有信息为止。 这可以与早起早发的原则联系在一起。 我们提早抛出,因此我们不必进行由于异常而不得不稍后恢复的操作,而为了及时掌握所有信息以正确处理异常,我们就可以迟到。 顺便说一句,当您捕获异常时,只有在解决它们时才记录日志,否则单个异常事件会导致日志混乱。 最后,对于异常处理,我个人更喜欢创建一个可以在代码的不同部分中使用的错误处理服务 ,并在日志记录,重新抛出,清理资源等方面采取适当的措施。它集中了我的错误处理行为,避免了代码重复,并帮助我从更高级的角度了解应用程序中如何处理错误。

因此,既然我们有了足够的上下文,悖论,规则及其例外,我们可以总结一下:

  • 尽量避免例外。 使用语言功能和适当的设计以实现它
  • 使用运行时异常,将方法与检查的异常包装在一起,然后将其转换为运行时
  • 尝试使用标准例外
  • 使您的例外情况具有特定性和描述性
  • 首先捕获最具体的异常
  • 不要赶上异常
  • 但是要在api的边界上抓住Exception。 完全掌控一切
  • 创建与应用程序的层和功能相匹配的异常层次结构
  • 在适当的抽象级别上抛出异常。 逐层移动时捕获异常并引发更高级别的异常
  • 通过在新的构造函数中提供异常,在重新抛出时传递完整的异常历史记录
  • 将try-catch-finally块视为事务。 当出现问题时,请确保将程序保持在有效状态
  • 在可以处理时捕获异常
  • 永远不要有空的catch子句
  • 处理异常时记录
  • 拥有全局异常处理服务,并具有如何处理错误的策略

就是这样! 继续前进,成为非凡!

翻译自: https://www.javacodegeeks.com/2017/12/how-to-deal-with-exceptions.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/348765.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

android动画详解

转自&#xff1a;工匠若水 http://blog.csdn.net/yanbober 1 背景 不能只分析源码呀&#xff0c;分析的同时也要整理归纳基础知识&#xff0c;刚好有人微博私信让全面说说Android的动画&#xff0c;所以今天来一发Android应用的各种Animation大集合。英文厉害的请直接移步参考…

凹数科技笔试

一、Java 1、成员变量作用域public/protected/defaultprivate/区别&#xff1f; public&#xff1a;该成员变量或其方法对当前类、同一包、子类、其他包都可见&#xff0c;所有类和对象都可以直接访问。 protected&#xff1a;该成员变量或其方法对当前类、同一包、子类都可…

jsr250-api_JSON处理的Java API(JSR-353)–流API

jsr250-apiJava很快将具有一组标准的API&#xff0c;作为Java EE 7的一部分处理JSON。该标准定义为JSR 353 – JSON处理的Java API &#xff08;JSON-P&#xff09;&#xff0c;目前正在最终批准投票中。 JSON-P提供面向对象和基于流的方法&#xff0c;在本文中&#xff0c;我将…

上传至GitHub

在工作目录下&#xff1a; git init git status git add . git commit -m"IndoorLocation" git status git remote add origin githttps://github.com/HiSunny/HelloInLoc.git git pull https://github.com/HiSunny/HelloInLoc.git master git push https://…

弹簧和线程:事务

为了能够在我们的线程中使用事务&#xff0c;我们需要了解事务如何在spring中工作。 spring中的事务信息存储在ThreadLocal变量中。 因此&#xff0c;这些变量特定于单个线程上正在进行的事务。 当涉及由单个线程运行的动作时&#xff0c;事务将在分层调用的Spring组件之间传播…

eclipse误删文件

右键工程--->restore from local history

Activity的LaunchMode和taskAffinity

Thanks to:http://www.cnblogs.com/SteveMing/archive/2012/04/24/2459575.html 【原】Activity的LaunchMode和taskAffinity 做项目到现在都一直没有理解LaunchMode有什么用&#xff0c;或许根本就没真正花心思去看&#xff0c;所以今天把这部分整理下。 设置Activity的Launc…

RelativeLayout重要属性

RelativeLayout用到的一些重要的属性&#xff1a;第一类:属性值为true或falseAndroid:layout_centerHrizontal 水平居中android:layout_centerVertical 垂直居中android:layout_centerI…

gradle spring_使用Gradle的简单Spring MVC Web应用程序

gradle spring除了我们现在将使用Spring MVC而不是原始servlet之外&#xff0c;该文章将与我们之前的文章Simple Gradle Web Application相似。 使用Gradle运行基本的Spring MVC应用程序确实很容易。 您可以在Github上下载本教程的源代码。 先决条件 安装Gradle 我们的基本项…

绘制Nine-Patch图片

Android sdk----->tools----->draw9patch.bat 打开draw9patch.bat文件 File--》open 0-patch将要绘制的.png图片加载进来 ctrl 图片边缘点击鼠标左键 绘制出线条&#xff0c;表示图片需要被拉伸的部分 File--save 9-patch 文件名pp.9.png。

jms spring_JMS和Spring:有时很重要的小事情

jms springJmsTemplate和DefaultMessageListenerContainer是用于访问JMS兼容MOM的Spring帮助器。 他们的主要目标是在JMS API之上形成一层&#xff0c;并处理诸如事务管理/消息确认之类的基础结构&#xff0c;并隐藏JMS API的某些重复和笨拙的部分&#xff08;保留在那里&#…

重温1 Android系统架构及版本

1、Linux内核层(Linux Kernel) 为Android设备的各种硬件提供了底层驱动&#xff0c;Display Driver/Camera Driver/Bluetooth Driver/Flash Memory Driver/Binder(IPC)Driver/USB Driver/Keypad Driver/WiFi Driver/Audio Drivers/Power Management 2、Libraries/Android Run…

REST资源何时应获得其自己的地址?

在纯粹的REST方法中&#xff0c;所有端点&#xff08;起始端点除外&#xff09;都是不透明的&#xff0c;不需要发布其各种详细信息。 即使使用这种方法&#xff0c;本文中的要点也很重要&#xff0c;因为服务器逻辑将必须确定何时需要结束点。 介绍 在REST体系结构中&#xf…

重温2 在Activity中使用Menu

1、引用string.xml中的字符串 1) 代码中 R.string.hello_world 2) XML中string/hello_world 2、AndroidManifest <activityandroid:name".MainActivity"android:label"string/app_name" ><intent-filter><action android:name"andr…

玩转大数据22:常见的关联规则挖掘算法

引言 关联规则挖掘是数据挖掘中的一种重要技术&#xff0c;主要用于发现数据集中项之间的有趣关系。关联规则挖掘在许多领域都有广泛的应用&#xff0c;如市场篮子分析、推荐系统等。常见的关联规则挖掘算法包括Apriori算法和FP-Growth算法。 一、Apriori算法 关联规则挖掘是…

重温3 Activity Intent 向下传递数据 向上传递数据

1、使用Intent向下传递数据 Intent intent new Intent(MainActivity.this,SecongActivity.class); intent.putExtra("data_from_MainActivity","I am MainActivity"); startActivity(intent);从SecondActivity取数据 Intent intent getIntent(); String …

junit 循环测试_重复运行JUnit测试而没有循环

junit 循环测试最近&#xff0c;我遇到了一个问题&#xff0c;我不得不编写一种方法的测试&#xff0c;该方法需要计算在一定可能性范围内的随机分布值1 。 更准确地说&#xff0c;如果您假设签名看起来像 interface RandomRangeValueCalculator {long calculateRangeValue( l…

Java 9:欢迎来到Module World

Java 9已于9月21日正式发布&#xff0c;Eclipse从Eclipse Oxygen.1a&#xff08;4.7.1a&#xff09;支持Java 9&#xff0c;让我们进入模块世界。 从此处下载Java 9&#xff0c;然后将其添加到Eclipse Installed JRE中&#xff0c;如下所示 就是这样&#xff0c;我们很高兴在…

重温5 UI开发

public class TextView extends View implements ViewTreeObserver.OnPreDrawListenerjava.lang.Object ↳android.view.View ↳android.widget.TextView Known Direct Subclasses Button,CheckedTextView,Chronometer,DigitalClock,EditText,TextClockButtonRepresents a…

JWT令牌的秘密轮换

当您使用JSON Web令牌 &#xff08; JWT &#xff09;或需要对有效载荷信息进行签名或加密的任何其他令牌技术时&#xff0c;设置令牌的到期日期很重要&#xff0c;因此&#xff0c;如果令牌到期&#xff0c;则可以假定这可能被视为安全漏洞&#xff0c;您拒绝使用此令牌进行任…