`

关于Java异常抛出和处理的思考

    博客分类:
  • java
阅读更多
看过不少关于java异常处理的争论,也在不同项目里见到过异常处理不善,造成的灾祸。
以下谈谈一些个人的感受。
一、异常处理的现状
印象里接触过的项目,在异常处理上都觉得欠缺妥当, 没有从全局上来考虑。于是大量的代码中可以看到
try{
...
}catch(Exception ex){
  ex.printStackTrace();
}
于是异常就被吃掉了, 程序还会若无其事的继续进行。
从各种情况来看,程序员其实很烦恼异常处理,因此碰到要强制捕获的时候,要么就throws,要买就catch(Exception ex)全部抓掉,再一句话:  e.printStackTrace()。
抛出异常, 我看到很多程序员也不太喜欢,宁可用int返回值定义各类异常情况。

事实上,异常处理不当,对整个项目危害是很大的,可能造成程序结构和逻辑上都会混乱,常常会容易出错,健壮性不好。因此, 应该努力在项目架构确定的同时也确定对异常的统一约定,应该本着简洁、合理的基本原则。

二、异常的两种类型
非检查异常和已检查异常。
1)非检查异常,Throwable继承树的两个分支被编译器放宽了异常检查行为,允许不作捕获。java.lang.Error和 java.lang.RuntimeException的子类免于编译时检查,这就是未检查异常(unchecked exception)。
2)其他的编译器要求强制捕获的就属于已检查异常(checked exception)
其实要讨论异常处理的基本原则,就是要理解以上两种异常的不同特点,何种情况应该抛出何种异常。容易发现,jdk的很多方法抛出的异常都是checked exception,因为会强制要求我们捕获。

三、错误和意外
在谈论异常抛出的基本准则之前,有必要先看看抛出异常时可能面对的两类情况:错误和意外。
1)意外
假设一个银行支付的场景,有一个CheckingAccount 环节,检查客户的账户余额是否足够,如果发现余额不足,抛出了一个InsufficientFundsException,并且要终止支付动作。
那么这个余额不足就应当那个视为一个意外事件,正常情况下是可以完成支付的,现在余额不足要终止。很显然,对于意外事件,我们是可以预料到的,而且也有相应的处理措施,这里就是终止支付操作,并提示客户需要充值了。

2)错误
如果以上场景, 网线掉了呢?我们可能会接受到一个NetworkException,但显然我们无法通过程序来处理这个问题的。
这时我们将这个异常视为错误, 因为我们没法处理,也无法预期这类问题, 网络的问题,可能是网线掉了,也可能是网络设置不对等原因。

三、异常抛出的基本准则
看了以上两类异常情况,我们大概就能意识到,意外的情形我们应该使用checked exception;而错误情形,我们就应该用unchecked exception。
有个底线,就是问问自己,这个异常如果跑出去是否还有挽救的余地,如果可以则用checked exception;否则就unchecked exception。

像UserNotFoundException、NoAuthenticationException、NotEnoughMoneyException都可以归于checked exception,都代表着一个意外事件,有应对的策略。而IOException、SQLException都是不可预料、不可恢复的,应该用unchecked exception会合适一些。不过jdk的开发人员显然是偏爱checked exception,强类型的更加打眼,能明显看出可能出现的问题, 却不管你能不能搞定它。碰到这个情况,我们可以将checked Exception转换成unchecked Exception,这种做法也出现在了大量的开源框架中,一般都会有个异常转换器来做这个转换工作 。

四、异常处理的准则?
我总结了两条出来:
1)对于unchecked,既然是不可恢复的,那我们就不能及时处理,但我们可能还是需要最后去记录一下,然后给出一个“系统错误”之类的提示。那么就可以在应用程序的上层设置一个屏障专门处理这类异常。
2)对于checked,我们需要及时处理,如果发现无法处理就转化成unchecked再次抛出。

异常处理需要try...catch,如果有几类异常就要写好几个catch,因此很多程序员都比较排斥用异常表示意外和错误,他们采用方法返回值int,然后根据这个返回值switch来确认不同的异常情况和作处理。于是,我也思考了异常跟这种方式的优劣。
用方法的返回值代替异常,我们也能在jdk的代码中看到影子,比如String.indexOf就是返回-1表示找不到字串或者字符,IO类里面更是用-1表示是否流读取结束。
这时看起来还是很容易理解的, 而且不用异常也能减低一些开销。但也能发现一个问题,如果这个返回值不是int这类数字,或者说不是基本类型,那么就不好拿返回值表示异常了,当然还有void的情形更不可行。再想想,其实-1,-2,-3表示异常肯定还是需要定义一个常量类来表示,并取个具有意义的变量命会比较好。
这样我就发现这个方式应付不了大多数情形,而且可读性上也差点,定义好异常,直接throws和catch倒还方便。

五、log异常
出现了异常,我们可能需要log记录下来,因为这个异常有些特殊的业务含义,我们需要归档。
合理的设计应该是:
1)对于checked异常, 抛出的时候我们log一下;
2)Unchecked异常,在最上层的错误屏障log,记录下异常信息和堆栈。

总结一下,异常但并不可怕,关键是我们能认真的理解和分析它们。
分享到:
评论

相关推荐

    Java异常处理和最佳实践(含案例分析).pdf

    "Java异常处理和最佳实践(含案例分析)" 本文将深入探讨Java中的异常处理机制,讨论如何正确地处理Java异常,避免常见的错误和best practice。通过本文的学习,您将了解Java异常的分类、为什么finally块中的代码...

    java 方法的流程控制与异常处理

    本次实验旨在通过一系列具体的编程任务,加深学生对Java语言中流程控制结构的理解,并掌握Java异常处理的基本方法。具体目标包括: - **熟练掌握Java各种流程控制结构(选择结构、循环结构)的使用**:通过对不同的...

    关于java中对象属性值的校验的思考

    当尝试创建或更新User对象时,Hibernate Validator会自动检测违反的约束并抛出异常。此外,该框架还支持自定义校验注解和校验逻辑。 除了使用注解,还可以编写自定义的验证逻辑。例如,通过重写`equals()`和`...

    java-大数据基础面试思考.pdf

    故障安全迭代器在迭代过程中不会抛出ConcurrentModificationException异常,它们使用的是集合的一个快照,因此在多线程环境下使用比较安全,不会抛出异常。不过,这种迭代器的缺点是无法保证遍历到最新的数据,因为...

    java习题-多选题

    - **异常处理**:Java中的try-catch-finally语句块用于捕获和处理运行时异常,throw和throws关键字用于抛出异常。 - **多线程**:Java内置了Thread类和Runnable接口,通过实现这些来创建并管理多线程。同步机制如...

    中南大学Java实验报告三

    实验报告可能包括如何抛出和处理自定义异常。 五、集合框架 Java集合框架是管理对象组的关键工具,包括List、Set和Map接口,以及ArrayList、LinkedList、HashSet、HashMap等实现类。学生可能会学习如何创建和操作...

    java api官方源代码

    了解如何正确地抛出和处理异常,可以提高程序的稳定性和可维护性。 7. **反射**:java.lang.reflect包提供了反射API,允许程序在运行时检查类的信息,如类名、方法名、字段名,并动态调用方法或访问字段。反射是...

    合工大Java作业

    学生可能需要学习如何使用try-catch-finally语句块来捕获和处理运行时错误,以及如何定义和抛出自定义异常。 4. **集合框架**: 集合框架是Java中用于存储和操作数据的重要工具,包括List、Set、Map等接口及其实现类...

    java学习小实例

    5. **异常处理**:Java强制使用try-catch块来处理运行时错误,了解异常的分类、如何抛出和捕获异常是编写健壮代码的必要条件。 6. **输入/输出流**:I/O流是处理文件读写、网络通信等操作的基础。熟悉...

    JAVA 考试竞赛题--有一定的难度

    5. **异常处理**:Java的异常处理机制通过try-catch-finally语句块实现,参赛者需要理解各种异常类型,以及何时和如何抛出、捕获和处理异常。 6. **集合框架**:ArrayList、LinkedList、HashSet、HashMap等是Java...

    Thinking in Java 4th Edition + Annotated Solution Guide (代码)英文文字版 带书签 有答案

    - **Exceptions**:关于异常处理的讨论,包括如何抛出、捕获和处理异常。 综上所述,这个压缩包提供了一个全面的Java学习资源,不仅有理论知识,还有实践代码和解答,对于任何希望深入理解和掌握Java的人都是一份...

    《从零开始学JAVA》配套程序

    - 自定义异常:理解如何创建自定义异常类,以及何时应该抛出异常。 4. **集合框架** - 数组与ArrayList:比较数组和动态数组`ArrayList`的异同,学习`ArrayList`的添加、删除、遍历操作。 - HashMap与HashSet:...

    java常用API-适合初学者

    - **try-catch-finally语句块**:用于捕获和处理异常。 - **throw关键字**:抛出异常。 - **throws关键字**:声明方法可能抛出的异常。 6. **反射机制** - **Class类**:代表运行时的类信息,用于动态加载和...

    JAVA 应用编程150例

    学习如何正确抛出和捕获异常,以及何时使用finally块,是避免程序意外终止的重要技能。 5. **输入输出流**:Java I/O系统包括读写文件、网络通信等,例如FileReader、FileWriter、Scanner、PrintWriter等类的使用。...

    JAVA结课机考练习题

    理解何时抛出异常,如何捕获并处理异常,是编写健壮代码的关键。 在复习过程中,注意题目中的陷阱,比如混淆关键字的用法、错误的异常处理、不恰当的类或接口使用等。这些易错点往往是测试你对JAVA概念理解深度的...

    java 语言程序设计 第十版 复习题答案

    Java提供了一套丰富的异常处理机制,支持异常捕获和抛出,使得开发者能够编写出更为安全的代码。 集合框架是Java中处理数据存储和管理的标准解决方案,包含各种不同的集合类,例如ArrayList、LinkedList、HashSet、...

    java核心基础全套全方面练习题一份带答案

    理解如何正确抛出和捕获异常,可以让你写出健壮的代码。 多线程是Java的另一大特色。Java提供了Thread类和Runnable接口来创建和管理线程。学习如何同步线程,避免并发问题,如死锁和竞态条件,是提升程序性能的关键...

    <java面向对象编程>孙卫琴课后答案

    8. **异常处理**:Java通过异常处理机制来捕获和处理程序运行时可能出现的错误。try-catch-finally语句块用于定义可能抛出异常的代码,并提供相应的处理策略。 9. **集合框架**:Java集合框架是一组接口和类,用于...

    java实现的掷硬币

    1. **异常处理**: 可以考虑加入异常处理机制,比如当用户输入非数字时给出提示并重新请求输入。 2. **界面美化**: 使用更复杂的UI库如Swing或JavaFX来设计一个更加友好的图形用户界面。 3. **多轮游戏支持**: 支持...

    The Thinking in Java Annotated Solution Guide

    了解何时及如何抛出、捕获和处理异常,可以编写更健壮的代码。 7. **网络编程**:Java的Socket编程允许创建客户端和服务器端应用,进行TCP/IP通信。ServerSocket和Socket类是主要的工具。 8. **反射和注解**:反射...

Global site tag (gtag.js) - Google Analytics