【Java】异常处理见解,了解,进阶到熟练掌握

各位看官早安午安晚安呀

如果您觉得这篇文章对您有帮助的话

欢迎您一键三连,小编尽全力做到更好
欢迎您分享给更多人哦

大家好我们今天来学习Java面向对象的的抽象类和接口,我们大家庭已经来啦~

目录

1.(throws和throw)我们不管这个异常,把这个异常抛出去

1.1:throws的注意事项

1.2:throw的注意事项

2:(try-catch)我们主动地去捕捉这个异常

2.1. try块内抛出异常位置之后的代码将不会被执行

2.2. 如果抛出异常类型与catch时异常类型不匹配

2.3. try中可能会抛出多个不同的异常对象

2.4:我们也可以有以下两种不推荐的捕获方式

3:关于 "调用栈"(e.printStackTrace())

关于 "调用栈"

4:finally

4:自定义异常类


Java 中,将程序执行过程中发生的不正常行为称为异常
要认识异常首先我们要认识一张图
从上图中可以看到:
1. Throwable 是异常体系的顶层类,其派生出两个重要的子类 , Error Exception
2. Error 指的是 Java 虚拟机无法解决的严重问题,比如: JVM 的内部错误、资源耗尽等 ,典型代表:
StackOverflowError OutOfMemoryError ,一旦发生回力乏术。
3. Exception 异常产生后程序员可以通过代码进行处理,使程序继续执行。比如:感冒、发烧。我们平时所说的异常就是Exception
异常大概处理方案:

关于异常的一般处理方式:

异常的种类有很多, 我们要根据不同的业务场景来决定.
对于比较严重的问题(例如和算钱相关的场景), 应该让程序直接崩溃, 防止造成更严重的后果
对于不太严重的问题(大多数场景), 可以记录错误日志, 并通过监控报警程序及时通知程序猿
对于可能会恢复的问题(和网络相关的场景), 可以尝试进行重试
在我们当前的代码中采取的是经过简化的第二种方式. 我们记录的错误日志是出现异常的方法调用信息, 能很快速的让我们找到出现异常的位置. 以后在实际工作中我们会采取更完备的方式来记录异常信息
比如我们之前写代码时经常遇到的:
1: 算数异常
   System.out.println(10/0);
2.   数组越界异常
int [] arr = { 1 , 2 , 3 };
System . out . println ( arr [ 100 ]);
3:空指针异常
        int [] array = null;System.out.println(array.length);

这些异常,我们写代码的时候不会显示异常,但是程序运行时就会报错;

(这种异常我们称之为运行时异常也叫非受查异常

既然有非受查异常,就自然会有受查异常

譬如:我们之前一直没明白的克隆方法,我们子类要用这个克隆方法,克隆之后肯定要用一个新的对象来接收,那就是Person person2 = person1.clone();但是clone()是Object类的方法,我们只能在类里通过super调用,所以子类要用就必须要重写这个方法。

但是重写之后还是会报异常CloneNotSupportedException

我们只要调用这个方法就会报异常,但是我们还是要用到这个方法

所以说现在摆在我们面前的有两条路:

1.我们不管这个异常,把这个异常抛出去

2:我们主动地去捕捉这个异常

1.(throws和throw)我们不管这个异常,把这个异常抛出去

抛出去这个异常就要引出一个关键字throws了

然后还有一个关键字throw和它很像(这个关键字也是抛出一个异常)

他俩的区别在于:

throws是抛出已经出现的异常(一般放在方法参数列表之后)(也算是处理了,但不算是真正的处理,只是异常报告给抛出异常方法的调用者,由调用者处理

而throw是我们主动去抛出一个异常,一般是程序没出现我们为了提醒别人而抛出的异常

但是这个异常迟早是要被捕捉的,不然异常一直存在,最后大家都处理,只能交给JVM去处理,最后程序就只能报出来这个异常

我们throws出去这个异常

大家可以看到这个时候就不报异常了,但是我们现在调用它试试呢?

现在它又报错了,为啥呢?就像我上面所说的你只是抛出了这个异常(暂时解决了这个问题)实际上是把问题抛给了这个方法的调用者

然后我们还不想解决,那就,让main函数也把这个责任甩出去

按理说程序运行时,JVM会处理这个异常

但是我们实现了一个克隆接口(允许)该类的对象被拷贝

Cloneable接口是一个标记接口(即它没有任何方法定义)。当一个类实现了Cloneable接口,这表示这个类的对象支持克隆操作,而且Person类实现了Cloneable接口并且正确地重写了clone()方法,所以说最终没有抛出这个异常

1.1:throws的注意事项

处在方法声明时参数列表之后,当方法中抛出编译时异常,用户不想处理该异常,此时就可以借助 throws 将异常抛给方法的调用者来处理。即当前方法不处理异常,提醒方法的调用者处理异常
注意:
1. throws 必须跟在方法的参数列表之后
2. 声明的异常必须是 Exception 或者 Exception 的子类
3. 方法内部如果抛出了多个异常, throws 之后必须跟多个异常类型,之间用逗号隔开,如果抛出多个异常类型具有父子关系,直接声明父类即可。
 
publicstaticvoidmain(String[]args)throwsArrayIndexOutOfBoundsException,ArithmeticException{}
可以替换成
public static void main(String[] args) throws Exception{
}

4:调用声明抛出异常的方法时,调用者必须对该异常进行处理,或者继续使用 throws 抛出
(我们讲述的clone方法和main方法)
5: 将光标放在抛出异常方法上,ALT点击+enter快速处理

1.2:throw的注意事项

1.throw 必须写在方法体内部
2. 抛出的对象必须是 Exception 或者 Exception 的子类对象
3. 如果抛出的是 RunTimeException 或者 RunTimeException 的子类,则可以不用处理,直接交给 JVM 来处理
4. 如果抛出的是编译时异常,用户必须处理,否则无法通过编译
throws继续抛出或者try-catch处理,最后try-catch不处理的话JVM处理,程序异常终止
5. 异常一旦抛出,其后的代码就不会执行

2:(try-catch)我们主动地去捕捉这个异常

就像如果我们捕捉了上面的克隆异常(但是他是一个异常,还是不会被执行,所以我们最好还是实现一个克隆接口,最终别抛出异常)

如果我们要捕捉异常

这时候就要引出一个组合了 try-catch捕获并处理(细节我们一会会具体将到)

try {
// 将可能出现异常的代码放在这里
} catch ( 要捕获的异常类型 e ){
// 如果 try 中的代码抛出异常了,此处 catch 捕获时异常类型与 try 中抛出的异常类型一致时 ,或者是try中抛出异常的基类时,就会被捕获到
// 对异常就可以正常处理,处理完成后,跳出 try-catch 结构,继续执行后序代码
} finally {
// 此处的语句无论是否发生异常,都会被执行到
}
// 如果没有抛出异常,或者异常被捕获处理了,这里的代码也会执行

示例:

2.1. try块内抛出异常位置之后的代码将不会被执行

 public static void main(String[] args){try{System.out.println(10/0);System.out.println("想你");}catch(ArithmeticException e){System.out.println("捕捉到了算数异常");}}

这里我们捕捉到了算数异常,但是“想你”并没有被打印这是因为这里程序运行到这里已经发现了异常,就会去捕捉(因为我们用try-catch组合了)所以后面的代码就不会被执行了

就像程序这里(哈哈打印了,想你没有打印)

2.2. 如果抛出异常类型与catch时异常类型不匹配

即异常不会被成功捕获,也就不会被处理,继续往外抛,直到 JVM收到后中断程序----异常是按照类型来捕获的

public static void main(String[] args){try{System.out.println(10/0);System.out.println("想你");}catch(NullPointerException e){System.out.println("捕捉到了算数异常");}System.out.println("想你");}

一个想你都没有打印

2.3. try中可能会抛出多个不同的异常对象

则必须用多个catch来捕获----即多种异常,多次捕获 ,并且捕获到了一个异常,就不会进入另一个catch了

  public static void main(String[] args){try{System.out.println(10/0);int[] arr = {1, 2, 3};System.out.println(arr[100]);}catch(NullPointerException e){System.out.println("捕获到了空指针异常");}catch(ArithmeticException e){System.out.println("捕捉到了算数异常");}System.out.println("想你");}

这时候就有小伙伴问了,那另一个异常去哪了?

其实我们上面就已经讲述过了,一个异常出现,我们用try-catch捕获处理(这个异常下面的代码就不会执行了,直接执行捕获后的catch里面的代码,以及处理完程序后面的代码)

2.4:我们也可以有以下两种不推荐的捕获方式

1.

catch ( ArrayIndexOutOfBoundsException | NullPointerException e ) {
...
}
不过我们不推荐这种方式:太模糊了,到底是哪种异常
2.一次性捕获所有异常:
catch( Exception e ){    这是异常的父类
}
注意: 如果异常之间具有父子关系,一定是子类异常在前 catch ,父类异常在后 catch ,否则语法错误:(否则,相当于,你在前面就把水全部堵住了,异常根本流过来)
子类异常在前 catch ,父类异常在后 catch(还是有点作用的,防止程序异常终止嘛)

3:关于 "调用栈"(e.printStackTrace()

关于 "调用栈"

方法之间是存在相互调用关系的, 这种调用关系我们可以用 "调用栈" 来描述. 在 JVM 中有一块内存空间称为 "虚拟机栈" 专门存储方法之间的调用关系. 当代码中出现异常的时候, 我们就可以使用 e.printStackTrace(); 的方式查看出现异常代码的调用栈.

譬如上述代码:

 public static void main(String[] args){try{System.out.println(10/0);int[] arr = {1, 2, 3};System.out.println(arr[100]);}catch(NullPointerException e){System.out.println("捕获到了空指针异常");}catch(ArithmeticException e){System.out.println("捕捉到了算数异常");e.printStackTrace();}System.out.println("想你");}

4:finally

在写程序时, 有些特定的代码,不论程序是否发生异常,都需要执行,比如程序中打开的资源 :网络连接、数据库 连接,IO 流等, 在程序正常或者异常退出时,必须要对资源进进行回收
另外,因为 异常会引发程序的跳转,可能 导致有些语句执行不到 finally 就是用来解决这个问题的。
 public static void main(String[] args){try {System.out.println(10 / 0);int[] arr = {1, 2, 3};System.out.println(arr[100]);}catch (ArrayIndexOutOfBoundsException e){System.out.println("捕获到了数组越界异常");}finally {System.out.println("不管是否捕获到了异常,finally后面的都会被执行");}System.out.println("想你");}

就是想你没有被执行(finally就是用来处理善后工作的)

譬如关闭输入流

大家可以想想如果没有finally,这个程序刚才没有捕捉到异常,就异常终止了,但是输入流没有没关闭(这时候就造成了资源的泄露)

注意:一般我们不建议在 finally 中写 return (被编译器当做一个警告).

public static void main(String[] args) {
System.out.println(func());
}
public static int func() {
try {
return 10;
} finally {
return 20;
}
}
A: 10 B: 20 C: 30 D: 编译失败

选择B

finally 执行的时机是在方法返回之前 (try 或者 catch 中如果有 return 会在这个 return 之前执行 finally).
但是如果
finally 中也存在 return 语句 , 那么就会执行 finally 中的 return, 从而不会执行到 try 中原有的 return.
一般我们不建议在 finally 中写 return ( 被编译器当做一个警告 )
所以说我们这两个问题大家清楚了吗?
1. throw throws 的区别?
2. finally 中的语句一定会执行吗?
异常处理流程总结
程序先执行 try 中的代码
如果 try 中的代码出现异常 , 就会结束 try 中的代码 , 看和 catch 中的异常类型是否匹配 .
如果找到匹配的异常类型 , 就会执行 catch 中的代码
如果没有找到匹配的异常类型 , 就会将异常向上传递到上层调用者
.
无论是否找到匹配的异常类型 , finally 中的代码都会被执行到 ( 在该方法结束之前执行 ).
如果上层调用者也没有处理的了异常 , 就继续向上传递 .
一直到 main 方法也没有合适的代码处理异常 , 就会交给 JVM 来进行处理 , 此时程序就会异常终止 .

4:自定义异常类

接下来我们实现一个登录系统

public class LogIn {private String userName = "admin";private String password = "123456";public void loginInfo(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 static void main(String[] args) {LogIn logIn = new LogIn();logIn.loginInfo("admin", "1234561");}
}

但是总是sout输出太俗了,不能有效的提醒,这个时候我们就可以自定义一个异常类

但是我们应该如何定义一个异常类呢?(并且运行时能给我们检测出来)我们不妨看看其他异常类是怎么定义的?譬如:算数异常

那么我们也这样写两个异常

然后我们的代码就可以改成这个样子

public class LogIn {private String userName = "admin";private String password = "123456";public void loginInfo(String userName, String password) throws UserNameException,UserPasswordException {if (!this.userName.equals(userName)) {throw new UserNameException("用户名错误");}if (!this.password.equals(password)) {throw new UserPasswordException("登录密码错误");}System.out.println("登陆成功");}public static void main(String[] args) {LogIn logIn = new LogIn();try{logIn.loginInfo("admin", "1234561");}catch(UserNameException e){System.out.println("");}}
}

登录密码错误

其实定义方式就是这个

1. 自定义异常类,然后继承自 Exception 或者 RunTimeException
2. 实现一个带有 String 类型参数的构造方法,参数含义:出现异常的原因

注意继承Exception时是编译异常 ,继承RunTimeException是受查时异常

所以说,继承RunTimeException时就不需要抛出异常了(写代码时也不会报异常),如果我们不处理,运行时JVM会帮我们处理(最后返回发生异常的位置)

大家可以看一下如果我继承Exception时不抛出异常,这个时候就会报错,因为继承Exception是编译时异常(受查异常)

总之,大家根据自己的需求去选择是用编译时异常还是运行时异常就好了

上述就是 Java异常处理了解,熟悉到进阶的全部内容了,能看到这里相信您一定对小编的文章有了一定的认可,异常的出现,让我们对于程序有了更多的认识~~~

有什么问题欢迎各位大佬指出
欢迎各位大佬评论区留言修正

您的支持就是我最大的动力​​​!!!!

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

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

相关文章

Oracle数据库 用户管理模式下的冷备份与热备份

1. 用户管理模式下的冷备份 1.1. 通过数据库相关视图查询 查实例 select instance_name,version,status,archiver,database_status from v$instance; 查数据库 select dbid,name,log_mode from v$database; 查数据文件状态 select file_name,tablespace_name,status,o…

Solon 3.0.4 发布(Spring 的替代方案,备胎计划)

Solon 框架! 面向全场景的 Java 应用开发框架。从零开始构建(非 java-ee 架构),有灵活的接口规范与开放生态。新一代 Java 应用软件的生态基座。开放原子开源基金会,孵化项目。 追求: 更快、更小、更简单…

严格单元测试造就安全软件

在信息技术迅速发展的今天,软件在各个行业中扮演着至关重要的角色,尤其是在汽车行业,其中软件的可靠性和安全性直接影响到人们的生命安全。软件缺陷所带来的潜在风险不容小觑,尤其在涉及到自动驾驶和车辆控制等关键系统时&#xf…

基于CALMET诊断模型的高时空分辨率精细化风场模拟技术应用

在研究流场时,常用观测、模型风洞测试和数值模拟方法进行研究。但时常遇到研究区气象站点分布稀疏,不能代表周边复杂地形的风场。风洞模拟需要对地形进行实景的微缩,但实际过程中可能更关心近地表边界层的风场。风洞模拟一方面费用较高&#…

手写签名OCR识别-提升文档处理效率

随着数字化办公和智能化管理的普及,企业在日常业务中处理的各类单据和文件数量与日俱增。这些文件不仅包括了繁琐的文字信息,还涉及到重要的签名和印章等手写元素。尤其是在合同、协议、发票、审批单等文档中,手写签名不仅是身份确认的重要标…

Flutter:webview打开网页

官方文档地址 下方代码来自官方文档&#xff0c;先简单记录下如何通过webview打开某个http地址 添加权限&#xff1a;android/app/src/main/AndroidManifest.xml <uses-permission android:name"android.permission.INTERNET"/>pubspec.yaml webview_flutter: …

WPF+LibVLC开发播放器-音量控制和倍速控制

界面 界面上增加音量的控件和倍速控制控件 音量控制 主要也是一个Slider进度条控件来实现音量调节 我们这里设置默认的最大值为100&#xff0c;默认Value值也为100&#xff0c;默认声音开到最大 这里目前完全由前端控制音量调节&#xff0c;可以直接使用ValueChanged事件实…

重生之我在异世界学编程之C语言:初识一维和二维数组篇

大家好&#xff0c;这里是小编的博客频道 小编的博客&#xff1a;就爱学编程 很高兴在CSDN这个大家庭与大家相识&#xff0c;希望能在这里与大家共同进步&#xff0c;共同收获更好的自己&#xff01;&#xff01;&#xff01; 本文目录 引言正文数组的基本类型&#xff1a;一维…

Ubuntu 新加硬盤分區

1. 查看系統是否識別新的硬盤 lsblk 假設硬盤的名字為&#xff1a;sudo fdisk /dev/sda 注意硬盤類型為gpt&#xff0c;大小沒有限制 2. 硬盤分區 sudo fdisk /dev/sda 根據提示&#xff1a; n 新加分區&#xff0c;設置分區大小。 p 查看分區情況以及大小 d: 刪除也…

openGauss开源数据库实战十六

文章目录 任务十六 openGauss逻辑结构:触发器管理任务目标实施步骤一、测试openGauss的触发器1.创建测试表2.创建触发器对应的函数3.创建触发器4.测试触发器 二、触发器的类型1.行级触发器2.语句级触发器3.AFTER触发器和 BEFORE触发器 任务十六 openGauss逻辑结构:触发器管理 …

键值对形式读取值

效果 学习啦&#xff01; 异步请求数据方法&#xff1a; ①async/await <script setup> import { nextTick, onMounted } from vuelet Info {}const loadData async () > {try {const response await fetch(/json/info.json)const data await response.json()data…

Java面向对像编程OOP

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” 文章目录 OOP总结&#xff1a; 类和对象的说明Java中有无static修饰的方法的使用区别&#xff1a;✅️有stat…

【MARL】MAT论文阅读笔记

文章目录 前言一、如何产生这个想法(TRPO -> ) PPO -> MAPPO -> HAPPO -> MAT 二、多智能体优势值分解定理三、transformer 在MAT的应用四、伪代码简述五、实验效果 前言 正好有节课让我们调研最新的自己的方向的新论文&#xff0c;找到一篇自己觉得比较可行&…

深度学习模型——双塔模型(Two-Tower Model)详解

双塔模型是一种高效、灵活的深度学习模型结构&#xff0c;广泛用于推荐系统和信息检索等场景中。其核心思想是分别为两个实体&#xff08;如用户和物品、查询和文档&#xff09;构建独立的特征表示网络&#xff08;两座塔&#xff09;&#xff0c;并在共享语义空间中通过相似性…

回溯里面的基本概念

1.深度优先遍历和深度优先搜索&#xff08;DFS) 深度优先遍历 主要思路是从图中一个未访问的顶点 V 开始&#xff0c;沿着一条路一直走到底&#xff0c;然后从这条路尽头的节点回退到上一个节点&#xff0c;再从另一条路开始走到底...&#xff0c;不断递归重复此过程&#xff…

LeetCode 热题100(十五)【动态规划】(3)

15.7最长递增子序列&#xff08;中等&#xff09; 题目描述&#xff1a;leetcode链接 300. 最长递增子序列 给你一个整数数组 nums &#xff0c;找到其中最长严格递增子序列的长度。 子序列 是由数组派生而来的序列&#xff0c;删除&#xff08;或不删除&#xff09;数组中的元…

springboot如何集成工作流审批流,流程设计器集成,业务表单和工作流绑定,详细步骤和实际案例参考(源码)

前言 activiti工作流引擎项目&#xff0c;企业erp、oa、hr、crm等企事业办公系统轻松落地&#xff0c;一套完整并且实际运用在多套项目中的案例&#xff0c;满足日常业务流程审批需求。 一、项目形式 springbootvueactiviti集成了activiti在线编辑器&#xff0c;流行的前后端…

02_Node.js模块化

02_Node.js模块化 知识点自测 以下代码运行的结果是多少&#xff1f; const arr [10, 20, 30] const result arr.map(val > val 1).reduce((sum, val) > sum val, 0) console.log(result) A&#xff1a;60 B&#xff1a;63 <details><summary>答案</…

vulnhub kioptirx1.2 超详细wp

探测 nmap --min-rate 10000 -p- 192.168.128.134 最小速率10000 nmap -sT -sV -sC -O 192.168.128.134 web打点 无弱口令 暴露cms寻找exp searchsploit LotusCMS -m 16982 [输入id号和参数m可以直接把东西复制到当前目录] 查看txt里面发现 都是xss没有rce github搜索到一个…

vulnhub靶场之【grotesque】三

前言 靶机&#xff1a;grotesque-3 192.168.1.44 攻击 &#xff1a;kali 192.168.1.16 都是虚拟机环境&#xff0c;桥接模式 主机发现 使用arp-scan -l或者netdiscover -r 192.168.1.1/24搜索 信息收集 使用nmap扫描 防止有遗漏&#xff0c;再扫描全端口 网站信息收集 …