10 | 异常处理都有哪些陷阱?
该思维导图由 AI 生成,仅供参考
异常就是非正常
- 深入了解
- 翻译
- 解释
- 总结
异常处理在编程中是一个基本功,但也存在许多陷阱。首先,需要分清正常状况和异常状况,避免滥用异常机制。其次,需要明确异常的类别,包括非正常异常、运行时异常和非运行时异常。对于运行时异常和非运行时异常,需要在方法的规范描述文档中清楚地标记异常,以便应用程序能够了解哪些异常需要处理、什么状况下会抛出异常以及该怎么处理这些异常。特别是对于运行时异常,需要特别注意标记清楚抛出异常,以避免降低代码效率和增加错误。因此,对于所有可能抛出运行时异常,都需要有清晰的描述,同时需要查看所有的调用方法的规范描述,确认抛出的异常要么已经处理,要么已经规范描述。这样能够有效提高编码和阅读代码的效率。 在处理异常时,需要了解异常机制的基本原理,包括异常类名、异常描述、异常堆栈和异常转换。这些要素能够帮助解决出错原因、出错位置和为何出错的问题。在编写规范的代码时,需要遵循三条准则:不要使用异常机制处理正常业务逻辑;异常的使用要符合具体的场景;具体的异常要在接口规范中声明和标记清楚。文章还提到了一段Java代码,让读者思考其中的异常处理是否违反了上述原则,并鼓励读者分享优化后的代码。 总的来说,本文强调了异常处理的重要性和注意事项,提供了对异常机制基本原理的深入理解,并鼓励读者在实践中不断优化异常处理的方式。
《代码精进之路》,新⼈⾸单¥59
全部留言(20)
- 最新
- 精选
- Y024当方法中返回的数组或集合,会有为空的情况,不要直接返回 null(会强迫调用方需要进行判空处理,否则可能出现 NullPointerException),最好返回大小为 0 的数组或集合。 其实分配一个空数组或集合所花费的时间和空间,性能损坏是很小到基本可以忽略不计的。同时,如果返回的空数组或者集合是 immutable(即不可变的)的话,可以进一步定义成 static final(对于数组而言)或者 Collections.emptyList()/emptyMap()/emptySet(),来公用同一个对象,减少性能影响。
作者回复: 这个留言必须赞!
2019-01-30349 - 老杨同志1. 没找到结果应该是正常业务,不用抛出异常 2. 缺失必要的参数校验 import java.util.HashMap; import java.util.Map; class Solution { /** * Given an array of integers, return indices of the two numbers * such that they add up to a specific target. * return null if nums==null or nums.length==0 or result not found. */ public int[] twoSum(int[] nums, int target) { if(nums==null || nums.length==0){ return null; } Map<Integer, Integer> map = new HashMap<>(); for (int i = 0; i < nums.length; i++) { int complement = target - nums[i]; if (map.containsKey(complement)) { return new int[] { map.get(complement), i }; } map.put(nums[i], i); } return null; } }
作者回复: 改的很赞! 另外,有一个小技巧和你分享一下。如果返回值是空值(null),如果调用代码没有检查空值就调用,会抛除NullPointerException异常。如果返回空数组(int[0]), 就不会出现这个问题了。 这个小技巧可以减少调用代码的错误,这样设计的接口比较皮实耐用。如果返回值是数组或者集合,我们通常使用这一个技巧。
2019-01-2513 - 木白“ 对于异常类名,我们要准确地选择异常类。” 老师,我们应该怎么去快速准确地知道需要抛哪个异常呢?因为有时候只是觉得应该做异常检测,但是属于哪种异常自己也描述不清楚。需要把jdk中的异常都记下来吗?我就是那种直接抛Exception异常的。
作者回复: 坏消息是,即使我们记住了所有的JDK异常类,也仅仅是异常世界的一小部分,不够用的。 好消息是,没有人能记住即便是JDK这么少的异常类,所有我们也不要折磨自己去记住所有的异常。 首先要记住,异常是一个可以扩展的类,需要时可以自己定义。所以,你的应用可以有自己定义的异常类。 弄不清楚使用哪种异常,有两种情况比较典型,一种是不了解调用的类;另一种是不了解自己编写的代码的逻辑。这两条清楚了,异常就清楚了。这两条不清楚,代码也很难清楚。 异常就不正常的状况。不正常的状况是什么,搞清楚了,异常该是什么也就知道了八九分。然后,去找合适的现存异常类,或者定义新的异常类,剩下的一两分工作也就凑齐了。 比如说,我回复这条信息的时候,总是输错字母。要是用异常表示,这个不正常的状况就是“键盘输入信息错误”。 然后,在你使用的类库中,去寻找有没有表示这个不正常状况的异常,找到了就用。找不到,如果你想精确定义,就自己定义一个KeyboadInputException; 如果你不想定义新异常,就扩大概念,从“键盘输入信息错误"扩大到"输入信息错误"。然后IOException就是大家都常用的异常了,然后你就可以使用new IOException("键盘输入信息错误")来表示“键盘输入信息错误”这个不正常状况了。 就这样,了解的代码,了解了不正常的状况到底是什么状况。
2019-03-287 - 草原上的奔跑课后习题,1.使用异常处理正常的业务逻辑。2.异常没有在接口规范中标记清楚。3.异常类名感觉用在这里不合适
作者回复: 找的都对。你想到怎么修改了吗?
2019-01-252 - Demon.Lee老师,请教一个问题,在写定时任务的业务时,一开始从表中提取一些记录,然后针对每条记录进行业务处理(业务处理有事务),如果业务处理成功,则更新这条记录状态为A,如果业务处理失败,则更新这条记录状态为B,此时我就感觉违背了“不要用异常处理业务逻辑”这条规则,老师有什么好建议。另外,我想到的是用两个线程处理 ,主线程更新状态,子线程处理业务逻辑,但是需要线程间同步。 public void execute(){ List<T> rows = list(); for(T t:rows){ try{ deal(t); updateStatus("A"); }catch(Exception ex){ updateStatus("B"); } } }
作者回复: deal()能不能不抛出异常?或者不正常的时候抛出异常,正常的业务不抛出异常? 使用线程处理更复杂,还不如捕获异常呢。
2019-03-0421 - pyhhou思考题: 1. 对输入数组需进行预判 2. 方法内如果有异常抛出,需进行标记描述 3. 异常名称名不副实 import java.util.HashMap; import java.util.Map; class Solution { /** * Given an array of integers, return indices of the two numbers * such that they add up to a specific target. */ public int[] twoSum(int[] nums, int target) { if (nums == null || nums.length ==0) { return new int[0]; } Map<Integer, Integer> map = new HashMap<>(); for (int i = 0; i < nums.length; i++) { int complement = target - nums[i]; if (map.containsKey(complement)) { return new int[] { map.get(complement), i }; } map.put(nums[i], i); } return new int[0]; } } 最后还想问下,如果说是 return 的不是 null 而是空数组,是不是就不需要在文档描述中标记写明? 年过了一半了,看看自己拉下的章节还有不少,得抓紧了,祝老师新的一年心想事成,身体健康,工作生活一切顺利~ 今年励志一定要好好和你学写代码,稳扎稳打,快速成长
作者回复: 新春快乐! JDK的习惯是,不论是null还是空的集合、数组,都在文档描述中标记清楚。这样,调用者能确切地知道该怎么检查。
2019-02-091 - 进化菌想起我们以前抛异常常用的话术“服务器繁忙,请稍后重试”,服务器是够繁忙的,光是异常就变化万千。 不过,我们还是得和异常和谐相处~ 异常处理的三条准则: 不要使用异常机制处理正常业务逻辑; 异常的使用要符合具体的场景; 具体的异常要在接口规范中声明和标记清楚。
作者回复: 第一次听说这样的话术,有意思!
2021-11-19 - 北风一叶异常的使用要符合具体的场景,这一条非常虚,不具备可执行性
作者回复: 哈哈,看来你喜欢干脆的结论。
2019-03-08 - 李星如果是异常没有被try-catch住的线程呢?
作者回复: 我们先来看看线程的主方法声明“public void run()”。这个声明没有抛出检查型异常,只能抛出运行时异常。所以,检查型异常一定要在线程的实现中得到处理;否则的话,编译器应该报错的。这个方法可以抛出运行时异常。一个线程,像一个普通的方法一样(run()),抛出运行时异常后,线程就终止了。问题在于,线程通常共享资源,如果线程之间有联系,很多事情就会发生,依赖于线程的具体实现逻辑。比如说,如果一个线程要等待另一个线程的I/O,也许会阻塞。
2019-02-12 - 李星想问一下作者,在多线程情况下时,当某一个线程发生运行时异常,并且不处理时,是否真的会阻塞当前线程呢?使得这个线程被废掉。?
作者回复: 当前线程是发生异常没处理的线程吗? 还是不同的两个线程? 没太明白问题。
2019-02-12