【Java】-- 异常

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


1.1 异常的概念

在Java中,将程序执行过程中发生的不正常行为称为异常。

public class Test {public static void main(String[] args) {//算术(ArithmeticException)异常
//        int a = 5/0;
//        System.out.println(a);//输出结果:Exception in thread "main" java.lang.ArithmeticException: / by zero//	at Test.main(Test.java:11)//数组下标越界(ArrayIndexOutOfBoundsException)异常
//        int array[] = {1, 2, 3};
//        System.out.println(array[10]);//输出结果:Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 10 out of bounds for length 3//	at Test.main(Test.java:20)//空指针(NullPointerException)异常int array1[] = null;System.out.println(array1[0]);//输出结果:Exception in thread "main" java.lang.NullPointerException: Cannot load from int array because "array1" is null//	at Test.main(Test.java:26)}
}

1.2 异常的体系结构

在这里插入图片描述
在这里插入图片描述

这些异常是一个一个的类,并且这三种异常都是继承于RunException 类,属于运行时异常。

  1. Throwable:是异常体系的顶层类,其派生出两个重要的子类, Error 和 Exception
  2. Error:指的是Java虚拟机无法解决的严重问题,比如:JVM的内部错误、资源耗尽等,典型代表:
    StackOverflowError和OutOfMemoryError,一旦发生回力乏术。
  3. Exception:异常产生后程序员可以通过代码进行处理,使程序继续执行。

1.3 异常的分类


1.3.1 编译时异常

在程序编译期间发生的异常,称为编译时异常,也称为受检查异常(Checked Exception)
Person类:

public class Person implements Cloneable{public String name;@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}

Test类:

public class Test {public static void main(String[] args) throws CloneNotSupportedException{Person person1 = new Person();Person person2 = (Person) person1.clone();//注意:clone出来的对象是Object类型的,需要强转。}
}

这段代码主要是克隆了一个Person对象,要想实现克隆,需要满足下面几点:

  1. 在Person类上实现Cloneable接口。
  2. 在Person类中重写clone方法。
  3. 在main函数中处理clone异常。

1.3.2 运行时异常

在程序执行期间发生的异常,称为运行时异常,也称为非受检查异常(Unchecked Exception)
RunTimeException以及其子类对应的异常,都称为运行时异常。比如:NullPointerException、
ArrayIndexOutOfBoundsException、ArithmeticException。

2. 异常的处理


2.1 防御式编程


2.1.1 事前防御型(LBYL)

在每一个可能发生异常的语句后面都紧接着进行异常的处理。

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

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

2.1.2 事后认错型(EAFP)

使用try-catch语句,将正常的流程语句放在一起,然后再添加对异常捕捉的语句。

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

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

2.2 异常的抛出

所有的受查时异常都要在后面throws一下,或者使用try catch。必须!!!!

  1. throw必须写在方法体内部
  2. 抛出的对象必须是Exception 或者 Exception 的子类对象
  3. 如果抛出的是 RunTimeException 或者 RunTimeException 的子类,则可以不用处理,直接交给JVM来处理
public class Test {public static void func(int[] array){if (array == null){throw new NullPointerException();}else {System.out.println("array != null");}}public static void main(String[] args) {func(null);}
}

输出结果:

Exception in thread "main" java.lang.NullPointerExceptionat Test.func(Test.java:11)at Test.main(Test.java:18)
  1. 如果抛出的是编译时异常,用户必须处理,否则无法通过编译
    在这里插入图片描述
    这里抛出了一个编译时异常(克隆异常),如果不对整个异常进行处理,那么抛出这条异常语句下面的红线就不会消失。可以像下面这样处理:

在这里插入图片描述
虽然现在func方法内不报错了,但是异常具有传递性,main方法调用了func,所以main方法也会报错。所以,此时也要在main方法中处理一下这个异常,还挺麻烦的。
在这里插入图片描述

  1. 异常一旦抛出,其后的代码就不会执行
public class Main {public static void func(int[] array){if (array == null){throw new NullPointerException();}else {System.out.println("array != null");}}public static void main(String[] args) {func(null);System.out.println("我执行了......");}
}

输出结果:

Exception in thread "main" java.lang.NullPointerExceptionat Main.func(Main.java:11)at Main.main(Main.java:19)

2.3 异常的捕获

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

2.3.1 异常声明throws

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

public class Main {public static void func(int[] array) throws CloneNotSupportedException,NullPointerException{if (array == null){throw new CloneNotSupportedException();}else {System.out.println("array != null");}}public static void main(String[] args) throws CloneNotSupportedException{func(null);}
}

这种在方法参数后面通过throws来处理异常的方式,其实并没有真正处理异常,只是骗过了编译器,

在这里插入图片描述
正常情况下,编译器的退出码都是0,但此时却是1,就表明程序是有问题的。要想真正去处理异常,需要使用try-catch。

2.3.2 try-catch捕获并处理

public class Main {public static void main(String[] args) {try {int[] array = {1,2,3};System.out.println(array[10]);//一旦程序抛出异常了,抛出异常的后面就不会执行了//当上面那行代码遇见数组下标越界异常就会直接来到下面的catch,// 走完catch,程序不会再回过头来执行下面的代码System.out.println("hahaha.....");//不会走这段代码}catch (ArrayIndexOutOfBoundsException e){e.printStackTrace();//打印出来当前所发生的异常信息System.out.println("数组下标越界异常.....");}catch (NullPointerException e){e.printStackTrace();System.out.println("空指针异常......");}//但是抛出的异常不影响外面的程序执行System.out.println("程序继续执行......");//执行}
}

输出结果:

java.lang.ArrayIndexOutOfBoundsException: Index 10 out of bounds for length 3at Main.main(Main.java:12)
数组下标越界异常.....
程序继续执行......

问:如果程序没有捕捉到异常呢?

public class Main {public static void main(String[] args) {try {int[] array = {1,2,3};System.out.println(array[10]);System.out.println("hahaha.....");//不会走这段代码}catch (NullPointerException e){e.printStackTrace();System.out.println("空指针异常......");}//但是抛出的异常不影响外面的程序执行System.out.println("程序继续执行......");//执行}
}

输出结果:

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 10 out of bounds for length 3at Main.main(Main.java:12)

答:. 如果抛出异常类型与catch时异常类型不匹配,即异常不会被成功捕获,也就不会被处理,继续往外抛,直到JVM收到后中断程序----异常是按照类型来捕获的

问:会不会同时打印数组下标越界异常和空指针异常?
答:不会。如果同时打印,就意味着这两个异常是同时抛出的。

问:会不会同时抛出两个异常?
答:不会。


public class Main {public static void main(String[] args) {try {int[] array = {1,2,3};System.out.println(array[10]);System.out.println("hahaha.....");//不会走这段代码}catch (ArrayIndexOutOfBoundsException | NullPointerException e){//这里的异常|可以写多个,但是变量e只有一个e.printStackTrace();//打印出来当前所发生的异常信息}//但是抛出的异常不应下外面的程序执行System.out.println("程序继续执行......");//执行}
}

输出结果:

java.lang.ArrayIndexOutOfBoundsException: Index 10 out of bounds for length 3at Main.main(Main.java:12)
程序继续执行......

如果异常之间具有父子关系,一定是子类异常在前catch,父类异常在后catch,否则语法错误。

在这里插入图片描述
在这里插入图片描述
遇到异常的时候,程序是从上往下来进行匹配的,此时父类RuntimeException在上面,就相当于下面的两个没有异常没有用。

2.3.3 finally

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

public class Main {public static void main(String[] args) {try {int[] array = {1,2,3};System.out.println(array[1]);System.out.println("hahaha.....");//不会走这段代码}catch (ArrayIndexOutOfBoundsException e){e.printStackTrace();//打印出来当前所发生的异常信息System.out.println("数组下标越界异常.....");}catch (NullPointerException e){e.printStackTrace();System.out.println("空指针异常......");}finally {System.out.println("finnal被执行了......");//无论有没有异常发生都会被执行}System.out.println("程序继续执行......");//执行}
}

public class Main {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);try {int a = scanner.nextInt();System.out.println(a);}catch (InputMismatchException e){System.out.println("输入类型匹配异常.....");}finally {scanner.close();//无论输入是否成功,都需要关闭这个输入资源}}
}

问:输入99,程序会返回99,还是100?

public class Main {public static int main(String[] args) {Scanner scanner = new Scanner(System.in);try {int a = scanner.nextInt();//输入99return a;}catch (InputMismatchException e){e.printStackTrace();System.out.println("输入类型匹配异常.....");}finally {return 100;}}
}

答:返回100。如果try和finally中都有return语句,最终return的值是以finally中的值为准的。建议不要在finally中进行return。此时编译器会有警告,黄色部分。


【面试题】:

  1. throw 和 throws 的区别?
    throw是抛出一个异常对象,throws是声明。
  2. finally中的语句一定会执行吗?
    一定会执行!!!!

2.4 异常的处理流程

关于 “调用栈”:

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

如果本方法中没有合适的处理异常的方式, 就会沿着调用栈向上传递。

【异常处理流程总结】

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

3. 自定义异常类

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

实现一个登录用户登录功能:
PasswordException类:

//public class PasswordException extends RuntimeException{//继承RuntimeException,就是运行时异常,也就是非受查异常
public class PasswordException extends Exception{//继承Exception,就是编译时异常,也就是受查异常public PasswordException(){}public PasswordException(String msg){super(msg);}
}

UserNameException类:

//public class UserNameException extends RuntimeException{
public class UserNameException extends Exception{public UserNameException(){}public UserNameException(String msg){super(msg);}
}

Login类:

public class Login {public String userName = "admin";public String password = "123456";public void loginInfo(String userName, String password)throws PasswordException,UserNameException{if(!this.userName.equals(userName)){throw new UserNameException("用户名错误异常.....");
//            System.out.println("用户名错误");
//            return;}if (!this.password.equals(password)){
//            System.out.println("密码错误");
//            return;throw new PasswordException("密码错误异常......");}System.out.println("登录成功!!!");}
}

Main类:

public class Main {public static void main(String[] args) {Login login = new Login();try {login.loginInfo("zhangsan","123456");}catch (PasswordException e){System.out.println("PasswordException");}catch (UserNameException e){System.out.println("UserNameException");}}
}

注意事项:

  • 自定义异常通常会继承自 Exception 或者 RuntimeException
  • 继承自 Exception 的异常默认是受查异常
  • 继承自 RuntimeException 的异常默认是非受查异常

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

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

相关文章

从零开始:利用Portainer CE和cpolar搭建NextCloud私有云存储

文章目录 前言1. 在PortainerCE中创建NextCloud容器2. 公网远程访问本地NextCloud容器2.1 内网穿透工具安装3.2 创建远程连接公网地址 3. 固定NextCloud私有云盘公网地址 前言 本文将介绍如何在本地利用Portainer CE的可视化界面创建NextCloud私有云盘容器,并通过c…

[安洵杯 2019]easy_web 详细题解

知识点: 编码转换 命令执行 linux空格_关键字绕过 打开页面 发现url 是 /index.php?imgTXpVek5UTTFNbVUzTURabE5qYz0&cmd 有img参数和cmd参数 cmd参数是没赋值的,随便赋值为123456 页面没有反应 鼠标移动到图片下面时发现有东西,当然直接查看页面源代码也可以发现 尝…

第2章 数据的表示和运算

王道学习 考纲内容 (一)数制与编码 进位计数制及其相互转换;定点数的编码表示 (二)运算方法和运算电路 基本运算部件:加法器;算术逻辑单元(ALU)…

Web3 游戏周报(11.03 - 11.09)

回顾上周的区块链游戏概况,查看 Footprint Analytics 与 ABGA 最新发布的数据报告。 【11.03 - 11.09】Web3 游戏行业动态: Ton Accelerator 推出名为「Synergy」的 500 万美元计划,旨在推动跨链创新,创造 TON 用户与 EVM 网络适应…

数据分析:16s差异分析DESeq2 | Corncob | MaAsLin2 | ALDEx2

禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍DESeq2原理计算步骤结果Corncob原理计算步骤结果MaAsLin2原理计算步骤结果ALDEx2原理计算步骤结果加载R包数据链接数据预处理微生物数据样本信息提取物种名称过滤零值保留结果读取…

H.264/H.265播放器EasyPlayer.js无插件H5播放器关于WASM的压缩优化

在当今的Web开发领域,流媒体播放器的性能和效率至关重要,尤其是在处理大型视频文件和高分辨率视频流时。EasyPlayer.js RTSP播放器作为一款先进的流媒体播放器,它在WebAssembly(WASM)的压缩优化方面表现出色&#xff0…

使用 Python 从 REST URL 下载文件

使用 Python 从 REST URL 下载文件,可以使用 requests 库来简化文件的下载和保存过程。以下是一个示例代码,展示了如何从给定的 REST API 或 URL 下载文件并保存到本地。 1、问题背景 我们需要编写一个脚本,从一个支持 REST URL 的网站下载一…

SpringMVC学习记录(三)之响应数据

SpringMVC学习记录(三)之响应数据 一、页面跳转控制1、快速返回模板视图2、转发和重定向 二、返回JSON数据1、前置准备2、ResponseBody 三、返回静态资源1、静态资源概念2、访问静态资源 /*** TODO: 一个controller的方法是控制层的一个处理器,我们称为h…

CSDN做样板,教我们如何为新网站引流

CSDN为我们做了个很好的例子,详细请看下图 亮点分析: 1. 未采用硬广在网站上进行引流。减少了给用户在直觉上的造成的反感; 2. 在GitHub的转跳页面中,植入额外的关联网站链接。虽然对用户解决问题没啥鸟用,但是人家能…

什么是头皮EA(剥头皮EA)?

在许多外汇交易者的眼中,剥头皮交易一直是一个神秘的存在。一部分人认为它是一种“外汇禁招”,而另一部分人则认为它比日内交易更容易盈利。那么,外汇剥头皮到底是什么?它与点差之间又有怎样的关系?本文将对剥头皮交易…

华为ensp防火墙配置(纯享版)

文章目录 前言一、拓扑结构二、配置步骤1.路由器配置(路由器代替互联网)2.server和pc配置3.防护墙配置4.测试 总结 前言 防火墙是生活和项目中不可或缺的一部分,本篇文章对华为的ensp防火墙配置做一个总结。在之前的dhcp配置中有软件的下载地…

区块链技术在数字版权管理中的应用

💓 博客主页:瑕疵的CSDN主页 📝 Gitee主页:瑕疵的gitee主页 ⏩ 文章专栏:《热点资讯》 区块链技术在数字版权管理中的应用 区块链技术在数字版权管理中的应用 区块链技术在数字版权管理中的应用 引言 区块链技术概述 …

docker 拉取MySQL8.0镜像以及安装

目录 一、docker安装MySQL镜像 搜索images 拉取MySQL镜像 二、数据挂载 在/root/mysql/conf中创建 *.cnf 文件 创建容器,将数据,日志,配置文件映射到本机 检查MySQL是否启动成功: 三、DBeaver数据库连接 问题一、Public Key Retrieval is not allowed 问题…

VUE3中Element table表头动态展示合计信息(不是表尾合计)

一、背景 原型上需要对两个字段动态合计,输出摘要信息 原先想到是的Element的 :summary-method,发现不是动态,所以换监听来实现 二、vue代码 <el-table v-model="loading" :data="itemList"><el-table-column label="药品名称" pro…

让直播流量不再是“数字游戏”!本地生活+AI数字人的共赢方式 !

如今&#xff0c;数字化浪潮席卷全球&#xff0c;直播行业竞争愈发激烈。许多人都希望能够将自己直播间所产生的热度实时变现&#xff0c;但总是没有头绪或者是把握不住机会&#xff0c;就这样让直播流量从白白流失。即便是有人使用上创新型智能AI数字人系统&#xff0c;也少有…

jmeter常用配置元件介绍总结之前置处理器、测试片段

系列文章目录 安装jmeter jmeter常用配置元件介绍总结之前置处理器、测试片段 6.前置处理器6.1用户参数6.2取样器超时6.3.测试片段6.4JSR223 PreProcessor6.5.JDBC PreProcessor 6.前置处理器 在取样器请求之前执行的操作&#xff0c;优先级比取样器高&#xff0c;用来处理一些…

前端代码分析题(选择题、分析题)——this指向、原型链分析

this指向 普通函数&#xff1a;this 的指向由调用方式决定&#xff0c;可以是全局对象、调用该函数的对象&#xff0c;或者显式指定的对象。箭头函数&#xff1a;this 的指向在定义时确定&#xff0c;始终继承自外层函数作用域的 this&#xff0c;不会被调用方式影响。 var obj…

Linux下进程链接结构,命令行参数,环境变量

bash 是一种 shell。在 Linux 系统中&#xff0c;当我们在终端输入命令时&#xff0c;通常是在一个 shell 环境下进行的。如果这个 shell 是 bash&#xff0c;那么所有命令行执行的命令都是 bash 的子进程。 1.Linux下进程链接结构 进程链接补充知识&#xff1a; 所有进程都…

Android studio中关于printf和print和println的区别

print:为一般输出&#xff0c;同样不能保留精度格式转化&#xff0c;也不能换行输出&#xff0c;输出需要加上换行符printf:常用于格式转换&#xff0c;但需要注意不是换行输出&#xff0c;只用于精度转换&#xff0c;跟C语言的printf一样的&#xff0c;输出需要加上换行符prin…

GISBox VS ArcGIS:分别适用于大型和小型项目的两款GIS软件

在现代地理信息系统&#xff08;GIS&#xff09;领域&#xff0c;有许多大家耳熟能详的GIS软件。它们各自具有独特的优势&#xff0c;适用于不同的行业需求和使用场景。在众多企业和开发者面前&#xff0c;如何选择合适的 GIS 软件成为了一个值得深入思考的问题。今天&#xff…