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.rhkb.cn/news/237378.html

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

相关文章

leetcode 每日一题 2024年01月11日 构造有效字符串的最少插入数

题目 2645. 构造有效字符串的最少插入数 给你一个字符串 word ,你可以向其中任何位置插入 “a”、“b” 或 “c” 任意次,返回使 word 有效 需要插入的最少字母数。 如果字符串可以由 “abc” 串联多次得到,则认为该字符串 有效 。 示例 …

17. 电话号码的字母组合(回溯)

从第一个数字开始遍历其对应的字母&#xff0c;将其加入StringBuffer中&#xff0c;继续深度优先搜索&#xff0c;当访问到最后一个数字的时候&#xff0c;将StringBuffer存储到ans中&#xff0c;然后回溯到下一个对应字母。 class Solution {public List<String> lette…

【二十】【动态规划】879. 盈利计划、377. 组合总和 Ⅳ、96. 不同的二叉搜索树 ,三道题目深度解析

动态规划 动态规划就像是解决问题的一种策略&#xff0c;它可以帮助我们更高效地找到问题的解决方案。这个策略的核心思想就是将问题分解为一系列的小问题&#xff0c;并将每个小问题的解保存起来。这样&#xff0c;当我们需要解决原始问题的时候&#xff0c;我们就可以直接利…

ALIENWARE:卓越游戏体验,源自创新基因

美国拉斯维加斯当地时间1月9日&#xff0c;CES 2024在万众期盼中如约而至。 作为全球消费电子领域一年一度的盛宴和行业风向标&#xff0c;CES 2024汇聚了来自全球的众多消费电子企业以及令人目不暇接的最新科技产品&#xff0c;因而受到了全球广大消费者的密切关注。 众所周知…

GitHub项目推荐-incubator

项目地址 Github地址&#xff1a;GitHub - apache/incubator-anser 官网&#xff1a;Apache Answer | Free Open-source Q&A Platform 项目简述 这是Apache的一个开源在线论坛&#xff0c;也可以部署成为一个自有的QA知识库。项目主要使用了Go和Typescript来开发&#…

持续集成-Jenkins显示HTML报告

1 需要安装startup-trigger-plugin和Groovy插件。 2 在Job配置页面&#xff0c;构建触发器&#xff0c;勾选Build when job nodes start&#xff1b; 3 在Job配置页面&#xff0c;增加构建步骤Execute system Groovy script&#xff0c;在Groovy Command中输入上面命令&…

element ui el-table展示列表,结合分页+过滤功能

vueelement-ui实现的列表展示&#xff0c;列表分页&#xff0c;列表筛选功能 1&#xff0c;分页器 el-table模块下面是分页器代码 <el-pagination></el-pagination> <el-table></el-table> <!-- 分页器 --><div class"block" st…

【代码随想录04】24. 两两交换链表中的节点 19. 删除链表的倒数第 N 个结点 面试题 02.07. 链表相交 142. 环形链表 II

24. 两两交换链表中的节点 题目描述 给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题&#xff08;即&#xff0c;只能进行节点交换&#xff09;。 做题思路 可以设置虚拟头结点cur和画图…

erlang/OTP 平台(学习笔记)(四)

Erlang语言精要 Erlang shell 相较于日常惯用的系统&#xff0c;Erlang系统是一套更富交互性的环境。使用大部分编程语言时&#xff0c;要么把程序编译成OS可执行文件后运行&#xff0c;要么用解释器来执行一堆脚本文件或编译后的字节码文件。无论哪种情况&#xff0c;都是让…

Vulnhub靶机:driftingblues 2

一、介绍 运行环境&#xff1a;Virtualbox 攻击机&#xff1a;kali&#xff08;10.0.2.15&#xff09; 靶机&#xff1a;driftingblues2&#xff08;10.0.2.18&#xff09; 目标&#xff1a;获取靶机root权限和flag 靶机下载地址&#xff1a;https://www.vulnhub.com/entr…

层叠布局(Stack)

目录 1、概述 2、开发布局 3、对齐方式 3.1、TopStart 3.2、Top 3.3、TopEnd 3.4、Start 3.5、Center 3.6、End 3.7、BottomStart 3.8、Bottom 3.9、BottomEnd 4、Z序控制 5、场景示例 1、概述 层叠布局&#xff08;StackLayout&#xff09;用于在屏幕上预留一…

记录用python封装的第一个小程序

前言 我要封装的是前段时间复现的一个视频融合拼接的程序&#xff0c;现在我打算将他封装成exe程序&#xff0c;我在这里只记录一下我封装的过程&#xff0c;使用的是pyinstaller&#xff0c;具体的封装知识我就不多说了&#xff0c;可以参考我另一篇博客&#xff1a;将Python…

HCIP-1

一、网络类型&#xff1a; 点到点 BMA&#xff1a;广播型多路访问 – 在一个MA网络中同时存在广播&#xff08;洪泛&#xff09;机制 NBMA&#xff1a;非广播型多路访问—在一个MA网络中&#xff0c;没有洪泛机制 MA&#xff1a;多路访问 在一个网段内&#xff0c;存在的节…

NGINX 配置本地HTTPS(免费证书)

生成秘钥key,运行: $ openssl genrsa -des3 -out server.key 2048 会有两次要求输入密码,输入同一个即可。输入密码然后你就获得了一个server.key文件。 以后使用此文件(通过openssl提供的命令或API)可能经常回要求输入密码,如果想去除输入密码的步骤可以使用以下命令: $ op…

Open3D AABB包围盒计算与使用(19)

Open3D AABB包围盒计算与使用(19) 一、算法速览二、算法实现1.代码2.结果少年听雨歌楼上。红烛昏罗帐。壮年听雨客舟中。江阔云低、断雁叫西风。 而今听雨僧庐下。鬓已星星也。悲欢离合总无情。一任阶前、点滴到天明。 一、算法速览 AABB包围盒就是将点云用一个各条边沿着坐…

SpringMVC(六)RESTful

1.RESTful简介 REST:Representational State Transfer,表现层资源状态转移 (1)资源 资源是一种看待服务器的方式,即,将服务器看作是由很多离散的资源组成。每个资源是服务器上一个可命名的抽象概念。因为资源是一个抽象的概念,所以它不仅仅能代表服务器文件系统中的一个文件…

Untiy HTC Vive VRTK 开发记录

目录 一.概述 二.功能实现 1.模型抓取 1&#xff09;基础抓取脚本 2&#xff09;抓取物体在手柄上的角度 2.模型放置区域高亮并吸附 1&#xff09;VRTK_SnapDropZone 2&#xff09;VRTK_PolicyList 3&#xff09;VRTK_SnapDropZone_UnityEvents 3.交互滑动条 4.交互旋…

什么是云安全?如何保护云资源

云计算允许组织通过互联网按需向其客户、合作伙伴或员工提供关键业务应用程序、服务和资源。换句话说&#xff0c;不再需要物理维护资源。每当您通过 Internet 从计算机访问文件或服务时&#xff0c;您都是在访问云。 迁移到云可以帮助企业增强安全性、简化运营并降低成本。企…

机器视觉在OCR字符检测的应用

在产品质量 检测过程中&#xff0c;对于字符、条码等标识信息的识别、读取、检测是非常重要的一部分&#xff0c;比如在食品饮料包装检测中&#xff0c;生产日期 、保质期 、生产批号 、条码等字符信息是产品管理和追溯必不可缺的&#xff0c;因此利用机器视觉技术进行OCR字符采…

Java设计模式-备忘录模式

备忘录模式 一、概述二、结构三、案例实现&#xff08;一&#xff09;“白箱”备忘录模式&#xff08;二&#xff09;“黑箱”备忘录模式 四、优缺点五、使用场景 一、概述 备忘录模式提供了一种状态恢复的实现机制&#xff0c;使得用户可以方便地回到一个特定的历史步骤&…