Java SE 认识异常 (Java SE完结篇)

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

1.1 异常的概念

在我们的生活中,一个人如果表情痛苦,我们可能会问: 你是生病了吗? 需要我陪你去看医生吗?
在这里插入图片描述
程序也和人是一样的,均会发生一些"生病"的行为,比如: 数据格式不对, 数组越界,网络中断等, 我们把这种程序出现的"生病"行为称为"异常".比如在我们之前写代码经常遇到的"报红",大多数都是异常.

  1. 算数异常
    在这里插入图片描述
  2. 数组越界异常
    在这里插入图片描述
  3. 空指针异常
    在这里插入图片描述
    从上述的展示中可以看出,异常也分不同的类型,都有与其对应的类来进行描述,总的来说,异常本质上是一种类.

1.2 异常的体系结构

异常的种类很多,为了对不同类型的异常或错误进行更好的管理,Java内部制定了异常的体系结构:
在这里插入图片描述

从上图中我们可以看到:

  1. Throwable是异常体系的顶层类,由它派生出了两个子类:Error和Exception,即错误和异常.
  2. Error:指的是Java虚拟机无法解决的严重问题,如JVM内部错误,资源耗尽等,典型代表就是StackOverflowError(栈溢出错误).
  3. Exception: 异常产生后程序员可以通过修改代码进行处理,可以使得程序继续执行.

1.3 异常的分类

根据异常发生的时间不同,我们可以将异常分为:

  1. 编译时异常
    在程序编译期间发生的异常,称为编译时异常,也称为受查异常,我们在用idea编译器时,编译器会在出现异常的语句下面划红线.
 public class Person {private int age;private String name;public Person(int age, String name) {this.age = age;this.name = name;}@Overrideprotected Object clone() {return super.clone();}
}

在这里插入图片描述
从上述的代码我们可以看出,在clone方法之后我们没有对发生的异常进行声明,所以在放重写的方法那里产生了编译时异常,在clone下面划了红线.

  1. 运行时异常
    在程序执行的期间发生的异常,称为运行时异常,也称为非受查异常,RunTimeException以及子类对应的异常,都叫运行时异常,比如我们上面提到的: 算数异常(ArithmeticException),空指针异常(NullPointerException),数组越界异常(ArraryIndexOutOfBoundsException).

2. 异常的处理

2.1 防御式编程

错误在代码中是客观存在的.此时就需要把出现的错误和异常及时通知程序员,主要有以下两种方式:

  1. LBYL: look before your leap. 在操作之前就进行充分的检查,即:事前防御,语法格式如下:
boolean ret = false;
ret = 操作1;
if(!ret){处理异常1;return;
}
ret = 操作2;
if(!ret){处理异常2;return;
}
......

缺点: 正常的流程和错误的处理混在一起,代码显得比较凌乱.
2. EAFP: It’s Easier to ask forgiveness than permission. 事后获取原谅比事前获取许可更容易.也就是先操作,遇到的问题放在最后一起处理,即:事后认错型.

try{操作1;操作2;......
}catch(异常1){处理异常1;
}catch(异常2){处理异常2;
}
.......

优势: 正常的流程和错误的处理是分开的,代码更加清晰,容易理解.
异常处理的核心思想就是EAFP
在Java中,处理异常主要有5个关键字: throw, try, catch, finally , throws.

2.2 异常的抛出

在编写程序时, 如果程序中出现错误, 此时就需要将错误的信息报告给调用者.
在Java中,可以使用throw关键字,抛出一个指定的异常对象,来将错误信息来报告给调用者.具体语法格式如下:

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

我们举一个例子来说明:例如我们要获取数组任意位置的元素和方法.

public static int getElement(int[] array,int index){if (array == null){throw new NullPointerException("element of array is null");}if (index > array.length-1||index < 0){throw new ArrayIndexOutOfBoundsException("index is out of array");}return array[index];}
public static void main(String[] args) {int[] array = {1,2,3};getElement(array,3);int[] array2 = null;getElement(array2,2);}

在这里插入图片描述
在这里插入图片描述
[注意事项]:

  1. throw必须写在方法内部
  2. 抛出的对象必须是Exception或者Exception的子类对象
  3. 如果抛出的是RunTimeException或者RunTimeException的子类,则可以不处理,直接交给jvm来处理
  4. 如果抛出的是编译时异常,用户必须处理,否者无法通过编译
  5. 异常一旦抛出,其后的代码就不会被执行

2.3 异常的捕获

异常的捕获,通常有两种: 异常的throws声明以及try-catch捕获处理

2.3.1 异常throws声明

处于方法声明的参数表之后,当方法中抛出编译时异常,用户不想处理该异常,此时就可以借助throws将异常抛给调用者来处理,即当前方法不处理异常,提醒方法的调用者处理该异常.若调用者没有处理或throws,就会报错,语法格式如下:

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

下面我们拿我们上面提到的clone方法来举例

public class Person {private int age;private String name;public Person(int age, String name) {this.age = age;this.name = name;}@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}
public class MyException {public static void main(String[] args)throws CloneNotSupportedException {Person p1 = new Person(12,"zhangsan");Person p2 = (Person) p1.clone();}
}

在这里插入图片描述
上述的代码我们可以看出如果我们在main方法调用clone方法的时候也没有直接处理异常,把异常throws了,该异常就会交给jvm来处理,若处理失败,程序会在出现异常的地方立即终止.
我们对上述代码进行一定地修正

public class Person implements Cloneable {private int age;private String name;public Person(int age, String name) {this.age = age;this.name = name;}@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}
public class MyException {public static void main(String[] args) throws CloneNotSupportedException{Person p1 = new Person(12,"zhangsan");Person p2 = (Person) p1.clone();}
}

上述代码我们对Person实现了Cloneable接口,jvm处理异常成功,程序运行之后便不会报错.

[注意事项]

  1. throws必须跟在方法的参数列表之后.
  2. 声明的异常必须是Exception或者是Exception的子类.
  3. 方法内部如果有多个异常,throws之后必须跟上多个异常,之间用逗号隔开,若抛出的异常具有父子关系,直接声明父类即可.
public static int getElement(int[] array,int index) throws RuntimeException{//声明运行时异常即可if (array == null){throw new NullPointerException("element of array is null");}if (index > array.length-1||index < 0){throw new ArrayIndexOutOfBoundsException("index is out of array");}return array[index];}
  1. 调用抛出异常的方法时,必须对异常进行处理,或者继续使用throws抛出.
public static int getElement(int[] array,int index) throws RuntimeException{if (array == null){throw new NullPointerException("element of array is null");}if (index > array.length-1||index < 0){throw new ArrayIndexOutOfBoundsException("index is out of array");}return array[index];}
public static void main(String[] args) throws RuntimeException{int[] array = {1,2,3};getElement(array,3);int[] array2 = null;getElement(array2,2);}
2.3.2 捕获并处理

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

try{操作1;操作2;
}catch(异常1){处理异常1;
}catch(异常2){处理异常2;
}finally{此处的代码一定会被执行
}
//后续代码

如果捕获异常成功,并且异常被处理成功,会跳出try-catch结构,之后的代码也会被执行,若有异常没有被捕获到,后续的代码就不会被执行.我们下面举个例子:

public static int getElement(int[] array,int index){if (array == null){throw new NullPointerException("element of array is null");}if (index > array.length-1||index < 0){throw new ArrayIndexOutOfBoundsException("index is out of array");}return array[index];}public static void main(String[] args) {try {int[] array = {1,2,3};getElement(array,3);}catch (ArrayIndexOutOfBoundsException e){System.out.println("处理了数组越界异常");}System.out.println("异常处理结束,后续代码被执行");}

关于异常的处理方式
异常的种类有很多, 我们要根据不同的业务场景来决定.
对于比较严重的问题(例如和算钱相关的场景), 应该让程序直接崩溃, 防止造成更严重的后果
对于不太严重的问题(大多数场景), 可以记录错误日志, 并通过监控报警程序及时通知程序猿
对于可能会恢复的问题(和网络相关的场景), 可以尝试进行重试.
在我们当前的代码中采取的是经过简化的第二种方式. 我们记录的错误日志是出现异常的方法调用信息, 能很快速的让我们找到出现异常的位置. 以后在实际工作中我们会采取更完备的方式来记录异常信息.

[注意事项]

  1. try代码块中,如果有一个地方抛出了异常,这个地方之后的代码不会被执行.
public static int getElement(int[] array,int index) throws RuntimeException{if (array == null){throw new NullPointerException("element of array is null");}if (index > array.length-1||index < 0){throw new ArrayIndexOutOfBoundsException("index is out of array");}return array[index];}public static void main(String[] args) {try {int[] array = {1,2,3};getElement(array,3);int[] array2 = null;getElement(array2,2);}catch (NullPointerException e){System.out.println("处理了空指针异常");}catch (ArrayIndexOutOfBoundsException e){System.out.println("处理了数组越界异常");}System.out.println("异常处理结束,后续代码被执行");}

在这里插入图片描述
由此可见,运行结果中并,没有打印空指针异常.即可说明上述一点.
2. 如果异常类型与catch时的异常类型不匹配,异常就不会被捕获成功,也不会被处理,继续抛出,知道JVM收到后终止.

ublic static void main(String[] args) {try {int[] array = {1,2,3};getElement(array,3);}catch (NullPointerException e){System.out.println("处理了空指针异常");}System.out.println("异常处理结束,后续代码被执行");}public static int getElement(int[] array,int index) throws RuntimeException{if (array == null){throw new NullPointerException("element of array is null");}if (index > array.length-1||index < 0){throw new ArrayIndexOutOfBoundsException("index is out of array");}return array[index];}

在这里插入图片描述
3. try中可能会有多个异常,必须用多个catch来捕获

public static void main(String[] args) {int[] arr = {1, 2, 3};try {System.out.println("before");arr = null;System.out.println(arr[100]);System.out.println("after");} catch (ArrayIndexOutOfBoundsException e) {System.out.println("这是个数组下标越界异常");e.printStackTrace();} catch (NullPointerException e) {System.out.println("这是个空指针异常");e.printStackTrace();}System.out.println("after try catch");}

若多个异常的处理方式完全相同,也可以写成这样:

catch (ArrayIndexOutOfBoundsException|NullPointerException){
......
}
  1. 如果异常之间有父子关系,一定是子类在前,父类在后.
public static void main(String[] args) {try {int[] array = {1,2,3};getElement(array,3);}catch (Exception e){System.out.println("处理了异常");}catch (ArrayIndexOutOfBoundsException e){System.out.println("处理了数组越界异常");}System.out.println("异常处理结束,后续代码被执行");}

在这里插入图片描述
数组越界异常已经被第一个catch处理了,该异常的捕获类型属于数组越界处理的父类,而后面的子类没有处理到,所以会报错.

2.3.3 finally

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

语法格式:
try{// 可能会发生异常的代码
}catch(异常类型 e){// 对捕获到的异常进行处理
}finally{// 此处的语句无论是否发生异常,都会被执行到
}// 如果没有抛出异常,或者异常被捕获处理了,这里的代码也会执行
public static void main(String[] args) {try {int[] array = {1,2,3};getElement(array,3);}catch (NullPointerException e){System.out.println("处理了空指针异常");}finally {System.out.println("一定会被执行");}System.out.println("异常处理结束,后续代码被执行");}

在这里插入图片描述
我们从上述执行结果可以看到,无论异常有没有被捕获到,finally一定会被执行到.
finally的代码一般用来进行一些资源清理的扫尾工作.例如:

public class TestFinally {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 {if(null != sc){sc.close();}return 0;
}

从上述代码中,我们知道如果sc被成功调用,直接返回了,若没有finally语句,sc的资源没有进行关闭,造成了资源泄露.

面试题

  1. throw 和 throws 的区别?throw用来抛出异常,throws用来声明异常,提醒调用者.
  2. finally中的语句一定会执行吗?一定会.

2.4 异常处理的流程 (总结)

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

3. 自定义异常

Java出了本身给出的异常类之外,我们还可以自定义异常,在重写的构造方法中,我们可以自定义异常原因.

ublic class PassWordException extends RuntimeException{//继承于运行时异常public PassWordException(String message) {//构造方法输入出现异常的原因super(message);}
}
public static void main(String[] args) {String password = "123457";if (password != "123456"){throw new PassWordException("password is false");}}

在这里插入图片描述
[注意事项]
继承自Exception时默认是编译时异常.

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

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

相关文章

Ps:文字工具

工具箱里的文字工具组中包含了四种工具&#xff1a; 横排文字工具 Horizontal Type Tool 直排文字工具 Vertical Type Tool 横排文字蒙版工具 Horizontal Type Mask Tool 直排文字蒙版工具 Vertical Type Mask Tool 快捷键&#xff1a;T 横排文字蒙版工具和直排文字蒙版工具…

iOS--第二章block

第二章block blocks 概要Blocks模式Blocks语法Blocks类型变量截获自动变量值_block 说明符 Blocks的实现Block的实质截获自动变量值_block说明符Block存储域 blocks 概要 Blocks是c语言的扩展&#xff0c;block是一个带有自动变量值的匿名函数&#xff0c;它也是一个数据类型&…

Lua面向对象

封装 实现了New方法&#xff0c;相当于创建了一个表obj&#xff0c;并设置元表可以通过obj表去访问__index指向表中的数据 继承 通过大G表传入字符串创建表&#xff0c;

安卓国产百度网盘与国外云盘软件onedrive对比

我更愿意使用国外软件公司的产品&#xff0c;而不是使用国内百度等制作的流氓软件。使用这些国产软件让我不放心&#xff0c;他们占用我的设备大量空间&#xff0c;在我的设备上推送运行各种无用的垃圾功能。瞒着我&#xff0c;做一些我不知道的事情。 百度网盘安装包大小&…

golang常用库之-golang常用库之-ladon包 | 基于策略的访问控制

文章目录 golang常用库之-ladon包 | 基于策略的访问控制概念使用策略 条件 Conditions自定义conditionLadon Condition使用示例 持久化访问控制(Warden) 结合 Gin 开发一个简易 ACL 接口参考 golang常用库之-ladon包 | 基于策略的访问控制 https://github.com/ory/ladon Lado…

Unity中UGUI中的PSD导入工具的原理和作用

先说一下PSD导入工具的作用&#xff0c;比如在和美术同事合作开发一个背包UI业务系统时&#xff0c;美术做好效果图后&#xff0c;程序在UGUI中制作好界面&#xff0c;美术说这个图差了2像素&#xff0c;那个图位置不对差了1像素&#xff0c;另外一个图大小不对等等一系列零碎的…

从零自制docker-4-【PID Namespace MOUNT Namespace】

文章目录 PID namespace代码mountnamespace通俗理解代码 PID namespace 每个命名空间都有独立的PID空间&#xff0c;即每个命名空间的进程都由一开始分配。 新建立的进程内部进程ID为1 代码 package main import ("log""os/exec""os""sy…

MySQL_数据库图形化界面软件_00000_00001

目录 NavicatSQLyogDBeaverMySQL Workbench可能出现的问题 Navicat 官网地址&#xff1a; 英文&#xff1a;https://www.navicat.com 中文&#xff1a;https://www.navicat.com.cn SQLyog 官网地址&#xff1a; 英文&#xff1a;https://webyog.com DBeaver 官网地址&…

Spring MVC文件上传配置

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl 文件上传 Spring MVC文件上传基于Servlet 3.0实现&#xff1b;示例代码如下&#xff1a; Overrideprotected void customizeRegistration(ServletRegistration.Dynamic reg…

【开源鸿蒙】编译OpenHarmony轻量系统QEMU RISC-V版

文章目录 一、背景介绍二、准备OpenHarmony源代码三、准备hb命令3.1 安装hb命令3.2 检查hb命令 四、编译RISC-V架构的OpenHarmony轻量系统4.1 设置hb构建目标4.2 启动hb构建过程 五、问题解决5.1 hb set 报错问题解决 六、参考链接 开源鸿蒙坚果派&#xff0c;学习鸿蒙一起来&a…

AJAX概念和axios使用、URL、请求方法和数据提交、HTTP协议、接口、form-serialize插件

AJAX概念和axios使用 AJAX概念 AJAX就是使用XMLHttpRequest对象与服务器通信&#xff0c;它可以使用JSON、XML、HTML和text文本等格式发送和接收数据&#xff0c;AJAX最吸引人的就是它的异步特性&#xff0c;也就是说它可以在不重新刷新页面的情况下与服务器通信&#xff0c;…

GPU密集型计算性能优化的方法和技术

对GPU密集型计算进行性能优化的方法和技术多种多样。通过一些优化策略和技术需要综合考虑应用程序的具体需求、所使用的GPU硬件、以及编程模型和库的选择。通过不断地分析和调整&#xff0c;可以实现GPU计算性能的持续提升。以下是一些常用的优化策略和技术&#xff1a; 算法优…

uni-popup(实现自定义弹窗提示、交互)

一般提示框的样式&#xff0c;一般由设计稿而定&#xff0c;如果用uniapp的showmodel&#xff0c;那个并不能满足我们需要的自定义样式&#xff0c;所以最好的方式是我们自己封装一个&#xff01;&#xff08;想什么样就什么样&#xff09;&#xff01; 一、页面效果 二、使用…

Redis的常用操作-String字符串类型

一、redis简介 redis 就是一个数据库&#xff0c;与传统数据库不同的是 redis 的数据是存在内存中的&#xff0c;所以读写速度非常快&#xff0c;因此redis 被应用于缓存方向。另外&#xff0c;redis 也经常用来做分布式锁。 二、redis作缓存&#xff0c;主要用来实现…

WebSocket多服务实例下的消息推送

最近在做一个项目&#xff0c;涉及到前后端的消息同步、推送&#xff0c;进而我们选择使用webSocket的方案进行实现&#xff0c;但是当websocket服务端部署在多个实例下&#xff0c;会出现前端socket意外断开导致无法收到消息的情况。手下我们先说我们的实现方案&#xff1a; 1…

【JavaScript】JavaScript 运算符 ⑤ ( 赋值运算符 | 基础赋值运算符 与 复合赋值运算符 )

文章目录 一、JavaScript 赋值运算符1、赋值运算符 概念2、基础赋值运算符 与 复合赋值运算符3、复合赋值运算符4、完整代码示例 一、JavaScript 赋值运算符 JavaScript 赋值运算符种类 : 基础赋值运算符 : 等于 : ; 复合赋值运算符 : 加等 : 减等 : -乘等 : *除等 : /取模等…

MongoDB的安装方法图文详细

官网&#xff1a;www.mongodb.com 选择 Products > Community Edition 就能进入社区版 在这里下载 windows 版对应的安装包 注意&#xff1a;6.0.1 版本的 MongoDB 配置环境变量有问题&#xff0c;并且我不知道怎么解决&#xff0c;如果想要避免出问题&#xff0c;建议使…

【javaWeb】在webapp中手动发布一个应用

标题 &#x1f432;一、为什么要在webapp中手动发布一个应用&#x1f389;二、手动发布步骤1.下载Tomcat2.解压并安装3.在webapps中创建文档 ✨三、总结 &#x1f432;一、为什么要在webapp中手动发布一个应用 好处解释灵活性手动发布应用程序可以根据自己的需求进行自定义配置…

《PyTorch 深度学习实战》- 第一章 深度学习回顾和PyTorch简介

《PyTorch 深度学习实战》- 第一章 深度学习回顾和PyTorch简介 1.1 PyTorch的历史 pytorch前身是Torch&#xff0c;Torch使用Lua和C语言&#xff0c;而后因为python的兴起&#xff0c;演变成为PyTorch。事实上&#xff0c;pytorch是提供动态图功能的chainer分支。 pytorch与…

【php基础】输出、变量、

php基础补充 1. 输出2.和"的区别3.变量3.1变量的命名规则3.2 两个对象指向同一个值3.3 可变变量 4.变量的作用域5. 检测变量 1. 输出 echo: 输出 print: 输出&#xff0c;输出成功返回1 print_r(): 输出数组 var_dump(): 输出数据的详细信息&#xff0c;带有数据类型和数…