5 - 异常处理

目录

1. 总览

1.1 Exception 与 Error

1.2 checked unchecked 异常

1)使用 try-catch 进行捕获

2)使用 throws 关键字抛出

1.3 throw 与 throws

1)throw

2)throws

3)区别

1.4 try-catch-finally

2. try with resources

2.1 try–catch-finally 不足

2.2 解决问题

 3. 异常处理实践

3.1 尽量不要捕获 RuntimeException

3.2 尽量使用 try-with-resource 来关闭资源

3.3 不要捕获  Throwable

3.4 不要省略异常信息的记录

3.5 不要记录了异常又抛出异常

3.6 不要在 finally 块中使用 return

3.7 抛出具体定义的检查性异常而不是 Exception

3.8 捕获具体的子类而不是捕获 Exception 类

3.9 自定义异常时不要丢失堆栈跟踪

3.10 finally 块中不要抛出任何异常

3.11 不要在生产环境中使用 printStackTrace()

3.12 对于不打算处理的异常,直接使用 try-finally,不用 catch

3.13 记住早 throw 晚 catch 原则

3.14 只抛出和方法相关的异常

3.15 切勿在代码中使用异常来进行流程控制

3.16 尽早验证用户输入以在请求处理的早期捕获异常

3.17 一个异常只能包含在一个日志中

3.18 将所有相关信息尽可能地传递给异常

3.19 终止掉被中断线程

3.20 对于重复的 try-catch,使用模板方法


1. 总览

异常是指中断程序正常执行的一个不确定的事件

有了异常处理机制后,程序在发生异常的时候就不会中断,我们可以对异常进行捕获,然后改变程序执行的流程

1.1 Exception 与 Error

Exception 和 Error 都继承了 Throwable 类

NoClassDefFoundError 和 ClassNotFoundException 有什么区别?

都是由于系统运行时找不到要加载的类导致的,但是触发的原因不一样

  • NoClassDefFoundError:程序在编译时可以找到所依赖的类,但是在运行时找不到指定的类文件;原因:可能是 jar 包缺失或者调用了初始化失败的类
  • ClassNotFoundException:当动态加载 Class 对象的时候找不到对应的类时抛出该异常;原因:要加载的类不存在或者类名写错了

Error 的出现,意味着程序出现了严重的问题,而这些问题不应该再交给 Java 的异常处理机制来处理,程序应该直接崩溃掉

Exception 的出现,意味着程序出现了一些在可控范围内的问题,应当采取措施进行挽救

1.2 checked unchecked 异常

checked 异常(检查型异常)在源代码里必须显式地捕获或者抛出,否则编译器会提示你进行相应的操作

unchecked 异常(非检查型异常)就是运行时异常,通常是可以通过编码进行规避的,并不需要显式地捕获或者抛出

1)使用 try-catch 进行捕获

try {Class clz = Class.forName("com.itwanger.s41.Demo1");
} catch (ClassNotFoundException e) {e.printStackTrace();
}

注:该方法会将异常的堆栈信息打印到标准的控制台下;如果是生产环境,必须使用日志框架把异常的堆栈信息输出到日志系统中,否则可能没办法跟踪

2)使用 throws 关键字抛出

public class Demo1 {public static void main(String[] args) throws ClassNotFoundException {Class clz = Class.forName("com.itwanger.s41.Demo1");}
}

这样做的好处是不需要对异常进行捕获处理,只需要交给 Java 虚拟机来处理即可

坏处就是没法针对这种情况做相应的处理

1.3 throw 与 throws

1)throw

throw 关键字,用于主动地抛出异常

throw new exception_class("error message");

2)throws

throws 与 try-catch 对比

public static void main(String args[]){try {myMethod1();} catch (ArithmeticException e) {// 算术异常} catch (NullPointerException e) {// 空指针异常}
}
public static void myMethod1() throws ArithmeticException, NullPointerException{// 方法签名上声明异常
}

3)区别

  1. throws 关键字用于声明异常,它的作用和 try-catch 相似;而 throw 关键字用于显式的抛出异常。
  2. throws 关键字后面跟的是异常的名字;而 throw 关键字后面跟的是异常的对象
  3. throws 关键字出现在方法签名上,而 throw 关键字出现在方法体里
  4. throws 关键字在声明异常的时候可以跟多个,用逗号隔开;而 throw 关键字每次只能抛出一个异常

1.4 try-catch-finally

try {// 可能发生异常的代码
}catch {// 异常处理
}finally {// 必须执行的代码
}

finally 块常用来关闭一些连接资源,比如说 socket、数据库链接、IO 输入输出流等

OutputStream osf = new FileOutputStream( "filename" );
OutputStream osb = new BufferedOutputStream(opf);
ObjectOutput op = new ObjectOutputStream(osb);
try{output.writeObject(writableObject);
} finally{op.close();
}

注:即使 try 块有 return,finally 块也会执行

 当然也存在 不执行 finally 的情况:

  • 死循环
  • 执行了 System.exit() 

System.exit() 与 return 不同,前者是用来退出程序的,后者只是回到了上一级方法调用


2. try with resources

2.1 try–catch-finally 不足

在 Java 7 之前,try–catch-finally 是确保资源会被及时关闭的最佳方法,无论程序是否会抛出异常

public class TrycatchfinallyDecoder {public static void main(String[] args) {BufferedReader br = null;try {String path = TrycatchfinallyDecoder.class.getResource("/a.txt").getFile();String decodePath = URLDecoder.decode(path,"utf-8");br = new BufferedReader(new FileReader(decodePath));String str = null;while ((str =br.readLine()) != null) {System.out.println(str);}} catch (IOException e) {e.printStackTrace();} finally {if (br != null) {try {br.close();} catch (IOException e) {e.printStackTrace();}}}}
}

存在一个严重的隐患:try 与 finally 都有可能抛出 IOException,那么程序的调试任务就变得复杂了起来,因为不确定到底是哪一处出了错误

2.2 解决问题

try-with-resources 可以解决该问题,前提是需要释放的资源(比如 BufferedReader)实现了 AutoCloseable 接口,并提供 close() 方法即可

try (BufferedReader br = new BufferedReader(new FileReader(decodePath));) {String str = null;while ((str =br.readLine()) != null) {System.out.println(str);}
} catch (IOException e) {e.printStackTrace();
}

把要释放的资源写在 try 后的 () 里,如果有多个资源(BufferedReader 和 PrintWriter)需要释放的话,可以直接在 () 中添加(就像写成员属性定义的语句一样)

try (BufferedReader br = new BufferedReader(new FileReader(decodePath));PrintWriter writer = new PrintWriter(new File(writePath))) {String str = null;while ((str =br.readLine()) != null) {writer.print(str);}
} catch (IOException e) {e.printStackTrace();
}

如果想释放自定义资源的话,只要让它实现 AutoCloseable 接口,并提供 close() 方法即可

public class TrywithresourcesCustom {public static void main(String[] args) {try (MyResource resource = new MyResource();) {} catch (Exception e) {e.printStackTrace();}}
}class MyResource implements AutoCloseable {@Overridepublic void close() throws Exception {System.out.println("关闭自定义资源");}
}

本质:编译器主动为 try-with-resources 进行了变身,在 try 中调用了 close() 方法

好处:是不会丢失任何异常


 3. 异常处理实践

异常处理实践经验

3.1 尽量不要捕获 RuntimeException

正例:

if (obj != null) {//...
}

反例:

try { obj.method(); 
} catch (NullPointerException e) {//...
}

3.2 尽量使用 try-with-resource 来关闭资源

当需要关闭资源时,尽量不要使用 try-catch-finally,禁止在 try 块中直接关闭资源

反例:

public void doNotCloseResourceInTry() {FileInputStream inputStream = null;try {File file = new File("./tmp.txt");inputStream = new FileInputStream(file);inputStream.close();} catch (FileNotFoundException e) {log.error(e);} catch (IOException e) {log.error(e);}
}

原因:一旦 close()之前发生异常,那么资源就无法关闭

正例:直接使用 try-with-resource

public void automaticallyCloseResource() {File file = new File("./tmp.txt");try (FileInputStream inputStream = new FileInputStream(file);) {} catch (FileNotFoundException e) {log.error(e);} catch (IOException e) {log.error(e);}
}

如果资源没有实现 AutoCloseable 接口,就在 finally 块关闭流

public void closeResourceInFinally() {FileInputStream inputStream = null;try {File file = new File("./tmp.txt");inputStream = new FileInputStream(file);} catch (FileNotFoundException e) {log.error(e);} finally {if (inputStream != null) {try {inputStream.close();} catch (IOException e) {log.error(e);}}}
}

3.3 不要捕获  Throwable

Throwable 是 exception 和 error 的父类,如果在 catch 子句中捕获了 Throwable,很可能把超出程序处理能力之外的错误也捕获了

反例:

public void doNotCatchThrowable() {try {} catch (Throwable t) {// 不要这样做}
}

3.4 不要省略异常信息的记录

需要记录错误信息

public void logAnException() {try {} catch (NumberFormatException e) {log.error("错误发生了: " + e);}
}

3.5 不要记录了异常又抛出异常

反例:

public void wrapException(String input) throws MyBusinessException {try {} catch (NumberFormatException e) {throw new MyBusinessException("错误信息描述:", e);}
}

3.6 不要在 finally 块中使用 return

try 块中的 return 语句执行成功后,并不会马上返回,而是继续执行 finally 块中的语句,如果 finally 块中也存在 return 语句,那么 try 块中的 return 就将被覆盖。

反例:

private int x = 0;
public int checkReturn() {try {return ++x;} finally {return ++x;}
}

3.7 抛出具体定义的检查性异常而不是 Exception

反例:

public void foo() throws Exception { //错误方式
}

声明的方法应该尽可能抛出具体的检查性异常

3.8 捕获具体的子类而不是捕获 Exception 类

反例:

try {someMethod();
} catch (Exception e) { //错误方式LOGGER.error("method has failed", e);
}

如果捕获 Exception 类型的异常,可能会导致以下问题:

  • 难以识别和定位异常:如果捕获 Exception 类型的异常,可能会捕获到一些不应该被处理的异常,从而导致程序难以识别和定位异常
  • 难以调试和排错:如果捕获 Exception 类型的异常,可能会使得调试和排错变得更加困难,因为无法确定具体的异常类型和异常发生的原因

正例:应该尽可能地捕获具体的子类

try {// 读取数据的代码
} catch (FileNotFoundException e) {// 处理文件未找到异常的代码
} catch (IOException e) {// 处理输入输出异常的代码
}

3.9 自定义异常时不要丢失堆栈跟踪

不要破坏原始异常的堆栈跟踪

public class MyException extends Exception {public MyException(String message, Throwable cause) {super(message, cause);}@Overridepublic void printStackTrace() {System.err.println("MyException:");super.printStackTrace();}
}

3.10 finally 块中不要抛出任何异常

try {someMethod();  //Throws exceptionOne
} finally {cleanUp();    //如果finally还抛出异常,那么exceptionOne将永远丢失
}

如果在 finally 块中抛出异常,可能会导致原始异常被掩盖

一旦 cleanup 抛出异常,someMethod 中的异常将会被覆盖

3.11 不要在生产环境中使用 printStackTrace()

它可能会导致以下问题:

  • printStackTrace() 方法将异常的堆栈跟踪信息输出到标准错误流中,这可能会暴露敏感信息,如文件路径、用户名、密码等。
  • printStackTrace() 方法会将堆栈跟踪信息输出到标准错误流中,这可能会影响程序的性能和稳定性。在高并发的生产环境中,大量的异常堆栈跟踪信息可能会导致系统崩溃或出现意外的行为。
  • 由于生产环境中往往是多线程、分布式的复杂系统,printStackTrace() 方法输出的堆栈跟踪信息可能并不完整或准确。

在生产环境中,应该使用日志系统来记录异常信息,日志系统可以将异常信息记录到文件或数据库中,而不会暴露敏感信息,也不会影响程序的性能和稳定性

// 可以使用 logback 记录异常信息,如下所示:
try {// some code
} catch (Exception e) {logger.error("An error occurred: ", e);
}

3.12 对于不打算处理的异常,直接使用 try-finally,不用 catch

try {method1();  // 会调用 Method 2
} finally {cleanUp();    //do cleanup here
}

3.13 记住早 throw 晚 catch 原则

在代码中尽可能早地抛出异常,以便在异常发生时能够及时地处理异常

在 catch 块中尽可能晚地捕获异常,以便在捕获异常时能够获得更多的上下文信息,从而更好地处理异常

3.14 只抛出和方法相关的异常

public class Demo {public static void main(String[] args) {try {int result = divide(10, 0);System.out.println("The result is: " + result);} catch (ArithmeticException e) {System.err.println("Error: " + e.getMessage());}}public static int divide(int a, int b) throws ArithmeticException {if (b == 0) {throw new ArithmeticException("Division by zero");}return a / b;}
}

只抛出了和方法相关的异常 ArithmeticException,这可以使代码更加清晰和易于维护

3.15 切勿在代码中使用异常来进行流程控制

使用异常来进行流程控制会导致代码的可读性、可维护性和性能出现问题

应该使用其他合适的控制结构来管理程序的流程

虽然是可以实现逻辑的,但是要避免这样使用

public class Demo {public static void main(String[] args) {String input = "1,2,3,a,5";String[] values = input.split(",");for (String value : values) {try {int num = Integer.parseInt(value);System.out.println(num);} catch (NumberFormatException e) {System.err.println(value + " is not a valid number");}}}
}

3.16 尽早验证用户输入以在请求处理的早期捕获异常

用 JDBC 的方式往数据库插入数据,那么最好是先 validate 再 insert

3.17 一个异常只能包含在一个日志中

反例:

log.debug("Using cache sector A");
log.debug("Using retry sector B");

在单线程环境中,这样看起来没什么问题,但如果在多线程环境中,这两行紧挨着的代码中间可能会输出很多其他的内容,导致问题查起来会很难

正例:

LOGGER.debug("Using cache sector A, using retry sector B");

3.18 将所有相关信息尽可能地传递给异常

有用的异常消息和堆栈跟踪非常重要

应该尽量把 String message, Throwable cause 异常信息和堆栈都输出

3.19 终止掉被中断线程

正例:

while (true) {try {Thread.sleep(100000);} catch (InterruptedException e) {break;}
}
doSomethingCool();

3.20 对于重复的 try-catch,使用模板方法

在尝试关闭数据库连接时的异常处理时使用模板方法:

class DBUtil{public static void closeConnection(Connection conn){try{conn.close();} catch(Exception ex){//Log Exception - Cannot close connection}}
}public void dataAccessCode() {Connection conn = null;try{conn = getConnection();....} finally{DBUtil.closeConnection(conn);}
}

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

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

相关文章

Airflow大揭秘:如何让大数据任务调度变得简单高效?

介绍:Airflow是一个开源的、用于创建、调度和监控数据管道的工作流平台。这个平台使用Python编写,并通过有向无环图(Directed Acyclic Graph, DAG)来管理任务流程,使得用户不需要知道业务数据的具体内容,只…

Python爬虫学习笔记(一)---Python入门

一、pycharm的安装及使用二、python的基础使用1、字符串连接2、单双引号转义3、换行4、三引号跨行字符串5、命名规则6、注释7、 优先级not>and>or8、列表(list)9、字典(dictionary)10、元组(tuple)11…

SDRAM小项目——写模块

写模块跟着视频看了一个多星期,一开始始终有点弄不清楚,现在记录一下理解的过程。 阅读文档信息: 首先阅读文档信息,了解SDRAM写过程的状态转换和时序图 SDRAM整体状态流程如图所示: 在SDRAM整体系统中&#xff0c…

【算法小课堂】动态规划

动态规划 动态规划相信大家都知道,动态规划算法也是新手在刚接触算法设计时很苦恼的问题,有时候觉得难以理解,但是真正理解之后,就会觉得动态规划其实并没有想象中那么难。网上也有很多关于讲解动态规划的文章,大多都…

Java--业务场景:在Spring项目启动时加载Java枚举类到Redis中(补充)

文章目录 前言步骤测试结果 前言 通过Java–业务场景:在Spring项目启动时加载Java枚举类到Redis中,我们成功将Java项目里的枚举类加载到Redis中了,接下来我们只需要写接口获取需要的枚举值数据就可以了,下面一起来编写这个接口吧。 步骤 在…

leetcode238:除自身以外数组的乘积

文章目录 1.使用除法(违背题意)2.左右乘积列表3.空间复杂度为O(1)的方法 在leetcode上刷到了这一题,一开始并没有想到好的解题思路,写篇博客再来梳理一下吧。 题目要求: 不使用除法在O(n)时间复杂度内 1.使用除法&am…

新一代数字原住民:市场痛点与“繁”思维应对之道

随着科技的迅速发展,尤其是互联网的普及,新一代数字原住民经营者已经逐渐成为市场的主力军。不同于传统的消费者,有着独特的消费习惯和心理需求。企业要在这激烈的市场竞争中获得优势,深入了解这一群体的特征和心理、行为&#xf…

有趣的事,讲给有趣的人听

哈哈哈,今天不写技术了,今天分享一下生活,技术我们什么时候都可以学,但是生活更值得我们现在就去更好的体验! 两年多的涤生大数据,认识了形形色色的小伙伴,陆续沟通下来6000多人,彼时…

数据库锁表原因、排查、解决

一.场景 场景1场景2二.原因三.排查四.解决方案 一.场景 场景1 锁表通常发生在DML( insert 、update 、delete ) A操作进行全量数据同步,对整个表的粒度进行上锁,导致B操作只能等待A操作完成才能进入插入数据。此时就出现了锁表…

Elasticsearch windows开箱即用【记录】

一、准备工作 安装ES之前要在本机安装好JDK,对应的兼容性见官网链接:https://www.elastic.co/cn/support/matrix ES官网链接:https://www.elastic.co/cn/, 我本机安装的是JDK8,测试使用的是7.3.0版本的ES和Kibana。 1、首先去…

PyTorch项目源码学习(3)——Module类初步学习

torch.nn.Module Module类是用户使用torch来自定义网络模型的基础,Module的设计要求包括低耦合性,高模块化等等。一般来说,计算图上所有的子图都可以是Module的子类,包括卷积,激活函数,损失函数节点以及相…

完成源示例

本主题演示如何创作和使用自己的完成源类&#xff0c;类似于 .NET 的 TaskCompletionSource。 completion_source 示例的源代码 下面的列表中的代码作为示例提供。 其目的是说明如何编写自己的版本。 例如&#xff0c;支持取消和错误传播不在此示例的范围内。 #include <w…

VR全景技术如何应用在城市发展,助力城市宣传展示

引言&#xff1a; 随着科技的不断发展&#xff0c;VR全景技术正逐渐渗透到各行各业&#xff0c;其中较为广泛的应用之一便是城市展示。那么VR全景技术如何运用在城市展示领域&#xff0c;这项技术给城市发展带来了哪些好处&#xff1f; 一. VR全景技术简介 1.什么是VR全景技术…

怎样制作一本旅游电子相册呢?

​随着数码技术的发展&#xff0c;旅游电子相册已成为越来越多旅游爱好者的必备工具。它不仅能让您随时随地欣赏自己的旅行回忆&#xff0c;还能分享给亲朋好友&#xff0c;甚至上传到社交媒体上&#xff0c;让更多人了解您的旅行故事。那么&#xff0c;如何制作一本精美的旅游…

Postman接口测试之断言,全网最细教程没有之一!

一、断言 在 postman 中我们是在Tests标签中编写断言&#xff0c;同时右侧封装了常用的断言&#xff0c;当然 Tests 除了可以作为断言&#xff0c;还可以当做后置处理器来编写一些后置处理代码&#xff0c;经常应用于&#xff1a; 【1】获取当前接口的响应&#xff0c;传递给…

【数据开发】BI数据报表之数据可测试性设计与分析

文章目录 1、什么是BI&数据报表2、什么是可测试性3、数据测试与方法3.1 数据准确性与对比&#xff08;重要&#xff09;3.2 数据安全性 1、什么是BI&数据报表 数据报表是一种数据可视化工具 用于将数据以图表、表格和其他可视化形式呈现出来&#xff0c;以便用户可以…

BRC20通证的深度科普:它的潜力与如何导入到bitget

​BRC-20通证是什么&#xff1f; BRC-20通证&#xff1a;比特币上的“变形金刚”&#xff1f;&#xff01;不依赖智能合约&#xff0c;它们就像拥有超能力的外星人&#xff0c;直接在比特币的最小单位——聪上刻写JSON代码。哈哈&#xff0c;这比把房子建在乐高积木上还要刺激…

逆水行舟 不进则退

目录 一、前言 二、2023年度总结 三、2024展望未来 一、前言 这是我从工作以来到现在最喜欢的一句话&#xff0c;我想把这句话送给自己也想送给大家。 2019年7月实习到现在已经过去了四年多&#xff0c;进入2024年也迎来了我工作生涯的第五个年头。 在这个行业里&#xff…

Docker五部曲之四:Docker Compose

文章目录 前言Compose应用程序模型Compose规范顶层属性servicenetworkvolumesconfigssecrets 环境变量.env文件environment属性主机shell中的环境变量 Profiles&#xff08;剖面&#xff09;启动剖面自动启动剖面和依赖项解析 多compose.yml文件共享与扩展构建规范构建属性 部署…