Java中的程序异常处理介绍

一、异常处理机制

Java提供了更加优秀的解决办法:异常处理机制。

异常处理机制能让程序在异常发生时,按照代码的预先设定的异常处理逻辑,针对性地处理异常,让程序尽最大可能恢复正常并继续执行,且保持代码的清晰。

Java中的异常可以是函数中的语句执行时引发的,也可以是程序员通过throw 语句手动抛出的,只要在Java程序中产生了异常,就会用一个对应类型的异常对象来封装异常,JRE就会试图寻找异常处理程序来处理异常。

Throwable类是Java异常类型的顶层父类,一个对象只有是 Throwable 类的(直接或者间接)实例,他才是一个异常对象,才能被异常处理机制识别。JDK中内建了一些常用的异常类,我们也可以自定义异常。

二、Java异常的分类和类结构图

在 Java 中,所有的异常都有一个共同的祖先java.lang包中的 Throwable类。Throwable: 有两个重要的子类:Exception(异常) 和 Error(错误) ,二者都是 Java 异常处理的重要子类,各自都包含大量子类。

Error(错误):这种类型的错误是程序无法处理的错误,表示运行应用程序中较严重问题。大多数错误与代码编写者执行的操作无关,而表示代码运行时 JVM(Java 虚拟机)出现的问题。例如,Java虚拟机运行错误(Virtual MachineError),当 JVM 不再有继续执行操作所需的内存资源时,将出现 OutOfMemoryError。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止。

Exception(异常):这种类型的错误是程序本身可以处理的异常。Exception 类有一个重要的子类 RuntimeException。RuntimeException 异常由Java虚拟机抛出。NullPointerException(要访问的变量没有引用任何对象时,抛出该异常)、ArithmeticException(算术运算异常,一个整数除以0时,抛出该异常)和 ArrayIndexOutOfBoundsException (下标越界异常)。

注意:异常和错误的区别:异常能被程序本身可以处理,错误是无法处理。

总体上我们根据Javac对Exception异常的处理要求,将异常类分为2类。

2.1、检查异常(IOException)

检查异常(checked exception):除了Error 和 RuntimeException的其它异常。javac强制要求程序员为这样的异常做预备处理工作(使用try…catch…finally或者throws)。在方法中要么用try-catch语句捕获它并处理,要么用throws子句声明抛出它,否则编译不会通过。这样的异常一般是由程序的运行环境导致的。因为程序可能被运行在各种未知的环境下,而程序员无法干预用户如何使用他编写的程序,于是程序员就应该为这样的异常时刻准备着。如SQLException , IOException,ClassNotFoundException 等。

2.2、非检查异常(RuntimeException)

非检查异常(unckecked exception):Error 和 RuntimeException 以及他们的子类。javac在编译时,不会提示和发现这样的异常,不要求在程序处理这些异常。所以如果愿意,我们可以编写代码处理(使用try…catch…finally)这样的异常,也可以不处理。对于这些异常,我们应该修正代码,而不是去通过异常处理器处理 。这样的异常发生的原因多半是代码写的有问题。如除0错误ArithmeticException,错误的强制类型转换错误ClassCastException,数组索引越界ArrayIndexOutOfBoundsException,使用了空对象NullPointerException等等。

三、初识异常

下面的代码会演示2个异常类型:ArithmeticException 和 InputMismatchException。前者由于整数除0引发,后者是输入的数据不能被转换为int类型引发。

package com.example;
import java. util .Scanner ;
public class AllDemo{public static void main (String [] args ){System . out. println( "----欢迎使用命令行除法计算器----" ) ;CMDCalculate ();}public static void CMDCalculate () {Scanner scan = new Scanner ( System. in );int num1 = scan .nextInt () ;int num2 = scan .nextInt () ;int result = devide (num1 , num2 ) ;System . out. println( "result:" + result) ;scan .close () ;}public static int devide (int num1, int num2 ){return num1 / num2 ;}
}

在控制台输入0,0

----欢迎使用命令行除法计算器----
0
0
Exception in thread "main" java.lang.ArithmeticException: / by zeroat com.example.java.execption.AllDemoExecption.devide(AllDemoExecption.java:22)at com.example.java.execption.AllDemoExecption.CMDCalculate(AllDemoExecption.java:16)at com.example.java.execption.AllDemoExecption.main(AllDemoExecption.java:9)

在控制台输入0,z

----欢迎使用命令行除法计算器----
0
z
Exception in thread "main" java.util.InputMismatchExceptionat java.util.Scanner.throwFor(Scanner.java:864)at java.util.Scanner.next(Scanner.java:1485)at java.util.Scanner.nextInt(Scanner.java:2117)at java.util.Scanner.nextInt(Scanner.java:2076)at com.example.java.execption.AllDemoExecption.CMDCalculate(AllDemoExecption.java:15)at com.example.java.execption.AllDemoExecption.main(AllDemoExecption.java:9)

异常是在执行某个函数时引发的,而函数又是层级调用,形成调用栈的,因为,只要一个函数发生了异常,那么他的所有的caller都会被异常影响。当这些被影响的函数以异常信息输出时,就形成的了异常追踪栈。

异常最先发生的地方,叫做异常抛出点。

从上面的例子可以看出,当devide函数发生除0异常时,devide函数将抛出ArithmeticException异常,因此调用他的CMDCalculate函数也无法正常完成,因此也发送异常,而CMDCalculate的caller——main 因为CMDCalculate抛出异常,也发生了异常,这样一直向调用栈的栈底回溯。这种行为叫做异常的冒泡,异常的冒泡是为了在当前发生异常的函数或者这个函数的caller中找到最近的异常处理程序。由于这个例子中没有使用任何异常处理机制,因此异常最终由main函数抛给JRE,导致程序终止。

上面的代码不使用异常处理机制,也可以顺利编译,因为2个异常都是非检查异常。但是下面的例子就必须使用异常处理机制,因为异常是检查异常。

代码中我选择使用throws声明异常,让函数的调用者去处理可能发生的异常。但是为什么只throws了IOException呢?因为FileNotFoundException是IOException的子类,在处理范围内。

@Test
public void testException() throws IOException
{//FileInputStream的构造函数会抛出FileNotFoundExceptionFileInputStream fileIn = new FileInputStream("E:\\a.txt");int word;//read方法会抛出IOExceptionwhile((word =  fileIn.read())!=-1) {System.out.print((char)word);}//close方法会抛出IOExceptionfileIn.clos
}

四、异常处理的基本语法

在编写代码处理异常时,对于检查异常,有2种不同的处理方式:使用try…catch…finally语句块处理它。或者,在函数签名中使用throws 声明交给函数调用者caller去解决。

4.1、try…catch…finally语句块
try{//try块中放可能发生异常的代码。//如果执行完try且不发生异常,则接着去执行finally块和finally后面的代码(如果有的话)。//如果发生异常,则尝试去匹配catch块。}catch(SQLException e){//每一个catch块用于捕获并处理一个特定的异常,或者这异常类型的子类。Java7中可以将多个异常声明在一个catch中。//catch后面的括号定义了异常类型和异常参数。如果异常与之匹配且是最先匹配到的,则虚拟机将使用这个catch块来处理异常。//在catch块中可以使用这个块的异常参数来获取异常的相关信息。异常参数是这个catch块中的局部变量,其它块不能访问。//如果当前try块中发生的异常在后续的所有catch中都没捕获到,则先去执行finally,然后到这个函数的外部caller中去匹配异常处理器。//如果try中没有发生异常,则所有的catch块将被忽略。}catch(Exception exception){//...
}finally{//finally块通常是可选的。//无论异常是否发生,异常是否匹配被处理,finally都会执行。//一个try至少要有一个catch块,否则, 至少要有1个finally块。但是finally不是用来处理异常的,finally不会捕获异常。//finally主要做一些清理工作,如流的关闭,数据库连接的关闭等。 
}

需要注意的地方

  • try 块:用于捕获异常。其后可接零个或多个catch块,如果没有catch块,则必须跟一个finally块。
  • catch 块:用于处理try捕获到的异常。
  • finally 块:无论是否捕获或处理异常,finally块里的语句都会被执行。当在try块或catch块中遇到return语句时,finally语句块将在方法返回之前被执行。

在以下4种特殊情况下,finally块不会被执行:

  • 在finally语句块第一行发生了异常。 因为在其他行,finally块还是会得到执行
  • 在前面的代码中用了System.exit(int)已退出程序。 exit是带参函数 ;若该语* 句在异常语句之后,finally会执行
  • 程序所在的线程死亡。
  • 关闭CPU。

如果try语句里有return,返回的是try语句块中变量值。 详细执行过程如下:

  • 如果有返回值,就把返回值保存到局部变量中;
  • 执行jsr指令跳到finally语句里执行;
  • 执行完finally语句后,返回之前保存在局部变量表里的值。
  • 如果try,finally语句里均有return,忽略try的return,而使用finally的return。
4.2、throws 函数声明

throws声明:如果一个方法内部的代码会抛出检查异常(checked exception),而方法自己又没有完全处理掉,则javac保证你必须在方法的签名上使用throws关键字声明这些可能抛出的异常,否则编译不通过。

throws是另一种处理异常的方式,它不同于try…catch…finally,throws仅仅是将函数中可能出现的异常向调用者声明,而自己则不具体处理。

采取这种异常处理的原因可能是:方法本身不知道如何处理这样的异常,或者说让调用者处理更好,调用者需要为可能发生的异常负责。

public void foo() throws ExceptionType1 , ExceptionType2 ,ExceptionTypeN{ //foo内部可以抛出 ExceptionType1 , ExceptionType2 ,ExceptionTypeN 类的异常,或者他们的子类的异常对象。
}
4.3、finally块

inally块不管异常是否发生,只要对应的try执行了,则它一定也执行。只有一种方法让finally块不执行:System.exit()。因此finally块通常用来做资源释放操作:关闭文件,关闭数据库连接等等。

良好的编程习惯是:在try块中打开资源,在finally块中清理释放这些资源。

需要注意的地方:

1、finally块没有处理异常的能力。处理异常的只能是catch块。

2、在同一try…catch…finally块中 ,如果try中抛出异常,且有匹配的catch块,则先执行catch块,再执行finally块。如果没有catch块匹配,则先执行finally,然后去外面的调用者中寻找合适的catch块。

3、在同一try…catch…finally块中 ,try发生异常,且匹配的catch块中处理异常时也抛出异常,那么后面的finally也会执行:首先执行finally块,然后去外围调用者中寻找合适的catch块。

这是正常的情况,但是也有特例。关于finally有很多恶心,偏、怪、难的问题,我在本文最后统一介绍了。

4.4、throw 异常抛出语句

throw exceptionObject

程序员也可以通过throw语句手动显式的抛出一个异常。throw语句的后面必须是一个异常对象。

throw 语句必须写在函数中,执行throw 语句的地方就是一个异常抛出点,它和由JRE自动形成的异常抛出点没有任何差别。

public void save(User user){if(user  == null) throw new IllegalArgumentException("User对象为空");//......
}

五、自定义异常

如果要自定义异常类,则扩展Exception类即可,因此这样的自定义异常都属于检查异常(checked exception)。如果要自定义非检查异常,则扩展自RuntimeException。

按照国际惯例,自定义的异常应该总是包含如下的构造函数:

  • 一个无参构造函数
  • 一个带有String参数的构造函数,并传递给父类的构造函数。
  • 一个带有String参数和Throwable参数,并都传递给父类构造函数
  • 一个带有Throwable 参数的构造函数,并传递给父类的构造函数。

下面是IOException类的完整源代码,可以借鉴。

public class IOException extends Exception{static final long serialVersionUID = 7818375828146090155L;public IOException(){super();}public IOException(String message){super(message);}public IOException(String message, Throwable cause){super(message, cause);}public IOException(Throwable cause){super(cause);}
}

写到最后

不会有人刷到这里还想白嫖吧?点赞对我真的非常重要!在线求赞。加个关注我会非常感激!

本文已整理到技术笔记中,此外,笔记内容还涵盖 Spring、Spring Boot/Cloud、Dubbo、JVM、集合、多线程、JPA、MyBatis、MySQL、微服务等技术栈。

需要的小伙伴可以点击 技术笔记 获取!

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

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

相关文章

航天航空零部件装配制造MES系统解决方案详解

航天航空零部件制造行业是一个技术密集、工艺复杂且对精度和可靠性要求极高的行业。为了提升生产效率、保证产品质量并满足严格的行业标准,越来越多的航天航空零部件制造企业引入了MES系统。本文将详细介绍MES系统在航天航空零部件制造行业的应用方法及其价值。 一…

git 初基本使用-----------笔记(结合idea)

Git命令 下载git 打开Git官网(git-scm.com),根据自己电脑的操作系统选择相应的Git版本,点击“Download”。 基本的git命令使用 可以在项目文件下右击“Git Bash Here” ,也可以命令终端下cd到指定目录执行初始化命令…

监控员工电脑的软件有哪些?6款企业必备的电脑监控软件

监控员工电脑的软件在企业管理和网络安全领域扮演着重要角色,它们可以帮助企业提高工作效率,确保数据安全,以及合规性。以下是六款知名的员工电脑监控软件: 1.安企神 - 一个全面的企业级电脑监控和管理解决方案。 2.Work Examine…

【unity实战】Unity中基于瓦片的网格库存系统——类似《逃离塔科夫》的库存系统

最终效果 文章目录 最终效果前言素材下载图片配置获取格子坐标动态控制背包大小添加物品移动物品物品跟随鼠标创建物品的容器,定义不同物品修改物品尺寸修复物品放置位置问题按物品尺寸占用对应大小的格子判断物品是否超出边界范围物品放置重叠,交换物品…

python API自动化(基于Flask搭建MockServer)

接口Mock的理念与实战场景: 什么是Mock: 在接口中,"mock"通常是指创建一个模拟对象来代替实际的依赖项,以便进行单元测试。当一个类或方法依赖于其他类或组件时,为了测试这个类或方法的功能,我们可以使用模拟对象来替代…

uni-app与原生插件混合开发调试1-环境准备

uni-app与原生插件混合开发调试系列文章分为3篇,分别详细讲了《环境准备》、《搭建uni-app本地开发调试环境》和《安卓原生插件开发调试和打包》,3篇文章完整详细地介绍了“从环境安装配置到本地开发调试到原生插件打包”整个流程。 相关名词和概念解释…

WPS-Word文档表格分页

一、问题描述 这种情况不好描述 就是像这种表格内容,但是会有离奇的分页的情况。这种情况以前的错误解决办法就是不断地调整表格的内容以及间隔显得很乱,于是今天去查了解决办法,现在学会了记录一下避免以后忘记了。 二、解决办法 首先记…

14、电科院FTU检测标准学习笔记-录波功能2

作者简介: 本人从事电力系统多年,岗位包含研发,测试,工程等,具有丰富的经验 在配电自动化验收测试以及电科院测试中,本人全程参与,积累了不少现场的经验 ———————————————————…

ONLYOFFICE 桌面编辑器 8.1 版发布:全面提升文档处理效率的新体验

文章目录 什么是ONLYOFFICE ?ONLYOFFICE 桌面编辑器 8.1 发布:新功能和改进功能强大的 PDF 编辑器幻灯片版式功能从右至左语言支持多媒体功能增强无缝切换工作模式其他改进和优化总结 什么是ONLYOFFICE ? https://www.onlyoffice.com/zh/off…

【Web APIs】JavaScript 事件基础 ② ( “ 事件 “ 开发步骤 | 常见鼠标 “ 事件 “ )

文章目录 一、" 事件 " 开发步骤1、" 事件 " 开发步骤2、完整代码示例 二、常见鼠标 " 事件 "1、常见鼠标 " 事件 "2、鼠标 " 事件 " 代码示例 Web APIs 博客相关参考文档 : WebAPIs 参考文档 : https://developer.mozilla…

代码随想录-Day42

1049. 最后一块石头的重量 II 有一堆石头&#xff0c;用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。 每一回合&#xff0c;从中选出任意两块石头&#xff0c;然后将它们一起粉碎。假设石头的重量分别为 x 和 y&#xff0c;且 x < y。那么粉碎的可能结果…

【软件测试】白盒测试与接口测试详解

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 一、什么是白盒测试 白盒测试是一种测试策略&#xff0c;这种策略允许我们检查程序的内部结构&a…

5.9k!一款清新好用的后台管理系统!【送源码】

今天给大家分享的开源项目是一个优雅清新后台管理系统——Soybean Admin。 简介 官方是这样介绍这个项目的&#xff1a; Soybean Admin 使用的是Vue3作为前端框架&#xff0c;TypeScript作为开发语言&#xff0c;同时还整合了NaiveUI组件库&#xff0c;使得系统具有高可用性和…

Vue3.3 的 defineOptions 的使用,方便在 setup 语法糖中为组件命名和控制父子属性透传,包含在线运行实例欧

defineOptions 是 Vue3.3 的新的宏&#xff0c;可以通过 defineOptions 宏在 <script setup> 中使用选项式 API&#xff0c;也就是说可以在一个宏函数中设置 name, props, emits, render, 控制是否允许父子非 props 的属性透传等功能。 defineOptions 可以直接在 setup …

[数据集][目标检测]花生米计数霉变检测数据集VOC+YOLO格式387张2类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;387 标注数量(xml文件个数)&#xff1a;387 标注数量(txt文件个数)&#xff1a;387 标注类别…

pycharm terminal终端不能激活 conda 虚拟环境,解决方法

# 1. 确保执行策略已更改 Set-ExecutionPolicy RemoteSigned -Scope CurrentUser# 2. 初始化Conda conda init powershell# 3. 重启PowerShell# 4. 验证Conda初始化 conda --version# 5. 激活Conda环境 conda activate shi_labelme关闭所有的终端&#xff0c;然后重新打开新的终…

【图像分类】Yolov8 完整教程 |分类 |计算机视觉

目标&#xff1a;用YOLOV8进行图像分类。 图像分类器。 学习资源&#xff1a;https://www.youtube.com/watch?vZ-65nqxUdl4 努力的小巴掌 记录计算机视觉学习道路上的所思所得。 1、文件结构化 划分数据集&#xff1a;train,val,test 知道怎么划分数据集很重要。 文件夹…

Linux系统相关函数总结

在应用程序当中&#xff0c;有时往往需要去获取到一些系统相关的信息&#xff0c;譬如时间、日期、以及其它一些系统相关信息&#xff0c;本章将向大家介绍如何通过 Linux 系统调用或 C 库函数获取这些系统信息。除此之外&#xff0c;还会向大家介绍 Linux 系统下的/proc 虚拟文…

Day.js

Day.js 是什么&#xff1f; Day.js是一个极简的JavaScript库&#xff0c;可以为现代浏览器解析、验证、操作和显示日期和时间。 Day.js中文网 为什么要使用Day.js &#xff1f; 因为Day.js文件只有2KB左右&#xff0c;下载、解析和执行的JavaScript更少&#xff0c;为代码留下更…

高考志愿不知道怎么填?教你1招,用这款AI工具,立省4位数

高中的岁月&#xff0c;就像一本厚厚的书&#xff0c;我们一页页翻过&#xff0c;现在&#xff0c;终于翻到了最后一页。但这不是结束&#xff0c;这是新的开始&#xff0c;是人生的新篇章。 高考落幕&#xff0c;学子们在短暂的放松后&#xff0c;又迎来了紧张的志愿填报。 “…