Java异常体系总结(下篇)

news2024/10/5 15:23:08

目录

1. 异常处理的三种方法

1.1 JVM 默认处理异常

1.2 通过 try...catch...自己处理异常

1.3 使用 throws和throw 抛出异常

1.3.1 使用 throws 抛出异常

1.3.2 使用 throw 抛出异常

2. try...catch.. 捕获到异常之后代码的执行顺序?

3. try...catch... 相关的四个面试题

3.1 如果 try...catch... 中没有出现异常,程序如何执行?

3.2 如果 try...catch... 中出现多个异常,程序会怎么执行?

3.3 如果 try... catch... 中遇到的异常没有被捕获,该怎么执行?

3.4 如果 try 中遇到了问题,那么下面的其他代码还会执行吗?

4. 什么时候使用 try...catch... ?什么时候使用 throws或 throw?


1. 异常处理的三种方法

上篇重点讲解了 Java 异常体系家族的分类,分析了运行时异常和编译时异常的由来,又举了几个例子,那么本片着重讲解 Java 异常的三种处理方式以及它们的使用细节。

在Java中,对于程序出现的异常,我们有以下三种解决方案

1.1 JVM 默认处理异常

如果我们对异常不做任何处理的话,Java底层会使用JVM虚拟机默认的异常处理方式,当程序出现异常时,JVM虚拟机会把异常的名称,异常的信息以及异常出现的位置输出在控制台;

此外,一旦程序出现异常,JVM虚拟机就会立即停止程序,出现异常的代码下方的代码都不会执行。

如下代码,我简单写一个数组越界异常,使用JVM默认的异常处理方式,来看看它会怎么做

public static void main(String[] args) {
        int[] arr = new int[3];
        arr[0] = 0;
        arr[1] = 1;
        arr[2] = 2;
        arr[3] = 3;
        System.out.println("看看我会执行吗");
    }

运行上述代码,可以在控制台得到如下结果

我们可以看到,System.out.println("看看我会执行吗")这一行代码并没有打印出来;

在控制台中,红色语句便是异常的描述

Exception in thread "main" 表示异常出现在在线程 main 中,也就是我们所编写的 main 方法;

java.lang.ArrayIndexOutOfBoundsException 表示我们出现的异常的名称,翻译过来就是数组越界异常;

at 是英语单词,意思是在;

cn.itcast.user.pojo.Test01.main 表示的是我们项目中的 包名 + 类名 + 方法名;

括号内蓝色字体 Test01.java:12 表示异常出现在Test01.java 类中的第十二行;

把整个异常信息综合起来意思就是说,"包名为cn.itcast.user.pojo类名为Test01.java 的main 方法的第十二行出现了 ArrayIndexOutOfBoundsException 数组越界异常";

还有一些情况,可能会出现多个异常,这种情况下,我们应该从下往上读,这里我就不展示了。

1.2 通过 try...catch...自己处理异常

如果我们不采用JVM默认的异常处理机制,我们就可以自己手动添加对异常的处理,而try...catch...就是方法之一,使用格式如下图所示

在 try 语句块中,我们需要将可能出现异常的代码写在括号内,然后在 catch 语句块中,我们需要写对于异常出现时的处理逻辑。

如果我们使用 try...catch... 语句块处理异常,那么当程序出现异常时,程序不会立即停止,而是继续向下将代码执行完,我们仍然拿刚才的的代码举例,选中刚才出现异常的哪一行代码,我们按快捷键 Ctrl + Alt + t,就会出现像屏幕中这样的提示,我们点击 try/catch 环绕,当然也可以自己手动敲,都可以,懒得写的同学可以记住这个快捷键,挺方便的

环绕完成之后,就会变成下图这样的结果

我们再次运行 main 方法,在控制台可以发现,刚才采用JVM默认处理机制没有运行的这行代码在加入 try...catch.. 语句块之后也成功运行了。

catch 语句块中的额 e.pritStackTrace 下面会说。

try...catch..语句快的好处就是,当程序出现异常时,不会马上停止,而是会继续向下执行将程序运行完毕。

1.3 使用 throws和throw 抛出异常

上面我们可以使用 try..catch...包裹代码的方式捕获异常,这里我们还可以使用关键字 throws或者throw 抛出异常

1.3.1 使用 throws 抛出异常

throws 关键字你是在写在方法定义处,表明声明一个异常,也可以写多个异常,使用格式如下所示

当我们定义一个方法时,如果方法中出现异常,但是又不想使用 try...catch... 进行捕获,我们此时就可以使用 throws,在方法声明处将该方法中可能出现的异常类声明并抛出。

throws 关键字在抛出异常时还有一个点需要注意,如果说出现的异常是编译时异常,必须抛出,如果是运行时异常,也可以选择不抛出。

例子如下,我们定义一个文件输入流对象,这里IDEA爆红了,提示我们有异常需要处理

因为这里有可能我们定义的文件不存在,我们就可以使用 throws 将异常抛出,直接写在方法上,如下所示,我们还可以写多个,中间用 "," 隔开

1.3.2 使用 throw 抛出异常

throw 关键字则使用在方法的内部,可以用来结束方法,它可以手动抛出异常对象,交给调用者,一旦抛出异常对象,那么下方的代码就不会再继续执行了,使用方法如下图所示

代码如下

public static void main(String[] args) {
        // 定义一个空数组
        int[] arr = null;
        // 先对数组做一个判空操作,如果为空抛出异常
        if (arr == null){
            // 数组为空,抛出异常
            throw new NullPointerException("空指针异常");
        }
        // 因为 throw 抛出异常,所以下方的打印语句不会执行
        System.out.println("这句话根本不会执行");
    }

运行上方代码,可以在控制台得到如下结果

可以看到,我们定义的"空指针异常"输出,说明该异常被抛出来了,而且下方的输出语句也没有执行。说明 throw 抛出异常时会直接结束方法的运行。

2. try...catch.. 捕获到异常之后代码的执行顺序?

当我们使用 try...catch... 捕获异常之后,其语句块内代码是有一定的执行顺序的,如下代码

public static void main(String[] args) {
        int[] arr = new int[3];
        arr[0] = 0;
        arr[1] = 1;
        arr[2] = 2;
        // 第一步,开始进入 try...catch...语句块
        try {
            // 第二步,执行此行代码,发现此处代码有异常,底层会创建出一个此异常的对象,也就是
            // ArrayIndexOutOfBoundsException 数组越界的对象
            arr[3] = 3;
            // 第三步,将刚才创建的对象与 catch 括号内的对象中做比较,看看能否接收
        } catch (Exception e) {
            // 第四步:若接收成功,就会执行 catch 语句块内部的这一行代码
            // 若接受不成功,不会执行这一行代码,程序直接跳出catch 语句块并不再向下执行,程序停止
            e.printStackTrace();
        }
        System.out.println("看看我会执行吗");
    }

使用 try...catch... 捕获异常,我们通常会对异常做处理,上述代码中,括号内创建了 Exception 的对象 e,这里是可以的,因为Exception 是所有异常类的父类,当出现异常产生异常对象时

底层就会出现 Exception e = new 异常对象,这是一种多态的写法,各位应该能看得懂,当然如果你在编写代码的时候就能确定代码会出现哪种类型的异常,也可以直接精确的写成 Exception 的子类,例如这里,知道它会产生数组越界异常,所以直接将 Exception 写成ArrayIndexOutOfBoundsException 也没有问题,其实不只是可以写成这两种,只要是产生的异常类的父类,都可以写在括号内接收子类异常对象。

3. try...catch... 相关的四个面试题

3.1 如果 try...catch... 中没有出现异常,程序如何执行?

我们来验证一下,如下代码

public static void main(String[] args) {

        // 第一步,开始进入 try...catch...语句块
        try {
            int[] arr = new int[3];
            arr[0] = 0;
            arr[1] = 1;
            arr[2] = 2;
            // 直接定义父类 Exception 接受可能出现异常的类对象
        } catch (Exception e) {
            // 自定义异常处理逻辑,打印一句话
            System.out.println("索引越界异常");
        }
        System.out.println("看看我会执行吗");
    }

 代码本身没有任何问题,我们直接运行,在控制台得到如下结果

可以看到,程序正常运行,没有任何问题

我们也可以就此得到结论,如果我们的 try...catch... 语句块中没有任何异常出现,程序会把 try 语句块中的代码全部执行完毕,不会执行 catch 语句块中的代码。

3.2 如果 try...catch... 中出现多个异常,程序会怎么执行?

如下代码,我定义两个异常,一个索引越界异常,一个算术运算异常,在 catch 中 我们也定义与之精准对应的两个 catch 异常类,运行代码看啊可能会有什么结果

public static void main(String[] args) {

        // 第一步,开始进入 try...catch...语句块
        try {
            int[] arr = new int[3];
            arr[0] = 0;
            arr[1] = 1;
            arr[2] = 2;
            // 制造一个索引越界异常
            arr[3] = 3;

            // 制造一个 除0 异常
            int a = 10/0;
        // 定义数组越界异常的catch语句块处理方案
        } catch (ArrayIndexOutOfBoundsException e) {
            // 自定义数组越界异常处理逻辑
            System.out.println("索引越界异常");
        // 定义算术运算异常的catch语句块处理方案
        }catch (ArithmeticException e){
            // 自定义除0异常处理逻辑
            System.out.println("算术运算异常");
        }
        System.out.println("看看我会执行吗");
    }

 运行此方法,我们在控制台得到如下结果,

可以看到控制台纸打印了"索引越界异常",说明算术运算异常的 catch 语句块根本就没有执行。

此外,有一点需要做补充,如果有多个异常可能出现,并且我们定义的 catch 语句块中异常类有父子继承关系,那么父类要写在子类的下边,不能写在子类的上边。看如下我演示的代码

可以看到,当我们定义了一个顶级父类异常Exception 之后,IDEA提示我们后面两个异常有错误,点过去 按Alt + Enter键,IDEA提示我们删除此异常,或者将定义的这个数组越界异常放在 Exception 异常之前。

原因是什么呢?

其实很简单,当我们程序出现问题之后,进入catch语句块,它还是会从上向下执行 catch 语句块,你现在把父类定义在了子类的前面,父类就可以把异常接受,那么子类存在的意义何在呢?因此要记住,当程序出现多个异常时,catch 语句块中父类异常不能在子类异常的前面,必须放在子类异常的后面。

由此我们可以得到以下结论:

如果 try 语句块中出现了多个问题,那么在出现第一个异常的时候,try 语句块中的代码就不会再继续向下执行了,程序会直接跳到 catch 语句块中,判断出现的异常与哪个异常对象相匹配,就会执行 catch 语句块中的内容,如果没有与之相匹配的异常对象,程序会报错。当程序出现多个异常时,catch 语句块中父类异常不能在子类异常的前面,必须放在子类异常的后面。

3.3 如果 try... catch... 中遇到的异常没有被捕获,该怎么执行?

接着刚才的例子,如果我 catch 语句块修改一下,如下代码,程序首先会出现数组越界异常,但在下方 catch 语句中我没有对出现的异常进行捕获,再次运行代码,在控制台得到如下结果

可以看到,程序报错了,但还是只报了数组越界异常,我们定义的算术运算异常并没有显示出来,没有执行算术运算一场语句块中的 sout 输出语句。

由此得出结论,当我们程序爆出的异常没有被 catch 语句块捕获时,会采用JVM默认的异常捕获机制捕获,也就是说,异常没有被捕获,tyr...catch... 代码块无效,白写了。

3.4 如果 try 中遇到了问题,那么下面的其他代码还会执行吗?

如下代码,我在出现异常的语句下方打印一句话,看看是否会执行

public static void main(String[] args) {

        // 第一步,开始进入 try...catch...语句块
        try {
            int[] arr = new int[3];
            arr[0] = 0;
            arr[1] = 1;
            arr[2] = 2;
            // 制造一个索引越界异常
            arr[3] = 3;
            // 打印一句话
            System.out.println("这句话根本不会执行");
        // 定义数组越界异常的catch语句块处理方案
        } catch (ArrayIndexOutOfBoundsException e) {
            // 自定义数组越界异常处理逻辑
            System.out.println("索引越界异常");
        }
    }

运行代码,得到如下结果

可以发现,我们定义在异常下方的输出语句根本没有执行,也证实了我们的结论

如果 try 语句中出现了问题,出现问题的代码下方的代码根本不会执行,它会直接进入到 catch 语句块中,如果catch 没有捕获该异常,还是会交给 JVM虚拟机去处理。

4. 什么时候使用 try...catch... ?什么时候使用 throws或 throw?

如果我们想要知道什么时候应该使用捕获,什么时候应该使用抛出,就要知道它们两个的核心思想。

捕获异常:让程序不要停止,一直运行下去;

抛出异常:告诉调用者出错了;

其实通过上面的讲解,我们也大概明白了,捕获异常是将遇到的异常自己解决,而抛出异常是将遇到的异常向上抛出,抛给该方法的调用者。

可以举个简短的例子,当你在工作的过程中遇到了很麻烦的一个问题,你既可以自己解决它,也可以向你的上级汇报,自己解决就好比是我们在代码中使用 try...catch...,汇报给上级就好比是throws与throw,上级可以继续throws给上级的上级,但是这个问题也不能一直向上抛吧!你见过主管会给老板汇报说工作遇到了大难题解决不了的吗?

这么说,各位应该明白了吧。

在开发过程中,当我的一个方法调用另一个方法时,如果被调用的方法可能出现异常,它就可以将异常抛给调用者,让调用者知并使用 try...catch... 将异常捕获,对异常加以处理,但如果是在第一调用者或者 main 方法中,或者我们想要执行完不中断的业务中,通常或进行 try...catch... 捕获处理,而不会继续向上抛。

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

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

相关文章

Mysql进阶(中) -- 索引

索引上部分 -> Mysql进阶(上) -- 存储引擎,索引_千帐灯无此声的博客-CSDN博客 👂 爸爸妈妈 - 王蓉 - 单曲 - 网易云音乐 👈目录看左栏 目录 🌼索引 🐻性能分析 - show profiles 🐻性能分析 - exp…

Cocos 适配 HarmonyOS NEXT,亮相 HDC2023,携手华为共筑鸿蒙生态!

HDC 2023 8月4-6日,作为华为合作伙伴,Cocos 引擎应邀参加了华为开发者大会 2023 - HDC 2023 暨 HarmonyOS 4 发布会,并获得了【鸿蒙生态能力共创奖】。 8月5日,在华为开发者大会(HDC.Together)游戏服务论坛…

SpringBoot系列---【使用jasypt把配置文件密码加密】

使用jasypt把配置文件密码加密 1.引入pom坐标 <dependency><groupId>com.github.ulisesbocchio</groupId><artifactId>jasypt-spring-boot-starter</artifactId><version>3.0.5</version> </dependency> 2.新增jasypt配置 2.1…

HCIP-linux知识

linux安装教程参考&#xff0c;https://blog.51cto.com/cloudcs/5245337 yum源配置 本地yum源配置&#xff1a; 8版本配置&#xff1a;将光盘iso挂载到某个目录&#xff0c;/dev/cdrom是/dev/sr0软链接&#xff0c;# mount /dev/cdrom /mnt&#xff0c;# ls /mnt AppStream B…

Elastic:linux设置elasticsearch、kibana开机自启

0. 引言 每次启动服务器都要手动启动es服务&#xff0c;相当之不方便&#xff0c;为此&#xff0c;书写一个脚本&#xff0c;实现es、kibana的开机自启 1. 原理 首先任何服务要实现开机自启&#xff0c;都可分为如下三步&#xff1a; 1、在/etc/init.d目录下创建启动、关闭服…

跳表与Redis

跳表原理 跳表是Redis有序集合ZSet底层的数据结构 首先有一个头结点 这个头结点里面的数据是null 就是他就是这个链表的最小值 就算是Math.Min也比它大 然后我们新建一个节点的时候是怎么操作的呢 先根据参数(假如说是5)创建一个节点 然后把它放在对应位置 就是找到小于他的最…

(JS逆向专栏十一)某融平台网站登入RSA

声明: 本文章中所有内容仅供学习交流&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff0c;若有侵权&#xff0c;请联系我立即删除&#xff01; 名称:点融 目标:登入参数 加密类型:RSA 目标网址:https://www.dianrong.com/accoun…

java: 无法访问org.springframework.web.bind.annotation.GetMapping(springboot构建时出现问题)

spring boot构建完成后出现以下问题 报错原因&#xff1a;SpringBoot 3.0以上版本要求JDK 17以上&#xff0c;jdk版本1.8 与 spring boot 3.0.1 版本不匹配 解决方法&#xff1a;

ORA-48913: Writing into trace file failed, file size limit [50000000] reached

检查某环境的alert_orcl1.log时&#xff0c;发现有很多的ORA-48913报错&#xff0c;细节如下 Sat Jul 22 19:34:04 2023 Non critical error ORA-48913 caught while writing to trace file "/u01/app/oracle/diag/rdbms/orcl/orcl1/trace/orcl1_dw00_138010.trc" E…

Python 中的机器学习简介:多项式回归

一、说明 多项式回归可以识别自变量和因变量之间的非线性关系。本文是关于回归、梯度下降和 MSE 系列文章的第三篇。前面的文章介绍了简单线性回归、回归的正态方程和多元线性回归。 二、多项式回归 多项式回归用于最适合曲线拟合的复杂数据。它可以被视为多元线性回归的子集。…

BenchmarkSQL 支持 TiDB 驱动以及 tidb-loadbalance

作者&#xff1a; GangShen 原文来源&#xff1a; https://tidb.net/blog/3c274180 使用 BenchmarkSQL 对 TiDB 进行 TPC-C 测试 众所周知 TiDB 是一个兼容 MySQL 协议的分布式关系型数据库&#xff0c;用户可以使用 MySQL 的驱动以及连接方式连接 TiDB 进行使用&#xff0…

Butterfly安装文档(三)主题配置-1

语言 修改站点配置文件 _config.yml 默认语言是 en 主题支持三种语言 default(en)zh-CN (简体中文)zh-TW (繁体中文) 网站资料 修改网站各种资料&#xff0c;例如标题、副标题和邮箱等个人资料&#xff0c;请修改博客根目录的_config.yml 导航栏设置 (Navigation bar set…

Data analysis|Tableau基本介绍及可实现功能

一、基础知识介绍 &#xff08;一&#xff09;什么是tableau tableau 成立于 2003 年&#xff0c;是斯坦福大学一个计算机科学项目的成果&#xff0c;该项目旨在改善分析流程并让人们能够通过可视化更轻松地使用数据。Tableau可以帮助用户更好地理解和发现数据中的价值&#x…

工具推荐之不出网环境下上线CS

前言 在实战攻防演练中&#xff0c;我们经常会遇到目标不出网的情况&#xff0c;即便获取了目标权限也不方便在目标网络进行下一步横向移动。本期我们将会推荐两个常用的代理工具&#xff0c;使我们能在不出网的环境下让目标上线到CS&#xff0c;方便后渗透的工作。 工具1&…

vue如何对node_modules源码进行修改,对第三方依赖包源码修改

方法 用patch-package给node_module中的包打补丁&#xff0c;解决修改源码的问题 使用 1、下载 patch-package 包&#xff1a;npm install patch-package -D 2、package.json文件中增加命令&#xff1a;"postinstall": "patch-package" {"scripts&quo…

【go-zero】docker镜像直接部署go-zero的API与RPC服务 如何实现注册发现?docker network 实现 go-zero 注册发现

一、场景&问题 使用docker直接部署go-zero微服务会发现API无法找到RPC服务 1、API无法发现RPC服务 用docker直接部署 我们会发现API无法注册发现RPC服务 原因是我们缺少了docker的network网桥 2、系统内查看 RPC服务运行正常API服务启动,通过docker logs 查看日志还是未…

MyCat垂直分库案例以及全局表概念讲解

这里的分片指的就是分库分表 1.垂直拆分 1.1场景介绍 1.2 数据库准备 1.3MyCat配置 schema.xml: <schema name"shopping" checkSQLschema"true" sqlMaxLimit"100"><table name"tb_goods_base" dataNode"dn1" pr…

⛳ Java注解

目录 ⛳ Java注解&#x1f3ed; 一&#xff0c;常见的注解&#x1f3a8; 二&#xff0c;JDK元注解&#x1f69c; 三&#xff0c;通过反射获取注解&#x1f43e; 3.1、JDK常用注解&#x1f463; 3.2、简单注解&#x1f4e2; 3.3、复杂注解 ⛳ Java注解 从 JDK 5.0 开始, Java 增…

python --windows获取启动文件夹路径/获取当前用户名/添加自启动文件

如何使用Python获取计算机用户名 一、Python自带的getpass模块可以用于获取用户输入的密码&#xff0c;但是它同样可以用来获取计算机用户名。 import getpassuser getpass.getuser() print("计算机用户名为&#xff1a;", user)二、使用os模块获取用户名 Python的…

深度学习部署:FastDeploy部署教程(CSharp版本)

FastDeploy部署教程(CSharp版本) 1. FastDeploy介绍 FastDeploy是一款全场景、易用灵活、极致高效的AI推理部署工具&#xff0c; 支持云边端部署。提供超过 &#x1f525;160 Text&#xff0c;Vision&#xff0c; Speech和跨模态模型&#x1f4e6;开箱即用的部署体验&#xf…