【java基础】异常处理(Exception)

news2025/8/30 8:41:22

文章目录

  • 基本介绍
  • 异常分类
  • 抛出异常
    • 非检查型异常
    • 检查型异常
  • 捕获异常
    • 捕获单个异常
    • 捕获多个异常
  • 创建自定义异常类
  • finally字句
  • try-with-Resource
  • 总结

基本介绍

对于一个程序,总是有bug的。如果我们的程序遇到一个错误就终止了,那么肯定是不合理,程序发生错误时,应该有一种通用的解决方式才合理。好在java给我们提供了一整套处理异常的机制,下面就来进行介绍


异常分类

在java中,所有的异常对象都派生与Throwable

在这里插入图片描述
由于Throwable是所有异常类的父类,我们有必要去看一下它的源码

在这里插入图片描述

很多方法和字段,具体用法大多是和名称一样的,这里建议大家先去看一下源码

需要注意的是,所有的异常都是由Throwable继承而来,但在下一层立即分解为两个分支:Error和Exception。

Error类层次结构描述了Java运行时系统的内部错误和资源耗尽错误。你的应用程序不应该抛出这种类型的对象。如果出现了这样的内部错误,除了通知用户,并尽力妥善地终止程序之外,你几乎无能为力。这种情况很少出现。

在设计Java程序时,要重点关注Exception层次结构。这个层次结构又分解为两个分支:一个分支派生于RuntimeException;另一个分支包含其他异常。一般规则是:由编程错误导致的异常属于RuntimeException;如果程序本身没有问题,但由于像I/O错误这类问题导致的异常属于其他异常。


下面就是属于RuntimeException和不属于RuntimeException的一些举例

派生于RuntimeException的异常包括以下问题:

  • 错误的强制类型转换。
  • 数组访问越界。
  • 访问null指针。

不是派生于RuntimeException的异常包括:

  • 试图超越文件末尾继续读取数据。
  • 试图打开一个不存在的文件。
  • 试图根据给定的字符串查找Class对象,而这个字符串表示的类并不存在。

“如果出现RuntimeException异常,那么就一定是你的问题”,这个规则很有道理。应该通过检测数组下标是否越界来避免ArrayIndexOutOfBoundsException异常;应该在使用变量之前通过检测它是否为null来杜绝NullPointerException异常的发生。

如何处理不存在的文件呢?难道不能先检查文件是否存在再打开它吗?嗯,这个文件有
可能在你检查它是否存在之后就立即被删除了。因此,“是否存在”取决于环境,而不只是
取决于你的代码。

Java语言规范将派生于Error类或RuntimeException类的所有异常称为非检查型(unchecked),所有其他的异常称为检查型(checked)异常


抛出异常

如果我们遇到了一个无法处理的情况,我们就可以使用throw抛出一个异常。原因很简单,因为方法不仅仅想要告诉编译器要返回上面值,还要告诉编译器有可能发生什么错误。

非检查型异常

现在假设我们自己写了一个方法,这个方法对传入的参数进行处理,但是参数可能不合法,我们已经无法处理了,这时我们就可以抛出一个异常

    public void handleParam(String param) {
        if (param.length() < 5) {
            throw new IllegalArgumentException("参数长度必须大于5");
        }
        // code logic
        // ....
    }

当然,我们也可以在方法上写上可能会抛出的异常。多个异常使用 **,**进行分隔,或者抛出它们的公共父类

    public void handleParam(String param) throws IllegalArgumentException {
        if (param.length() < 5) {
            throw new IllegalArgumentException("参数长度必须大于5");
        }
        // code logic
        // ....
    }

上面的IllegalArgumentException是一个RuntimeException,是一个非检查型异常。

在这里插入图片描述

我们上面写的方法throw了一个非检查异常,表示可能发生异常,这样,这个方法还是可以正常调用,调用者不需要去处理这个异常

在这里插入图片描述


检查型异常

现在我们来说应该检查型异常,例如IOException

我们来看一个FileInputStream的构造器会抛出一个FileNotFoundException,这是IOException的子类

在这里插入图片描述

在这里插入图片描述

现在,如果我们创建一个FileInputStream,看看会出现什么情况

    @Test
    public void t2() {
        FileInputStream fileInputStream = new FileInputStream("");
    }

运行代码编译器给出以下信息,编译都不能通过

在这里插入图片描述
对于检查型异常,我们必须将其捕获或者将其抛出,我们可以进行如下更改,继续往外抛出

    @Test
    public void t2() throws FileNotFoundException {
        FileInputStream fileInputStream = new FileInputStream("");
    }

捕获异常

捕获单个异常

我们先来看一段程序如下

    @Test
    public void t1() {
        int a = 1 / 0;
        System.out.println("程序结束....");
    }

上面代码如果运行就会报错

在这里插入图片描述

程序抛出了应该ArithmeticException异常,然后程序就结束,这显然不合理,就因为一个小错误就终止程序,于是我们就可以使用try-catch来捕获异常,捕获到异常后就不会终止程序。

try-catch语法如下

try{
	code
	more code
	more code
	.....
}catch(ExceptionType e){
	handler exception
}

我们将可能出现异常的代码放在try代码块中,发生异常的时候,try中代码就不会再执行了,catch就会根据写在catch中的异常进行匹配,如果异常类型相同或者抛出的异常为写在catch上的异常的子类,那么可以成功捕获,捕获后就会执行catch中的内容,然后继续执行try-catch下面的代码。如果没有捕获成功,那么程序就会终止。

现在我们就可以对上面代码进行改造

    @Test
    public void t1() {
        try {
            int a = 1 / 0;
        } catch (ArithmeticException e) {
            System.out.println("发生了异常,原因是:" + e.getMessage());
        }
        System.out.println("程序结束....");
    }

代码运行输出如下,发生异常后就不会直接结束程序了,而是被catch捕获

在这里插入图片描述

上面我使用了ArithmeticException来进行异常的捕获,至于为什么用这个异常,这是因为我刚好知道当除0时就会抛出这个异常,下面为这个类的源代码

在这里插入图片描述

我们再来看一下这个类的类图,可以发现这个异常就是RuntimeException下的子类。

在这里插入图片描述

上面我知道会发生什么异常,所以我可以捕获,如果我不知道要发生什么异常呢?上面不是说明了吗,捕获异常我们只需和抛出的异常类型相同或者为其父类就行了。我们既然不知道是上面异常,是不是就可以直接使用Exception接收呢?当然可以

    @Test
    public void t2() {
        try {
            int a = 1 / 0;
        } catch (Exception e) {
            System.out.println("发生了异常,原因是:" + e.getMessage());
        }
        System.out.println("程序结束....");
    }

输出和上面没有任何区别

在这里插入图片描述

对于知道可能发生的异常类型的,我们尽量都使用精确的异常来进行匹配。

捕获多个异常

我们还是先来看一段代码

    @Test
    public void t3() {
        Scanner in = new Scanner(System.in);
        String next = in.next();
        int num = Integer.parseInt(next);
        int ans = 100 / num;
        System.out.println("程序结束....");
    }

上面这个代码就有可能发生2个地方的异常,一个是Integer.parseInt,还有一个是100 / num,对于上面的情况,我们该怎么进行处理呢?很简单,因为它们发出异常肯定要抛出一个异常类,那么父类肯定就是Exception,我们直接使用Exception进行捕获即可

    @Test
    public void t3() {
        try {
            Scanner in = new Scanner(System.in);
            String next = in.next();
            int num = Integer.parseInt(next);
            int ans = 100 / num;
        } catch (NumberFormatException e) {
            System.out.println("发生了异常,原因是:" + e.getMessage());
        }
        System.out.println("程序结束....");
    }

但是如果我们想要对不同的异常进行不同的处理该怎么做呢?这时就可以使用多个catch,异常捕获将按照顺序来,如果捕获到了就不再执行其他catch

try {

}catch(exceptionType1 e1){

}catch(exceptionType2 e2){

}catch(exceptionType3 e3){

}catch(exceptionType4 e4){

}

但是现在又有一个问题,我们并不知道上面的代码中会抛出什么异常,其实,我们可以进入源代码查看,下面为Integer.parseInt()的源代码

在这里插入图片描述

可以发现异常是NumberFormatException,对于除0的异常上面已经说过了,于是我们可以编写以下代码

    @Test
    public void t4() {
        try {
            Scanner in = new Scanner(System.in);
            String next = in.next();
            int num = Integer.parseInt(next);
            int ans = 100 / num;
        } catch (NumberFormatException e) {
            System.out.println("数字格式化异常" + e.getMessage());
        } catch (ArithmeticException e) {
            System.out.println("发生了算术异常--" + e.getMessage());
        }
        System.out.println("程序结束....");
    }

这样我们在发生不同异常的时候就可以进行不同的处理
如果要捕获的异常特别多,但是有些处理逻辑相同的,我们可以在一个catch里面使用 | 进行分隔,表示或

    @Test
    public void t5() {
        try {

        } catch (ClassCastException | ArrayStoreException e) {
            System.out.println("XXX");
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("xxx");
        }
    }

创建自定义异常类

对于创建自定义异常类,想必大家看完上面应该都已经会了,我们只需要继承Exception或者RuntimeException就行了。继承RuntimeException就是非检查型异常,继承Exception就是检查型异常

定义检查型异常

public class MyCheckedException extends Exception{
    
}

自定义非检查型异常

public class MyNoCheckedException extends RuntimeException{
    
}

对于为什么继承Exception就是检查型异常,继承RuntimeException就是非检查型异常,上面已经说明过了,这里再来看一下源代码,注释上面也有说明

Exception
在这里插入图片描述

RuntimeException
在这里插入图片描述

finally字句

上面的try-catch中,只要出现异常,那么就会停止处理try后续的代码,有没有一种方法让try-catch无论是否出现异常都一定执行一些代码呢。finally就是这个作用,finally写在try或catch后面

        try {

        }catch (Exception e){

        }finally {
            // 一定会执行
        }

        try {
            
        } finally {
            // 一定会执行
        }

我们可以将一些通用的操作放在finally中,例如文件的关闭操作。
由于finally中的代码一定会执行,下面大家来看一段代码,看看返回上面

    public int getNum() {
        try {
            int a = 1 / 0;
            return 0;
        } catch (Exception e) {
            return 1;
        } finally {
            return 2;
        }
    }

上面的代码在try中会发生异常,所以return 0 肯定不会执行,发生异常后会执行catch中的语句return 1,但是发现还有finally,所以会暂缓返回,先执行finally,由于finally中是return 2,就直接返回了。所以最终的返回的结果是2

    @Test
    public void t1() {
        System.out.println(getNum());
    }

在这里插入图片描述

try-with-Resource

前面说了,finally这条语言一定会执行,一般用于关闭文件或者释放资源。但是这样写还是有点麻烦,在java7之后还有一种更便捷的写法。try-with-Resource语句

try(Resource res = ...){
	use res
}
// 可以写catch和finally,也可以不写

当try退出的时候,会自动调用res.close()。
看到这样,大家应该想到了,使用这种语法是有条件的,因为要调用close方法,所以一定要实现某个接口,该接口含有close方法,这个接口就是AutoCloseable即可

在这里插入图片描述

下面就使用FileInputStream举个例子,该类实现了AutoCloseable

在这里插入图片描述

    @Test
    public void t1() {
        try (FileInputStream fileInputStream = new FileInputStream("")) {
            int read = fileInputStream.read();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

在try种还可以指定多个资源,使用 **;**分隔

    @Test
    public void t1() {
        try (FileInputStream fileInputStream = new FileInputStream("");
             FileOutputStream fileOutputStream = new FileOutputStream("")) {
            int read = fileInputStream.read();
            int available = fileInputStream.available();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

再次强大,当try退出时就会调用 xxx.close()方法。优先级在catch和finally之前。

总结

经过上面的说明,最后再给出几点使用异常的技巧

  1. 异常处理不能代替简单的测试(捕获异常比 if 耗时大得多)
  2. 不要过分细化异常
  3. 充分利用异常层次结构
  4. 不要压制异常
  5. 在检查错误时,苛刻要比放任更好
  6. 不要羞于传递异常

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

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

相关文章

数据爬取(urllib+BeautifulSoup)

文章目录知识点总结爬虫步骤爬虫三要素爬虫注意事项python爬取技术学习网页抓取库Urllib网页解析库Beautifulsoup案例知识点总结 爬虫是一种按照一定规则&#xff0c;自动抓取互联网上网页中的相应信息的程序或脚本。 爬虫步骤 1.需求分析 2.找到要爬取信息的网站 3.下载reque…

基于halo后台管理+Gblog-wx搭建的微信小程序

先决条件 1、已经通过docker安装了halo后台管理系统(参考:http://43.136.39.20:8090/archives/halo-build) 2、安装的halo版本为1.5.3版本。此版本的halo才能安装小程序主题并启动小程序 3、需要修改小程序文件配置 解决安装的不是1.5.3的halo 1、如果是docker安装的halo…

蓝牙技术|蓝牙5.4标准正式发布,蓝牙ESL电子价签迎来一波利好

蓝牙技术联盟于2023年1月31日批准了蓝牙核心规范v5.4版本(以下简称蓝牙5.4版本)&#xff0c;并已正式公布。 蓝牙5.4版本引入了四个新特性&#xff0c;如下: 广播数据加密&#xff08;Encrypted Advertising Data&#xff09;&#xff1a;对广播数据进行加密以提高广播数据传…

[神经网络]Swin Transformer网络

一、概述 Swin Transformer是一个用了移动窗口的层级式Vision Transformer。 在图像领域&#xff0c;Transformer需要解决如下两个问题&#xff1a; ①尺度问题&#xff1a;同一语义的物体在图像中有不一样的尺度。(大小不同) ②Resolution过大&#xff1a;若以像素点作为单位&…

利用python写一个gui小公举--环境搭建

文章目录背景搭建环境安装必要库添加工具快捷方式检验背景 在实习过程中遇到一个问题&#xff0c;某项目是通过python代码实现的&#xff0c;而且需要一直修改参数实现功能&#xff0c;过程有些繁琐。虽然师兄用PHP study搭了一个网站用于查看结果&#xff0c;但是还是过于繁琐…

分布式新闻项目实战 - 12.热点文章-实时计算(kafkaStream)

死海效应&#xff1a; 公司发展到一定阶段后&#xff0c;工作能力强的员工&#xff0c;就会离职&#xff0c;因为他无法容忍公司的某些行为&#xff0c;即使辞职也很快会找到好工作&#xff1b;工作能力差的员工&#xff0c;却赖着不走&#xff0c;因为辞职以后也不太好找工作&…

JavaScript实现十大排序算法

目录 概览 一、冒泡排序 1、算法描述 2、图示 3、代码 二、选择排序 1、算法描述 2、图示 3、代码 三、插入排序 1、算法描述 2、图示 ​编辑 3、代码 四、希尔排序 1、算法描述 2、图示 3、代码 五、并归排序 1、算法描述 2、图示 ​编辑​编辑3、代码 …

食品与疾病关系预测赛题

和鲸平台数据分析实战 题目&#xff1a;食品与疾病关系预测算法赛道 一、赛题描述 食品与疾病关系预测算法赛道 越来越多的证据表明&#xff0c;食物分子与慢性疾病之间存在关联甚至治疗关系。营养成分可能直接或间接地作用于人类基因组&#xff0c;并调节参与疾病风险和疾病…

php结课报告--会员注册管理系统

目录 1&#xff0e; 系统背景及意义 1 2&#xff0e; 系统的设计思路 1 2.1 数据库设计分析 1 2.2 功能模块设计分析 1 3&#xff0e; 程序功能测试及截图 1 3.1代码测试与功能演示 1 4&#xff0e; 总结与收获 6 1&#xff0e;系统背景及意义 随着现在时代得发展&#xff0c;…

【AI面试】NMS 与 Soft NMS 的辨析

往期文章&#xff1a; AI/CV面试&#xff0c;直达目录汇总【AI面试】L1 loss、L2 loss和Smooth L1 Loss&#xff0c;L1正则化和L2正则化 一、NMS 非极大值抑制&#xff08;Non-Maximum Suppression&#xff0c;NMS&#xff09;&#xff0c;并不是深度学习时期&#xff0c;目标…

VS项目配置常用的配置

背景随着学习使用VS的深入在项目配置使用一些相对路径是必不可少的,使用绝对路径是最简单的,但是加入你换了电脑或者别人拉取你的代码,就会发现通常会编译不过.因为项目配置使用了绝对路径.所以使用相对路径的好处就会体现.在VS项目配置有自己的一套配置,简单记录一下我使用到的…

mysql一主键uuid和自增的选择

文章目录 1.自增ID的优缺点1.1 优点1.2 缺点1.3 不适合以自增ID主键作为主键的情况2.UUID作为主键2.1 介绍2.2 优点2.3 缺点3.有序UUID作为主键3.1 介绍3.2 演示使用3.2.1 前提知识3.2.1.1 数据类型 - binary3.2.1.2 函数 - hex()3.2.1.3 函数 - unhex()3.2.2 数据库层3.2.3 JA…

蓝桥杯第十四届校内赛(第三期) C/C++ B组

一、填空题 &#xff08;一&#xff09;最小的十六进制 问题描述   请找到一个大于 2022 的最小数&#xff0c;这个数转换成十六进制之后&#xff0c;所有的数位&#xff08;不含前导 0&#xff09;都为字母&#xff08;A 到 F&#xff09;。   请将这个数的十进制形式作…

提升Mac使用性能的5大方法,CleanMyMacX 2023非常的好用哦~

近些年伴随着苹果生态的蓬勃发展&#xff0c;越来越多的用户开始尝试接触Mac电脑。然而很多人上手Mac后会发现&#xff0c;它的使用逻辑与Windows存在很多不同&#xff0c;而且随着使用时间的增加&#xff0c;一些奇奇怪怪的文件也会占据有限的磁盘空间&#xff0c;进而影响使用…

sql数据库常用操作指令

一、操作库-- 创建库create database db1;-- 创建库是否存在&#xff0c;不存在则创建create database if not exists db1;-- 查看所有数据库show databases;-- 查看某个数据库的定义信息 show create database db1; -- 修改数据库字符信息alter database db1 character set ut…

Istio Sidecar启动顺序 - 导致的应用容器网络不通

目录一、问题二、Istio 1.7及其之后版本的解决方案2.1 方式1&#xff1a;安装Istio时全局设置2.2 方式2&#xff1a;在应用Deployment通过annotation设置2.3 holdApplicationUntilProxyStarts启用效果三、Istio 1.7之前的解决方案一、问题 线上应用集成了Spring Cloud K8S Con…

HCIP实验1

实验要求 1 R6为isp, 接口IP地址均为公有地址;该设备只能配置IP地址&#xff0c;之后不能冉对其进行其他任何配置; 2 R1-R5为局域网&#xff0c;私有IP地址192.168.1.0/24&#xff0c; 请合理分配; 3 R1, R2, R4,各有两个环回地址; R5; R6各有一个环回地址;所有路由器上环回均…

2 GateWay工作流程+GateWay搭建

GateWay工作流程GateWay搭建 核心流程图如下&#xff1a; 核心概念&#xff1a; 客户端向 Spring Cloud Gateway 发出请求。如果Gateway Handler Mapping确定请求与路由匹配&#xff0c;则将其发送到Gateway Web Handler 处理程序。此处理程序通过特定于请求的Fliter链运行请求…

ARM uboot 的移植1-从三星官方 uboot 开始移植

一、移植初体验 1、直接编译三星移植版 uboot 尝试运行 (1) 复制到 linux 的源生目录下&#xff0c;然后解压开。 (2) 检查 Makefile 中的交叉编译工具链。 (3) 配置时使用&#xff1a;make smdkv210single_config&#xff0c;对应 include/configs/smdkv210single.h 头文件。…

力扣(LeetCode)430. 扁平化多级双向链表(2023.03.04)

你会得到一个双链表&#xff0c;其中包含的节点有一个下一个指针、一个前一个指针和一个额外的 子指针 。这个子指针可能指向一个单独的双向链表&#xff0c;也包含这些特殊的节点。这些子列表可以有一个或多个自己的子列表&#xff0c;以此类推&#xff0c;以生成如下面的示例…