十五、异常(4)

本章概要

  • Java 标志异常
    • 特例:RuntimeException
  • 使用 finally 进行清理
    • finally 用来做什么?
    • 在 return 中使用 finally
    • 缺憾:异常丢失

Java 标准异常

Throwable 这个 Java 类被用来表示任何可以作为异常被抛出的类。Throwable 对象可分为两种类型(指从 Throwable 继承而得到的类型):Error 用来表示编译时和系统错误(除特殊情况外,一般不用你关心);Exception 是可以被抛出的基本类型,在 Java 类库、用户方法以及运行时故障中都可能抛出 Exception 型异常。所以 Java 程序员关心的基类型通常是 Exception。要想对异常有全面的了解,最好去浏览一下 HTML 格式的 Java 文档(可以从 java.sun.com 下载)。为了对不同的异常有个感性的认识,这么做是值得的。但很快你就会发现,这些异常除了名称外其实都差不多。同时,Java 中异常的数目在持续增加,所以在书中简单罗列它们毫无意义。所使用的第三方类库也可能会有自己的异常。对异常来说,关键是理解概念以及如何使用。

基本理念是用异常的名称代表发生的问题。异常的名称应该可以望文知意。异常并非全是在 java.lang 包里定义的;有些异常是用来支持其他像 util、net 和 io 这样的程序包,这些异常可以通过它们的完整名称或者从它们的父类中看出端倪。比如,所有的输入/输出异常都是从 java.io.IOException 继承而来的。

特例:RuntimeException

在本章的第一个例子中:

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

如果必须对传递给方法的每个引用都检查其是否为 null(因为无法确定调用者是否传入了非法引用),这听起来着实吓人。幸运的是,这不必由你亲自来做,它属于 Java 的标准运行时检测的一部分。如果对 null 引用进行调用,Java 会自动抛出 NullPointerException 异常,所以上述代码是多余的,尽管你也许想要执行其他的检查以确保 NullPointerException 不会出现。

属于运行时异常的类型有很多,它们被 java 自动抛出,所以不必在异常说明中把它们列出来。非常方便的是,通过将这些异常设置为 RuntimeException的子类而把它们归类起来,这是继承的一个绝佳例子:建立具有相同特征和行为的一组类型。

RuntimeException 代表的是编程错误:

  1. 无法预料的错误。比如从你控制范围之外传递进来的 null 引用。
  2. 作为程序员,应该在代码中进行检查的错误。(比如对于 ArrayIndexOutOfBoundsException,就得注意一下数组的大小了。)在一个地方发生的异常,常常会在另一个地方导致错误。

在这些情况下使用异常很有好处,它们能给调试带来便利。

如果不捕获这种类型的异常会发生什么事呢?因为编译器没有在这个问题上对异常说明进行强制检查,RuntimeException 类型的异常也许会穿越所有的执行路径直达 main() 方法,而不会被捕获。要明白到底发生了什么,可以试试下面的例子:

// exceptions/NeverCaught.java
// Ignoring RuntimeExceptions
// {ThrowsException}
public class NeverCaught {static void f() {throw new RuntimeException("From f()");}static void g() {f();}public static void main(String[] args) {g();}
}

输出结果为:

在这里插入图片描述

如果 RuntimeException 没有被捕获而直达 main(),那么在程序退出前将调用异常的 printStackTrace() 方法。

你会发现,RuntimeException(或任何从它继承的异常)是一个特例。对于这种异常类型,编译器不需要异常说明,其输出被报告给了 System.err。

请务必记住:代码中只有 RuntimeException(及其子类)类型的异常可以被忽略,因为编译器强制要求处理所有受检查类型的异常。

值得注意的是:不应把 Java 的异常处理机制当成是单一用途的工具。是的,它被设计用来处理一些烦人的运行时错误,这些错误往往是由代码控制能力之外的因素导致的;然而,它对于发现某些编译器无法检测到的编程错误,也是非常重要的。

使用 finally 进行清理

有一些代码片段,可能会希望无论 try 块中的异常是否抛出,它们都能得到执行。这通常适用于内存回收之外的情况(因为回收由垃圾回收器完成),为了达到这个效果,可以在异常处理程序后面加上 finally 子句。完整的异常处理程序看起来像这样:

try {
// The guarded region: Dangerous activities
// that might throw A, B, or C
} catch(A a1) {
// Handler for situation A
} catch(B b1) {
// Handler for situation B
} catch(C c1) {
// Handler for situation C
} finally {
// Activities that happen every time
}

为了证明 finally 子句总能运行,可以试试下面这个程序:

// exceptions/FinallyWorks.java
// The finally clause is always executed
class ThreeException extends Exception {
}public class FinallyWorks {static int count = 0;public static void main(String[] args) {while (true) {try {// Post-increment is zero first time:if (count++ == 0) {throw new ThreeException();}System.out.println("No exception");} catch (ThreeException e) {System.out.println("ThreeException");} finally {System.out.println("In finally clause");if (count == 2) {break; // out of "while"}}}}
}

输出为:

在这里插入图片描述

从输出中发现,无论异常是否被抛出,finally 子句总能被执行。这也为解决 Java 不允许我们回到异常抛出点这一问题,提供了一个思路。如果将 try 块放在循环里,就可以设置一种在程序执行前一定会遇到的异常状况。还可以加入一个 static 类型的计数器或者别的装置,使循环在结束以前能尝试一定的次数。这将使程序的健壮性更上一个台阶。

finally 用来做什么?

对于没有垃圾回收和析构函数自动调用机制的语言来说,finally 非常重要。它能使程序员保证:无论 try 块里发生了什么,内存总能得到释放。但 Java 有垃圾回收机制,所以内存释放不再是问题。而且,Java 也没有析构函数可供调用。那么,Java 在什么情况下才能用到 finally 呢?

当要把除内存之外的资源恢复到它们的初始状态时,就要用到 finally 子句。这种需要清理的资源包括:已经打开的文件或网络连接,在屏幕上画的图形,甚至可以是外部世界的某个开关,如下面例子所示:

Switch.java

// exceptions/Switch.java
public class Switch {private boolean state = false;public boolean read() {return state;}public void on() {state = true;System.out.println(this);}public void off() {state = false;System.out.println(this);}@Overridepublic String toString() {return state ? "on" : "off";}
}

OnOffSwitch.java

// exceptions/OnOffSwitch.java
// Why use finally?
public class OnOffSwitch {private static Switch sw = new Switch();public static void f()throws OnOffException1, OnOffException2 {}public static void main(String[] args) {try {sw.on();// Code that can throw exceptions...f();sw.off();} catch (OnOffException1 e) {System.out.println("OnOffException1");sw.off();} catch (OnOffException2 e) {System.out.println("OnOffException2");sw.off();}}
}

OnOffException2.java

// exceptions/OnOffException2.java
public class OnOffException2 extends Exception {
}

OnOffException1.java

// exceptions/OnOffException1.java
public class OnOffException1 extends Exception {
}

输出为:

在这里插入图片描述

程序的目的是要确保 main() 结束的时候开关必须是关闭的,所以在每个 try 块和异常处理程序的末尾都加入了对 sw.off() 方法的调用。但也可能有这种情况:异常被抛出,但没被处理程序捕获,这时 sw.off() 就得不到调用。但是有了 finally,只要把 try 块中的清理代码移放在一处即可:

// exceptions/WithFinally.java
// Finally Guarantees cleanup
public class WithFinally {static Switch sw = new Switch();public static void main(String[] args) {try {sw.on();// Code that can throw exceptions...OnOffSwitch.f();} catch (OnOffException1 e) {System.out.println("OnOffException1");} catch (OnOffException2 e) {System.out.println("OnOffException2");} finally {sw.off();}}
}

输出为:

在这里插入图片描述

这里 sw.off() 被移到一处,并且保证在任何情况下都能得到执行。

甚至在异常没有被当前的异常处理程序捕获的情况下,异常处理机制也会在跳到更高一层的异常处理程序之前,执行 finally 子句:

// exceptions/AlwaysFinally.java
// Finally is always executed
class FourException extends Exception {
}public class AlwaysFinally {public static void main(String[] args) {System.out.println("Entering first try block");try {System.out.println("Entering second try block");try {throw new FourException();} finally {System.out.println("finally in 2nd try block");}} catch (FourException e) {System.out.println("Caught FourException in 1st try block");} finally {System.out.println("finally in 1st try block");}}
}

输出为:

在这里插入图片描述

当涉及 break 和 continue 语句的时候,finally 子句也会得到执行。请注意,如果把 finally 子句和带标签的 break 及 continue 配合使用,在 Java 里就没必要使用 goto 语句了。

在 return 中使用 finally

因为 finally 子句总是会执行,所以可以从一个方法内的多个点返回,仍然能保证重要的清理工作会执行:

// exceptions/MultipleReturns.java
public class MultipleReturns {public static void f(int i) {System.out.println("Initialization that requires cleanup");try {System.out.println("Point 1");if (i == 1) {return;}System.out.println("Point 2");if (i == 2) {return;}System.out.println("Point 3");if (i == 3) {return;}System.out.println("End");return;} finally {System.out.println("Performing cleanup");}}public static void main(String[] args) {for (int i = 1; i <= 4; i++) {f(i);}}
}

输出为:

在这里插入图片描述

从输出中可以看出,从何处返回无关紧要,finally 子句永远会执行。

缺憾:异常丢失

遗憾的是,Java 的异常实现也有瑕疵。异常作为程序出错的标志,决不应该被忽略,但它还是有可能被轻易地忽略。用某些特殊的方式使用 finally 子句,就会发生这种情况:

// exceptions/LostMessage.java
// How an exception can be lost
class VeryImportantException extends Exception {@Overridepublic String toString() {return "A very important exception!";}
}class HoHumException extends Exception {@Overridepublic String toString() {return "A trivial exception";}
}public class LostMessage {void f() throws VeryImportantException {throw new VeryImportantException();}void dispose() throws HoHumException {throw new HoHumException();}public static void main(String[] args) {try {LostMessage lm = new LostMessage();try {lm.f();} finally {lm.dispose();}} catch (VeryImportantException | HoHumException e) {System.out.println(e);}}
}

输出为:

在这里插入图片描述

从输出中可以看到,VeryImportantException 不见了,它被 finally 子句里的 HoHumException 所取代。这是相当严重的缺陷,因为异常可能会以一种比前面例子所示更微妙和难以察觉的方式完全丢失。相比之下,C++把“前一个异常还没处理就抛出下一个异常”的情形看成是糟糕的编程错误。也许在 Java 的未来版本中会修正这个问题(另一方面,要把所有抛出异常的方法,如上例中的 dispose() 方法,全部打包放到 try-catch 子句里面)。

一种更加简单的丢失异常的方式是从 finally 子句中返回:

// exceptions/ExceptionSilencer.java
public class ExceptionSilencer {public static void main(String[] args) {try {throw new RuntimeException();} finally {// Using 'return' inside the finally block// will silence any thrown exception.return;}}
}

如果运行这个程序,就会看到即使方法里抛出了异常,它也不会产生任何输出。

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

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

相关文章

C/C++笔试面试真题

C/C++笔试面试真题 1、堆和栈的区别 1、栈由系统自动分配,而堆是人为申请开辟; 2、栈获得的空间较小,而堆获得的空间较大; 3、栈由系统自动分配,速度较快,而堆一般速度比较慢; 4、栈是连续的空间,而堆是不连续的空间。 2、什么是野指针?产生的的原因? 野指针的指向的…

ubuntu下源码编译方式安装opencv

基础条件 ubuntu 20.04 opencv 3.4.3 opencv 源码编译的安装步骤 第一步&#xff0c; 首先clone源码 git clone https://github.com/opencv/opencv.git第二步&#xff0c;依赖包&#xff0c;执行下面的命令 sudo apt-get install build-essential sudo apt-get install cmak…

记一次Mybatis驼峰命名导致的线上BUG及处理方案

前言 方向从一开始就错了&#xff0c;还是执着的去寻找问题的解决方案&#xff0c;简直就是一场重大灾难&#xff0c;但这也是每个修行者的必由之路。这个线上问题&#xff0c;差点让我的心里防线崩溃&#xff0c;苦寻无门&#xff0c;最终得以解决也多亏了身边的各路大佬的群…

Android studio “Layout Inspector“工具在Android14 userdebug设备无法正常使用

背景描述 做rom开发的都知道&#xff0c;“Layout Inspector”和“Attach Debugger to Android Process”是studio里很好用的工具&#xff0c;可以用来查看布局、调试系统进程&#xff08;比如setting、launcher、systemui&#xff09;。 问题描述 最进刚开始一个Android 14…

数据结构与算法之堆: Leetcode 215. 数组中的第K个最大元素 (Typescript版)

数组中的第K个最大元素 https://leetcode.cn/problems/kth-largest-element-in-an-array/ 描述 给定整数数组 nums 和整数 k&#xff0c;请返回数组中第 k 个最大的元素。请注意&#xff0c;你需要找的是数组排序后的第 k 个最大的元素&#xff0c;而不是第 k 个不同的元素。…

Android Shape设置背景

设置背景时&#xff0c;经常这样 android:background“drawable/xxx” 。如果是纯色图片&#xff0c;可以考虑用 shape 替代。 shape 相比图片&#xff0c;减少资源占用&#xff0c;缩减APK体积。 开始使用。 <?xml version"1.0" encoding"utf-8"?…

云安全之HTTP协议介绍

HTTP的基本概念 什么是网络协议 网络协议是计算机之间为了实现网络通信而达成的一种“约定”或者”规则“&#xff0c;有了这种”约定不同厂商生产的设备&#xff0c;以及不同操作系统组成的计算机之间&#xff0c;就可以实现通信。 网络协议由三个要素构成&#xff1a;1、语…

WSL2和ubuntu的安装过程

目录 1.WSL2的安装 2.Ubuntu的安装 3.安装完成后的打开方式 1.WSL2的安装 按下WINX键&#xff0c;选择Windows PowerShell (管理员) 1.1执行以下命令&#xff0c;该命令的作用是&#xff1a;启用适用于 Linux 的 Windows 子系统 dism.exe /online /enable-feature /featur…

Oracle 快速入门

当你刚开始探索 Oracle 数据库时&#xff0c;可能会觉得有些复杂。然而&#xff0c;本文将为你提供 Oracle 数据库的快速入门指南&#xff0c;帮助你迅速上手这个强大的关系型数据库管理系统&#xff08;RDBMS&#xff09;。无论你是数据库新手还是有经验的数据库管理员&#x…

【小沐学前端】Node.js实现基于Protobuf协议的WebSocket通信

文章目录 1、简介1.1 Node1.2 WebSocket1.3 Protobuf 2、安装2.1 Node2.2 WebSocket2.2.1 nodejs-websocket2.2.2 ws 2.3 Protobuf 3、代码测试3.1 例子1&#xff1a;websocket&#xff08;html&#xff09;3.1.1 客户端&#xff1a;yxy_wsclient1.html3.1.2 客户端&#xff1a…

【踩坑日记】Docker elasticsearch too many open files问题处理

项目场景&#xff1a; 使用单机ES作为日志存储数据库&#xff0c;每日生成一个日期索引&#xff0c;由于每日的数据量可能较大&#xff0c;有时候需要进行磁盘扩容操作&#xff0c;本次问题记录还未找到根本的触发原因&#xff0c;后续找到原因后再进行记录 问题描述 每日创建…

利用mAP计算yolo精确度

当将yolo算法移植部署在嵌入式设备上&#xff0c;为了验证算法的准确率。将模型测试的结果保存为txt文件&#xff08;每一个txt文件&#xff0c;对应一个图片&#xff09;。此外&#xff0c;需要将数据集中的标签由[x,y,w,h]转为[x1,y1,x2,y2]。最后&#xff0c;运行验证代码 …

【redisson学习笔记】

1)clone项目 git clone https://github.com/redisson/redisson.git本来想直接用maven编译源码&#xff0c; 却发现各种错误&#xff0c;主要是maven的编译插件版本问题。 2)然后用maven包方式引入 <dependencies><dependency><groupId>org.redisson</gr…

Secureboot从入门到精通

关键词&#xff1a;trustzone视频、tee视频、ATF视频、secureboot视频、安全启动视频、selinux视频&#xff0c;cache视频、mmu视频&#xff0c;armv8视频、armv9视频 FF-A视频、密码学视频、RME/CCA视频、学习资料下载、免费学习资料、免费 周贺贺&#xff0c;baron&#xff0…

把握现在,热爱生活

博客主页&#xff1a;https://tomcat.blog.csdn.net 博主昵称&#xff1a;农民工老王 主要领域&#xff1a;Java、Linux、K8S 期待大家的关注&#x1f496;点赞&#x1f44d;收藏⭐留言&#x1f4ac; 目录 厨艺房价琐事计划随想 今年的中秋国庆假期放8天&#xff0c;比春节假期…

【RuoYi项目分析】网关的AuthFilter完成“认证”,注意是认证而不是权限

文章目录 1. 功能介绍2. AuthFilter的配置3. AuthFilter实现分析4. 资料参考 过滤器的功能是检验经过网关的每一个请求&#xff0c;检查 token 中的信息是否有效。 注意是“认证检查”&#xff0c;而不是“权限” 1. 功能介绍 1、在用户完成登录后&#xff0c;程序会把用户相关…

Centos7配置firewalld防火墙规则

这里写自定义目录标题 欢迎使用Markdown编辑器一、简单介绍二、特点和功能2.1、区域&#xff08;Zone&#xff09;2.2、运行时和永久配置2.3、服务和端口2.4、动态更新2.5、连接跟踪2.6、D-Bus接口 三、设置规则3.1、启动防火墙服务3.2、新建防火墙规则的服务&#xff0c;添加端…

分布式链路追踪--SkyWalking7.0.0+es7.0.0

分布式链路追踪–SkyWalking ​ 微服务的出现&#xff0c;的确解决了一些业务痛点&#xff0c;但是也造成了新的问题比如随着调用链的拉长&#xff0c;如果想要知道请求为什么这么慢&#xff0c;这个请求到底经历了哪些环节&#xff0c;又依赖了哪些东西&#xff0c;在微服务架…

亲测可用国产GPT人工智能

分享一些靠谱、可用、可以白嫖的GPT大模型。配合大模型&#xff0c;工作效率都会极大提升。 清华大学ChatGLM 官网&#xff1a; 智谱清言中国版对话语言模型&#xff0c;与GLM大模型进行对话。https://chatglm.cn/开源的、支持中英双语的1300亿参数的对话语言模型&#xff0…

【MATLAB】字体美化和乱码

文章目录 前言首先说说说字体美化乱码到底是怎么导致的&#xff1f;1 字体导致的乱码2 编码导致的乱码总结 前言 最近打开MATLAB&#xff0c;又发现了一个问题&#xff1a;编辑器中的中文输入在命令行或者说终端输出竟然是乱码&#xff0c;然后赶紧翻阅了一下此前的博客以及未发…