Java——异常详解

异常五个主要关键字:throw、try、catch、finally、throws

1. 异常的概念与体系结构

1.1 异常的概念

在Java中,程序执行过程中发生的不正常行为被称为异常,如:

1. 算数异常

    public static void main(String[] args) {System.out.println(10 /0);}

运行结果:

 2. 数组越界异常

    public static void main(String[] args) {int[] arr = {1,2,3};System.out.println(arr[10]);System.out.println("after");}

运行结果:当发生异常,程序直接结束,后面代码不再执行

3. 空指针异常

    public static void main(String[] args) {int[] array = null;System.out.println(array.length);}

运行结果:

 从上述过程中可以看出,Java中不同类型的异常,都有与之对应的类来描述


1.2 异常的体系结构

异常种类繁多,为了对不同的异常或错误进行分类管理,Java内部维护了一个异常的体系结构:

tips:

Throwable:是异常体系的顶层类,其派生出两个重要子类Error和Exception

Error:指的是Java虚拟机无法解决的严重问题,比如:JVM的内部错误、资源耗尽等,典型代表:StackOverflowError和OutOfMemoryError,示例:

    public static void funcction(){funcction();}public static void main(String[] args){funcction();}

 如上代码,递归自己调用自己,且没有结束条件,就会一直在栈上开辟内存,最终导致资源耗尽,运行结果:

Exception:异常产生后程序员可以通过代码进行处理,使程序继续执行,如:网络不好,重新链接一下。


1.3 异常的分类

而Exception下面又分为 编译时异常/受查异常运行时异常/非受查异常

编译时异常/受查异常示例:

像上面这样的情况称为 编译时异常/受查异常 ,该类型异常的特点为,必须解决,否则无法运行,该种异常要与 编译时错误/语法错误 做出区分


2. 异常的处理

2.1防御式编程

1.LBYL:Look Before You Leap.在操作之前就做充分的检查,即:事前防御型

boolean ret = false;ret = 登陆游戏();if (!ret) {处理登陆游戏错误;return;}ret = 开始匹配();if (!ret) {处理匹配错误;return;}ret = 游戏确认();if (!ret) {处理游戏确认错误;return;}ret = 选择英雄();if (!ret) {处理选择英雄错误;return;
}ret = 载入游戏画面();if (!ret) {处理载入游戏错误;return;}......

缺陷:正常流程和错误处理流程代码混为一起,代码整体显得比较混乱

2. EAFP:It's Easier to Ask Forgiveness than Permission.“请求宽恕比请求许可更容易”,也就是先操作遇到问题再处理,即:事后认错型

try {登陆游戏();开始匹配();游戏确认();选择英雄();载入游戏画面();...} catch (登陆游戏异常) {处理登陆游戏异常;} catch (开始匹配异常) {处理开始匹配异常;} catch (游戏确认异常) {处理游戏确认异常;} catch (选择英雄异常) {处理选择英雄异常;} catch (载入游戏画面异常) {处理载入游戏画面异常;}......

优势:正常流程和错误流程是分开的,程序与可以更好的关注正常流程,代码更清晰,容易理解代码

异常处理的核心就是EAFP

在Java中,异常处理主要的5个关键字:throw、try、catch、finally、throws


2.2 异常的抛出 throw

在编写程序时,如果程序中出现错误,此时需要将错误的信息告知调用者

在Java中,可以借助throw关键字,抛出一个指定的异常对象,将错误信息告知给调用者,具体语法如下:

throw new XXXException("异常产生的原因");

示例:

    public static void testException(int a){if(a == 0){throw new ArithmeticException();}}public static void main(String[] args){testException(0);}

运行结果:

进入 ArithmeticException 源码可发现其带有一个无参构造方法和一个有参构造方法

我们可以将错误信息当作参数传递,此时,随着异常的打印,错误信息也会打印,更方便我们解决异常,如下:

tips:

throw必须写在方法内部

抛出的对象必须是Exception或者Exception的子类

如果抛出的时RuntimeException或者RuntimeException的子类,则可以不用处理,直接交给JVM处理

如果抛出的是编译时异常,用户必须处理,否则无法通过编译

异常一旦抛出,其后代码就不会执行


2.3 异常的捕获

异常的捕获,也就是异常的具体处理方式,主要有两种:异常声明 throwstry-catch 捕获处理

 2.3.1 异常声明throws

位置在方法声明参数列表之后,当方法中抛出编译时异常,用户不想处理该异常,就可以借助throws将异常抛给方法的调用者来处理,即当前方法不处理异常,提醒方法的调用者处理异常

 语法格式:

修饰符  返回值类型   方法名(参数列表) throws 异常类型1,异常类型2...{}

示例:

 tips:

throws必须跟在方法的参数列表之后

声明的异常必须是Exception或者Exception的子类

方法内部如果抛出了多个异常,throws之后必须跟多个异常类型,之间用逗号隔开,如果抛出多个异常类型具有父子关系,直接声明父类即可

调用声明抛出异常的方法时,调用者必须对该异常进行处理,或者继续使用throws抛出

2.3.2 try-catch 捕获并处理

throws对异常并没有真正处理,而是将异常报告给抛出异常方法的调用者,由调用者处理,如果真正要对异常进行处理,就需要try-catch

语法格式:
try{// 将可能出现异常的代码放在这里
}catch(要捕获的异常类型  e){// 如果try中的代码抛出异常了,此处catch捕获时异常类型与try中抛出的异常类型一致时,或者是try中抛出异常的基类时,就会被捕获到// 对异常就可以正常处理,处理完成后,跳出try-catch结构,继续执行后序代码
}[catch(异常类型 e){// 对异常进行处理
}finally{// 此处代码一定会被执行到
}]// 后序代码
// 当异常被捕获到时,异常就被处理了,这里的后序代码一定会执行
// 如果捕获了,由于捕获时类型不对,那就没有捕获到,这里的代码就不会被执行注意:
1. []中表示可选项,可以添加,也可以不用添加
2. try中的代码可能会抛出异常,也可能不会

示例1:

示例2:

 

关于异常的处理方式

异常的种类有很多,我们要根据不同的业务场景来决定

对于比较严重的问题(例如和算钱相关的场景),应该让程序直接崩溃, 防止造成更严重的后果

对于不太严重的问题(大多数场景),可以记录错误日志,并通过监控报警程序及时通知程序员

对于可能会恢复的问题(和网络相关的场景),可以尝试进行重试

tips:

  • 1.try块内抛出异常位置之后的代码将不会被执行,即不会同时抛出多个异常
  • 2.如果抛出异常类型与catch捕获异常类型不匹配,即异常不会被成功捕获,继续往外抛,直到JVM收到后中断程序,即异常是按照类型来捕获的
  • 3.try中可能会抛出多个不同的异常对象,则必须用多个catch来捕获,即多种异常,多次捕获
  • 如果异常之间具有父子关系,一定是子类异常在前catch,父类异常在后catch,否则语法错误
  • 4.可以通过一个catch捕获所有的异常,即多个异常,一次捕获(不推荐)
  • 由于Exception类是所有异常类的父类,所以可以用这个类捕获所有异常

2.3.3 finally

 在写程序时,有些特定的代码,无论程序是否发生异常,都需要执行,比如程序打开的资源:网络连接、数据库连接、IO流等,在程序正常或者异常退出时,必须要对资源进行回收。另外,因为异常会引发程序的跳转,可能导致有些语句执行不到,finally就是用来接解决这些问题的

语法格式:

语法格式:
try{// 可能会发生异常的代码
}catch(异常类型  e){// 对捕获到的异常进行处理
}finally{// 此处的语句无论是否发生异常,都会被执行到
}// 如果没有抛出异常,或者异常被捕获处理了,这里的代码也会执行

这里就有一个问题:既然finally和try-catch-finally后的代码都会被执行,那为什么还要有finally呢?

举例分析:

需求:实现getData方法,内部输入一个整型数字,然后将该数字返回,并在main方法中打印

    public static int getData(){Scanner sc = null;try{sc = new Scanner(System.in);int data = sc.nextInt();return data;}catch (InputMismatchException e){e.printStackTrace();}finally {System.out.println("finally中代码");}System.out.println("try-catch-finally之后代码");if(null != sc){sc.close();}return 0;}public static void main(String[] args) {int data = getData();System.out.println(data);}

运行结果:

上述程序中,如果正常输入,成功接收输入的值后,程序就返回了,try-catch-finally之后的代码根本就没有执行,即输入流没有被释放,造成资源泄露,这中情况下就需要用到 finally 了,改为如下:

改完之后,发现try报警告:

此时鼠标放在try上,按Alt+Enter:

编译器会自动改为合适的形式

把打开的资源放到 try 后的小括号中,finally 中的 sc.close() 就可以不写了

tip:finally中的代码一定会执行,一般在finally中进行一些资源清理的扫尾工作

练习题:下面程序输出什么?

    public static int testFinally(){try{return 10;}catch(InputMismatchException e){e.printStackTrace();}finally{return 20;}}public static void main(String[] args) {System.out.println(testFinally());}

运行结果:20

解析:

finally 执行的时机是在方法返回之前(try 或者 catch 中如果有 return 会在这个 return 之前执行 finally),但是如果 finally 中也存在 return 语句,那么就会执行 finally 中的 return,从而不会执行到 try 中原有的 return

一般不建议在 finally 中写 return (会被编译器当做一个警告)


2.4 异常的处理流程

当我们运行如下代码:

    public static void test(){int[] array = {1,2,3};System.out.println(array[10]);}public static void main(String[] args) {test();}

运行结果:

解析:

异常处理流程:

  1. 程序先执行try中的代码
  2. 如果try中的代码出现异常,就会结束try中的代码,看和catch中的异常类型是否匹配
  3. 如果找到匹配的异常类型,就会执行catch中的代码
  4. 如果没有找到匹配的异常类型,就会将异常向上传递到上层调用者
  5. 无论是否找到匹配的异常类型,finally中的代码都会被执行到(在该方法结束前执行)
  6. 如果上层调用者也没有处理异常,就继续向上传递
  7. 一直到main方法也没有合适的代码处理异常,就会交给JVM来进行处理,程序就会异常终止

3. 自定义异常类

Java中虽然已经内置了丰富的异常类,但是并不能完全表示实际开发中所遇到的一些异常,此时就需要维护符合我们实际情况的异常结构

示例:实现一个用户登录功能

class Login {private String userName;private String passWord;public String getUserName() {return userName;}public void setUserName(String userName) {this.userName = userName;}public String getPassWord() {return passWord;}public void setPassWord(String passWord) {this.passWord = passWord;}public void logInfo(String userName, String passWord) {if(!this.userName.equals(userName)) {System.out.println("用户名错误!");return;}if(!this.passWord.equals(passWord)) {System.out.println("密码错误!");return;}System.out.println("登录成功!");}
}
public class Test {public static void main(String[] args){Login login = new Login();login.setUserName("abc");login.setPassWord("123");login.logInfo("abc","123");}
}

运行结果:

解析:运行结果有三种情况,当我们在处理用户名错误或密码错误时,需要抛出两种异常,我们可以基于已有的异常类进行扩展(继承),创建和我们业务相关的类

改为自定义异常表示,步骤:

1.自定义异常类,然后继承Exception或者RuntimeException

2.实现一个带有String类型参数的构造方法,参数含义:出现异常的原因

//UserNameException 类
public class UserNameException extends RuntimeException{public UserNameException(){super();}public UserNameException(String s){super(s);}
}//PassWordException 类
public class PassWordException extends RuntimeException{public PassWordException() {super();}public PassWordException(String s) {super(s);}
}//Test 类
class Login {private String userName;private String passWord;public String getUserName() {return userName;}public void setUserName(String userName) {this.userName = userName;}public String getPassWord() {return passWord;}public void setPassWord(String passWord) {this.passWord = passWord;}public void logInfo(String userName, String passWord) throws UserNameException,PassWordException {if(!this.userName.equals(userName)) {throw new UserNameException("用户名错误异常...");}if(!this.passWord.equals(passWord)) {throw new PassWordException("密码错误异常...");}System.out.println("登录成功!");}
}public class Test {public static void main(String[] args){Login login = new Login();login.setUserName("abc");login.setPassWord("123");try {login.logInfo("abc","1232");}catch (UserNameException e) {e.printStackTrace();}catch (PassWordException e) {e.printStackTrace();}finally {}}
}

tips:

自定义异常通常会继承自Exception或者RuntimeException

继承自Exception的异常默认是受查异常,必须在try-catch中解决才能运行代码

继承自RuntimeException的异常默认是非受查异常

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

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

相关文章

基于MingGW64 GCC编译Windows平台上的 libuvc

安装cmake 打开cmake官网 https://cmake.org/download/,下载安装包: 安装时选择将cmake加到系统环境变量里。安装完成后在新的CMD命令窗口执行cmake --version可看到输出: D:\>cmake --version cmake version 3.29.3 CMake suite mainta…

牛客网刷题 | BC108 反斜线形图案

目前主要分为三个专栏,后续还会添加: 专栏如下: C语言刷题解析 C语言系列文章 我的成长经历 感谢阅读! 初来乍到,如有错误请指出,感谢! 描述 KiKi学习了循环&am…

社交媒体数据恢复:Voxer

一、Voxer数据恢复教程 了解Voxer应用 Voxer是一款专门为iPhone和Android智能手机设计的免费对讲机应用,为用户提供即时的语音、文本、照片等信息发送和接收服务。该应用有点类似短信服务,但用声音代替文本。当你下载之后,如果不邀请朋友&a…

重复文件怎么查找并清理?电脑重复文件清理工具分享:4个

在日常使用电脑的过程中,我们不可避免地会遇到各种重复文件的问题。这些重复文件不仅占据了宝贵的存储空间,还可能导致系统性能下降,甚至引发一些不必要的问题。因此,如何有效地查找并清理这些重复文件成为了许多用户关注的焦点。…

计算 x 的二进制表示中 1 的个数

计算 x 的二进制表示中 1 的个数 代码如下: int func(int x){int countx 0;while (x>0){countx;x x & (x - 1);}return countx;} 完整代码: using System; using System.Collections.Generic; using System.ComponentModel; using System.Dat…

STM32基于HAL库的HC-SR04模块超声波测距

文章目录 一、HC-SR04模块介绍二、创建工程1.选择芯片2.配置RCC、SY![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/9d2a5b883f0e409eabb804e6da861277.png)3.配置串口14.配置定时器5.配置GPIO 三、Keil代码1.勾选Use MicroLIB2.创建SR04.c和SR04.h文件3.其他代码 …

html three.js 引入.stl模型示例

1.新建一个模块用于放置模型 <div id"chart_map" style"width:800px;height:500px"></div> 2. 引入代码根据需求更改 <!-- 在head或body标签内加入以下链接 --> <script src"https://cdn.jsdelivr.net/npm/three0.137/build/t…

2.7HDR与LDR

一、基本概念 1.基本概念 动态范围&#xff08;Dynamic Range&#xff09; 最高亮度 / 最低亮度 HDR High Dynamic RangeLDR Low Dynamic Range HDR与LDR和Tonemapping的对应关系&#xff1a; 我们常用的各种显示器屏幕&#xff0c;由于不同的厂家不同的工艺导致它们的…

HALCON-从入门到入门-软件界面介绍

1.废话 从halcon12到halcon23&#xff0c;开发的IDE界面大差不差&#xff0c;简单说下界面上不同功能按键的分布&#xff0c;以及一些快捷键啥的&#xff0c;要是还有我没有总结到的&#xff0c;又比较好用的&#xff0c;欢迎大家补充一下。 1.菜单栏 从上看到下&#xff0c;…

python深度学习入门-从零构建CNN和RNN

文章目录 第1章 基本概念1.1. 导数1.2. 链式法则1.3. 多输入函数的导数1.4. 多输入向量函数的导数1.5. 向量函数及其导数&#xff1a; 再进一步1.6. 包含两个二维矩阵数据的计算图 第2章 基本原理2.1. 监督学习概述2.2. 监督学习模型2.3. 线性回归2.3.1. 线性回归&#xff1a; …

[激光原理与应用-94]:电控 - 低噪声运放的原理

目录 一、什么是低噪声运放 1.1 什么是低噪声水平 1.2 什么是高增益 在电子工程中的应用 在通信领域的应用 在音频和视频处理中的应用 注意事项 1.3 什么是宽带宽 1.4 什么是低偏置电流 重要性 特点 解决方法 应用 二、低噪声运放的原理图 1. 基本构成 2. 设计…

第五维度【百度之星】/二分

第五维度 二分 思路&#xff1a;看到题目是尽可能晚的情况下最早就应该想到贪心。 #include<bits/stdc.h> using namespace std; typedef long long ll; ll a[100005],b[100005]; ll n,m; bool check(ll t) {ll res0,big0;for(ll i0;i<n;i) {if(a[i]>t) continue…

JMeter工具介绍

Jmeter功能概要 JDK常用文件目录介绍 Bin目录&#xff1a;存放可执行文件和配置文件 Docs目录&#xff1a;是Jmeter的API文档&#xff0c;用于开发扩展组件 printable_docs目录&#xff1a;用户帮助手册 lib目录&#xff1a;存放JMeter依赖的jar包和用户扩展所依赖的Jar包 修…

HALCON-从入门到入门-读取图片保存图片

1.废话 视觉算法库的第一步。 读取图片&#xff1a; 看你是从哪里读取&#xff0c;从相机读取还是从本地硬盘中读取。 保存图片&#xff1a;就只有保存到本地了。 上面的截图显示我读取了一张图片 从相机中读取另开一篇来说&#xff0c;先说从本地磁盘读取哈。 怎么读取的…

数据结构复习指导之交换排序(冒泡排序,快速排序)

目录 交换排序 复习提示 1.冒泡排序 1.1基本思想 1.2算法代码 1.3性能分析 2.快速排序 2.1基本思想 2.2算法代码 2.3性能分析 交换排序 复习提示 所谓交换&#xff0c;是指根据序列中两个元素关键字的比较结果来对换这两个记录在序列中的位置。 基于交换的排序算法很…

python解决flask启动的同时启动定时任务

业务场景描述&#xff1a;在常规的开发中&#xff0c;我们开发接口服务&#xff0c;一般会将数据放在数据库、文件等第三方文件&#xff0c;启动服务后&#xff0c;服务到后台数据库中加载数据&#xff0c;这样做的好处当然是开发会更加便利以及数据的可复用性较高&#xff0c;…

深度学习-03-函数的连续调用

深度学习-03-函数的连续调用 本文是《深度学习入门2-自製框架》 的学习笔记&#xff0c;记录自己学习心得&#xff0c;以及对重点知识的理解。如果内容对你有帮助&#xff0c;请支持正版&#xff0c;去购买正版书籍&#xff0c;支持正版书籍不仅是尊重作者的辛勤劳动&#xff0…

LLaMA-Factory推理实践

运行成功的记录 平台&#xff1a;带有GPU的服务器 运行的命令 git clone https://github.com/hiyouga/LLaMA-Factory.git cd LLaMA-Factory/ conda create -n py310 python3.10 conda activate py310由于服务器不能直接从huggingface上下载Qwen1.5-0.5B&#xff0c;但本地可…

51仿真器 PZ-51Tracker 未知设备

插上仿真器&#xff0c;右击我的电脑 等待一下&#xff0c;选择winUSB 此时在keil中选择仿真器会报错&#xff0c;需要安装如下我是win10) 安装好后退出再试&#xff0c;没有报错即可 这项也要选择 另外配置晶振

React + SpringBoot开发用户中心管理系统

用户中心项目搭建笔记 技术栈 前端技术栈 “react”: “^18.2.0”,ant-design-pro 后端技术栈 SpringBoot 2.6.x 项目源码地址 https://gitee.com/szxio/user-center 前端项目搭建 快速搭建一个后端管理系统项目框架 初始化 antDesignPro 官网&#xff1a; https://…