当前位置: 首页 > news >正文

十五、异常(1)

本章概要

  • 异常概念
  • 基本异常
    • 异常参数
  • 异常捕获
    • try 语句块
    • 异常处理程序
    • 终止与恢复

Java 的基本理念是“结构不佳的代码不能运行”。

改进的错误恢复机制是提高代码健壮性的最强有力的方式。错误恢复在我们所编写的每一个程序中都是基本的要素,但是在 Java 中它显得格外重要,因为 Java 的主要目标之一就是创建供他人使用的程序构件。

发现错误的理想时机是在编译阶段,也就是在你试图运行程序之前。然而,编译期间并不能找出所有的错误,余下的问题必须在运行期间解决。这就需要错误源能通过某种方式,把适当的信息传递给某个接收者——该接收者将知道如何正确处理这个问题。

要想创建健壮的系统,它的每一个构件都必须是健壮的。

Java 使用异常来提供一致的错误报告模型,使得构件能够与客户端代码可靠地沟通问题。

Java 中的异常处理的目的在于通过使用少于目前数量的代码来简化大型、可靠的程序的生成,并且通过这种方式可以使你更加确信:你的应用中没有未处理的错误。异常的相关知识学起来并非艰涩难懂,并且它属于那种可以使你的项目受益明显、立竿见影的特性之一。

因为异常处理是 Java 中唯一官方的错误报告机制,并且通过编译器强制执行,所以不学习异常处理的话,你也就只能写出书中那么些例子了。本章将教你如何编写正确的异常处理程序,以及当方法出问题的时候,如何产生自定义的异常。

异常概念

C 以及其他早期语言常常具有多种错误处理模式,这些模式往往建立在约定俗成的基础之上,而并不属于语言的一部分。通常会返回某个特殊值或者设置某个标志,并且假定接收者将对这个返回值或标志进行检查,以判定是否发生了错误。然而,随着时间的推移,人们发现,高傲的程序员们在使用程序库的时候更倾向于认为:“对,错误也许会发生,但那是别人造成的,不关我的事”。所以,程序员不去检查错误情形也就不足为奇了(何况对某些错误情形的检查确实很无聊)。如果的确在每次调用方法的时候都彻底地进行错误检查,代码很可能会变得难以阅读。正是由于程序员还仍然用这些方式拼凑系统,所以他们拒绝承认这样一个事实:对于构造大型、健壮、可维护的程序而言,这种错误处理模式已经成为了主要障碍。

解决的办法是,用强制规定的形式来消除错误处理过程中随心所欲的因素。这种做法由来已久,对异常处理的实现可以追溯到 20 世纪 60 年代的操作系统,甚至于 BASIC 语言中的“on error goto”语句。而 C的异常处理机制基于 Ada,Java 中的异常处理机制则建立在 C 的基础之上(尽管看上去更像 Object Pascal)。

“异常”这个词有“我对此感到意外”的意思。问题出现了,你也许不清楚该如何处理,但你的确知道不应该置之不理,你要停下来,看看是不是有别人或在别的地方,能够处理这个问题。只是在当前的环境中还没有足够的信息来解决这个问题,所以就把这个问题提交到一个更高级别的环境中,在那里将作出正确的决定。

异常往往能降低错误处理代码的复杂度。如果不使用异常,那么就必须检查特定的错误,并在程序中的许多地方去处理它。而如果使用异常,那就不必在方法调用处进行检查,因为异常机制将保证能够捕获这个错误。理想情况下,只需在一个地方处理错误,即所谓的异常处理程序中。这种方式不仅节省代码,而且把“描述在正常执行过程中做什么事”的代码和“出了问题怎么办”的代码相分离。总之,与以前的错误处理方法相比,异常机制使代码的阅读、编写和调试工作更加井井有条。

基本异常

异常情形(exceptional condition)是指阻止当前方法或作用域继续执行的问题。把异常情形与普通问题相区分很重要,所谓的普通问题是指,在当前环境下能得到足够的信息,总能处理这个错误。而对于异常情形,就不能继续下去了,因为在当前环境下无法获得必要的信息来解决问题。你所能做的就是从当前环境跳出,并且把问题提交给上一级环境。这就是抛出异常时所发生的事情。

除法就是一个简单的例子。除数有可能为 0,所以先进行检查很有必要。但除数为 0 代表的究竟是什么意思呢?通过当前正在解决的问题环境,或许能知道该如何处理除数为 0 的情况。但如果这是一个意料之外的值,你也不清楚该如何处理,那就要抛出异常,而不是顺着原来的路径继续执行下去。

当抛出异常后,有几件事会随之发生。首先,同 Java 中其他对象的创建一样,将使用 new 在堆上创建异常对象。然后,当前的执行路径(它不能继续下去了)被终止,并且从当前环境中弹出对异常对象的引用。此时,异常处理机制接管程序,并开始寻找一个恰当的地方来继续执行程序。这个恰当的地方就是异常处理程序,它的任务是将程序从错误状态中恢复,以使程序能要么换一种方式运行,要么继续运行下去。

举一个抛出异常的简单例子。对于对象引用 t,传给你的时候可能尚未被初始化。所以在使用这个对象引用调用其方法之前,会先对引用进行检查。可以创建一个代表错误信息的对象,并且将它从当前环境中“抛出”,这样就把错误信息传播到了“更大”的环境中。这被称为_抛出一个异常_,看起来像这样:

if(t == null){throw new NullPointerException();
}

这就抛出了异常,于是在当前环境下就不必再为这个问题操心了,它将在别的地方得到处理。

异常允许你将做的每件事都当作一个事务来考虑,而异常守护着这些事务:“…事务的基本保障是,我们需要的分布式计算的异常处理机制。事务相当于计算机中的合同法。如果任何事出现了错误,我们只需要丢弃整个计算。”你也可以将异常看作一种内建的“恢复”(undo)系统,因为(在细心使用时)你在程序中可以有各种恢复点。一旦程序的一个部分失败了,异常将“恢复”到一个已知的稳定点上。

异常参数

与使用 Java 中的其他对象一样,我们总是用 new 在堆上创建异常对象,这也伴随着存储空间的分配和构造器的调用。所有标准异常类都有两个构造器:一个是无参构造器;另一个是接受字符串作为参数,以便能把相关信息放入异常对象的构造器:

throw new NullPointerException("t = null");

要把这个字符串的内容提取出来可以有多种不同的方法。

关键字 throw 将产生许多有趣的结果。在使用 new 创建了异常对象之后,此对象的引用将传给 throw。尽管异常对象的类型通常与方法设计的返回类型不同,但从效果上看,它就像是从方法“返回”的。可以简单地把异常处理看成一种不同的返回机制,当然若过分强调这种类比的话,就会有麻烦了。另外还能用抛出异常的方式从当前的作用域退出。在这两种情况下,将会返回一个异常对象,然后退出方法或作用域。

抛出异常与方法正常返回的相似之处到此为止。因为异常返回的“地点”与普通方法调用返回的“地点”完全不同。(异常将在一个恰当的异常处理程序中得到解决,它的位置可能离异常被抛出的地方很远,也可能会跨越方法调用栈的许多层级。)

此外,能够抛出任意类型的 Throwable 对象,它是异常类型的根类。通常,对于不同类型的错误,要抛出相应的异常。错误信息可以保存在异常对象内部或者用异常类的名称来暗示。上一层环境通过这些信息来决定如何处理异常。(通常,唯一的信息只有异常的类型名,而在异常对象内部没有任何有意义的信息。)

异常捕获

要明白异常是如何被捕获的,必须首先理解监控区域(guarded region)的概念。它是一段可能产生异常的代码,并且后面跟着处理这些异常的代码。

try 语句块

如果在方法内部抛出了异常(或者在方法内部调用的其他方法抛出了异常),这个方法将在抛出异常的过程中结束。要是不希望方法就此结束,可以在方法内设置一个特殊的块来捕获异常。因为在这个块里“尝试”各种(可能产生异常的)方法调用,所以称为 try 块。它是跟在 try 关键字之后的普通程序块:

try {// Code that might generate exceptions
}

对于不支持异常处理的程序语言,要想仔细检查错误,就得在每个方法调用的前后加上设置和错误检查的代码,甚至在每次调用同一方法时也得这么做。有了异常处理机制,可以把所有动作都放在 try 块里,然后只需在一个地方就可以捕获所有异常。这意味着你的代码将更容易编写和阅读,因为代码的意图和错误检查不是混淆在一起的。

异常处理程序

当然,抛出的异常必须在某处得到处理。这个“地点”就是异常处理程序,而且针对每个要捕获的异常,得准备相应的处理程序。异常处理程序紧跟在 try 块之后,以关键字 catch 表示:

try {// Code that might generate exceptions
} catch(Type1 id1) {// Handle exceptions of Type1
} catch(Type2 id2) {// Handle exceptions of Type2
} catch(Type3 id3) {// Handle exceptions of Type3
}
// etc.

每个 catch 子句(异常处理程序)看起来就像是接收且仅接收一个特殊类型的参数的方法。可以在处理程序的内部使用标识符(id1,id2 等等),这与方法参数的使用很相似。有时可能用不到标识符,因为异常的类型已经给了你足够的信息来对异常进行处理,但标识符并不可以省略。

异常处理程序必须紧跟在 try 块之后。当异常被抛出时,异常处理机制将负责搜寻参数与异常类型相匹配的第一个处理程序。然后进入 catch 子句执行,此时认为异常得到了处理。一旦 catch 子句结束,则处理程序的查找过程结束。注意,只有匹配的 catch 子句才能得到执行;这与 switch 语句不同,switch 语句需要在每一个 case 后面跟一个 break,以避免执行后续的 case 子句。

注意在 try 块的内部,许多不同的方法调用可能会产生类型相同的异常,而你只需要提供一个针对此类型的异常处理程序。

终止与恢复

异常处理理论上有两种基本模型。Java 支持终止模型(它是 Java 和 C++所支持的模型)。在这种模型中,将假设错误非常严重,以至于程序无法返回到异常发生的地方继续执行。一旦异常被抛出,就表明错误已无法挽回,也不能回来继续执行。

另一种称为恢复模型。意思是异常处理程序的工作是修正错误,然后重新尝试调用出问题的方法,并认为第二次能成功。对于恢复模型,通常希望异常被处理之后能继续执行程序。如果想要用 Java 实现类似恢复的行为,那么在遇见错误时就不能抛出异常,而是调用方法来修正该错误。或者,把 try 块放在 while 循环里,这样就不断地进入 try 块,直到得到满意的结果。

在过去,使用支持恢复模型异常处理的操作系统的程序员们最终还是转向使用类似“终止模型”的代码,并且忽略恢复行为。所以虽然恢复模型开始显得很吸引人,但不是很实用。其中的主要原因可能是它所导致的耦合:恢复性的处理程序需要了解异常抛出的地点,这势必要包含依赖于抛出位置的非通用性代码。这增加了代码编写和维护的困难,对于异常可能会从许多地方抛出的大型程序来说,更是如此。

相关文章:

十五、异常(1)

本章概要 异常概念基本异常 异常参数 异常捕获 try 语句块异常处理程序终止与恢复 Java 的基本理念是“结构不佳的代码不能运行”。 改进的错误恢复机制是提高代码健壮性的最强有力的方式。错误恢复在我们所编写的每一个程序中都是基本的要素,但是在 Java 中它显得…...

Redis 哨兵模式搭建教程

一、介绍 本文实战搭建一主两从三哨兵,通过使用哨兵模式,可以有效避免某台服务器的 Redis 挂掉出现的不可用问题,保障系统的高可用。 本文通过虚拟机搭建的三台 Centos7 服务器进行测试,使用的 Redis 版本为 6.25。 二、准备环…...

【C++】gnustl_static 与 c++_shared 的区别

参考:GNU与cSTL的区别与联系-爱代码爱编程​ gnustl_static 与 c_shared 的区别: 不同版本的 STL TSL是一个与STL兼容的多线程支持库。 STLport是一个可移植、高度兼容的STL实现。 SGI STL是最早的STL实现之一,对STL的发展起到了重要的作用…...

怎样选择第三方检测机构获取功能测试报告?

数字化时代,软件给人们的生活带来了越来越多的便利,产品功能测试也成为了软件开发方在研发时的重要环节,这关系到用户使用产品的体验感。所以做好软件功能测试对把控产品质量有着很大影响,通过有效的功能测试能够发现系统潜在的问…...

【@PostConstruct、 @Autowired与构造函数的执行顺序】

PostConstruct、 Autowired与构造函数的执行顺序 一、PostConstruct介绍二、Spring框架中在bean初始化和销毁时候执行实现方式三、项目验证1.MyServiceImpl2.测试结果3. 项目源码 最近对同事代码进行codeReview时候发现用PostConstruct注解,特地对此注解执行顺序进行…...

用vite搭建vue3+ts项目

一、环境搭建 1:首先vite环境安装 npm init vite 或者 yarn init vite 2:输入项目的名字,在这里用vue3_test ? Project name: › vite-project 3:选择项目类型,这里选择Vuets 4:出现下图,初始化基本…...

前端求职指南

简历求职指南 为什么没有面试? 1、简历写的不好 2、简历投递不好 简历的定义是什么? 是求职者向未来雇主展示自己专业技能和职业素养的自我推销工具,以找到工作为目的。 什么时候改简历? 每半年或一年更新一次工作中的成长 再工…...

datax同步数据翻倍,.hive-staging 导致的问题分析

一、背景 有同事反馈 Datax 从 Hive 表同步数据到 Mysql 数据翻倍了。通过查看 Datax 任务日志发现,翻倍的原因是多读取了 .hive-staging_xx 开头的文件。接下里就是有关 .hive-staging 的分析。 二、环境 Hive 版本 2.1.1 三、分析 3.1 .hive-staging_hive 产…...

DataGrip 恢复数据

DataGrip 恢复数据 准备数据库 create database chap02; use chap02;恢复数据 运行sql脚本方式恢复数据到刚刚创建的数据库 数据右键—> SQL Scripts —> Run SQL Script… 选择sql脚本...

【深度学习实验】前馈神经网络(一):使用PyTorch构建神经网络的基本步骤

目录 一、实验介绍 二、实验环境 1. 配置虚拟环境 2. 库版本介绍 三、实验内容 0. 导入库 1. 定义x,w,b 2. 计算净活性值z 3. 实例化线性层并进行前向传播 4. 打印结果 5. 代码整合 一、实验介绍 本实验使用了PyTorch库来构建和操作神经网络模型,主要是关…...

【Unity】LODGroup 计算公式

Unity 在配置 LodGroup 时,其分级切换的计算方法是按照物体在相机视野中占据的比例计算的。在运行时,如果相机视野范围(Field of View)没有改变,那么这个值可以直接换算成物体距离相机的距离。这里就讨论下如何计算得到…...

【数学建模】2023华为杯研究生数学建模F题思路详解

强对流降水临近预报 我国地域辽阔,自然条件复杂,因此灾害性天气种类繁多,地区差异大。其中,雷雨大风、冰雹、龙卷、短时强降水等强对流天气是造成经济损失、危害生命安全最严重的一类灾害性天气[1]。以2022年为例,我国…...

docker 安装 nessus新版、awvs15-简单更快捷

一、docker 安装 nessus 参考项目地址: https://github.com/elliot-bia/nessus 介绍:几行代码即可一键安装更新 nessus -推荐 安装好 docker后执行以下命令 #拉取镜像创建容器 docker run -itd --nameramisec_nessus -p 8834:8834 ramisec/nessus …...

使用API Monitor工具巧妙探测C++程序中监听某端口的模块

目录 1、问题说明 2、API Monitor工具介绍 2.1、API Monitor主要用途 2.2、如何使用API Monitor工具 3、使用API Monitor监测程序对bind函数的调用,定位启用2620端口的模块 3.1、为啥要监控socket API函数bind 3.2、编写演示代码进行说明 3.3、使用API Moni…...

vue-grid-layout移动卡片到页面底部时页面滚动条跟随滚动

问题描述:默认情况下 vue-grid-layout 移动卡片到页面底部时页面滚动条并不会跟随卡片滚动。 问题解决: 在 grid-item中的move事件中,获取到当前移动的元素,并使用scrollIntoView方法来实现滚动条跟随。 代码如下: c…...

git查看自己所在的分支

很多时候可能大家不太想切换其他工具,又不知道自己是否在自己需要操作的分支 可以直接终端执行 git branch此时 他就会在终端将所有的本地分支输出出来 并特殊标注自己所在的分支 这样我们就可以进一步去做自己想要做的操作了 当然 随着各种编辑器的发展 这个命令…...

竞赛 基于视觉的身份证识别系统

0 前言 🔥 优质竞赛项目系列,今天要分享的是 基于机器视觉的身份证识别系统 该项目较为新颖,适合作为竞赛课题方向,学长非常推荐! 🧿 更多资料, 项目分享: https://gitee.com/dancheng-sen…...

Redis的softMinEvictableIdleTimeMillis和minEvictableIdleTimeMillis参数

背景: Redis的softMinEvictableIdleTimeMillis,minEvictableIdleTimeMillis是一个令人疑惑两个参数,特别是当它和minIdle组合起来时就更难理解了,本文就来梳理下他们的之间的关系 softMinEvictableIdleTimeMillis,mi…...

向量数据库库Milvus Cloud2.3 的QA问题

1. Milvus 从 2.2.x 升级至 2.3.x 的最大变化是什么? 如果用一句话来总结,那就是使用的场景更加丰富了。具体可以从两个方面来体现,即部署环境和用户的使用感。 例如,从部署环境来看,Milvus 原来只支持 X86 架构的 CPU,版本升级后,不仅可以支持 GPU,还能够支持 ARM 架构…...

嵌入式 - 经典的有刷电机和先进的无刷电机

自从无刷直流电机诞生,“古老的”有刷电机就开始没落,但它依然是低成本应用的可靠选择,并且实现起来简单。 在有刷电机中,磁极方向的跳转是通过移动固定位置的接触点来完成的,该接触点在电机转子上与电触点相对连接。这…...

第19节 Node.js Express 框架

Express 是一个为Node.js设计的web开发框架,它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用,和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...

在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module

1、为什么要修改 CONNECT 报文? 多租户隔离:自动为接入设备追加租户前缀,后端按 ClientID 拆分队列。零代码鉴权:将入站用户名替换为 OAuth Access-Token,后端 Broker 统一校验。灰度发布:根据 IP/地理位写…...

高等数学(下)题型笔记(八)空间解析几何与向量代数

目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...

相机从app启动流程

一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...

工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配

AI3D视觉的工业赋能者 迁移科技成立于2017年,作为行业领先的3D工业相机及视觉系统供应商,累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成,通过稳定、易用、高回报的AI3D视觉系统,为汽车、新能源、金属制造等行…...

ArcGIS Pro制作水平横向图例+多级标注

今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作:ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等(ArcGIS出图图例8大技巧),那这次我们看看ArcGIS Pro如何更加快捷的操作。…...

【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习

禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...

【分享】推荐一些办公小工具

1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由:大部分的转换软件需要收费,要么功能不齐全,而开会员又用不了几次浪费钱,借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...

【JVM面试篇】高频八股汇总——类加载和类加载器

目录 1. 讲一下类加载过程? 2. Java创建对象的过程? 3. 对象的生命周期? 4. 类加载器有哪些? 5. 双亲委派模型的作用(好处)? 6. 讲一下类的加载和双亲委派原则? 7. 双亲委派模…...

根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的----NTFS源代码分析--重要

根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的 第一部分: 0: kd> g Breakpoint 9 hit Ntfs!ReadIndexBuffer: f7173886 55 push ebp 0: kd> kc # 00 Ntfs!ReadIndexBuffer 01 Ntfs!FindFirstIndexEntry 02 Ntfs!NtfsUpda…...