我们知道java的异常有两个大类,一种是check Exception,一种是runtime Exception。
1.check exception:如果一个方法抛出check exception,调用代码要么catch要么在方法声明中重抛该异常。
2. runtime Exception:如果一个方法抛出RuntimeException,则调用代码可以catch,也可以忽略。由于可以忽略,初学者可能非常喜欢。
为了探究Exception的本质,让我们回到exception的历史上。exception的目的是方便我们查找问题以及简化我们的业务逻辑。在c语言中没有exception一说,所有程序的状态都是需要代码控制,往往通过函数的返回值来得到函数执行的状态。让我们看一个简单例子,假如一个函数需要传递一个参数id,从DB中获取对应id的值,而id的值的范围是所有int,调用DB过程中可能发生一些错误,调用者如何知道返回的值是id真实的值还是异常状态呢?我们只能通过指针回传值,而返回值作为执行的状态。这样的函数看起来就别扭。当我们增加exception功能后,这样的问题处理起来就简单多了,只需要函数的声明中增加expeition就可以了。显然这种方式更加符合人的思维,也直观明了。因此在现代程序开发语言中,异常处理成了必不可少的语言特性。
程序员写代码容易,但真正要把异常处理好可真不是那么容易的。我们知道异常的真正目的是为了方便知道究竟是什么原因出错了。一种是业务逻辑的错误,一种是系统错误。比如一个函数通过传递一个userId去获取用户信息,有几种情况:正常获取到用户信息,DB未启动,连接不上,网络断掉,连接不上,connection申请不到,已经达到上限。或者是用户不存在。显然我们可能需要针对不同的情况代码逻辑做相应的处理。比如如果是连接不上,可能需要我们重试几次,如果是用户不存在我们需要友好提示你查找的用户不存在,如果是DB没有启来,则需要提示系统错误,有可能需要通知相关的DB人员。因此好的程序员一定要仔细对待每一个异常的处理。正确的异常处理是保证程序健壮性的关键之一。
那么我们什么时候该抛出check exception,什么时候该抛出runtime exception。前面已经解释了两者的差异。check exception需要强迫调用者必须处理它,而runtime exception不必。既然强迫别人做事总是会让人不爽,runtime exception是不是最佳选择呢。当然不是,否则sun就不会搞出这两类异常。一个相对比较有效的建议是如果抛出的异常需要调用者关注并可以采取措施进行不同的代码流程控制的,check exception无疑是唯一的选择。而runtime exceptin是针对那些“我这里有问题发生,但是告诉你你也不能解决”的问题。当然有些时候由于接口声明异常的限制,最初的接口没有声明异常,导致实现该接口时即使有异常抛出也只能乖乖地抛出runtime exception,因为别无选择。这也给我敲了警钟,如果你声明接口,除非你有十足把握没有错误出现,建议你声明一下异常吧。
但是check exception带给我们最大的麻烦是,代码遍地是try catch块,主要的业务流程被这些try catch块分割得支离破碎,看到这也的代码确实心烦。有什么好办法吗?答案是肯定的。我们的程序总是由多层代码构成,以及同一层的不同业务包构成。我们需要切分不同层以及不同业务包,比如DB层,统一抛出SqlException,文件相关的接口统一抛出IOException,如果是business层,与用户管理相关的模块,统一抛出UserException,在不同业务层及模块间调用时,wrap一下exception,统一由抛出该层相关的check exception,这样,内部的业务逻辑处理就非常简单,可以尽量少的看到try catch块,而且又能合理利用exception的好处。
对于什么时候该使用Runtime Exception,什么时候该使用Check Exception已经有一定认识了,让我们再次回归到Exception的本质,Exception一个非常重要的作用是需要告诉调用者发生了何种错误,错误发生的地方在哪里。Java中的Exception的callstack显然非常有用。可以知道何处发生了何种错误。因此我们通常会将异常通过日志工具记录到日志里,以方便查找问题。这个话题似乎不值得一提,发生错误的地方记录log日志是显然的答案。其实未必,很多看起来显而易见的问题仔细深究问题就不那么显然了。如何打印Exception到日志中,需要回答几个问题。哪里记,记什么,日志级别怎么定?并不是任何发生异常的地方都该记,并不是发生了exception都该记为error级别,不是所有发生问题的地方都需要把Exception的调用栈打出来。另外一个问题是并不是所有的地方都可以把一个异常包装为一个新定义的异常抛出。以下是我在代码编写过程中总结的一些经验,可能有些地方也不仅其然。
哪里记?应该在exception的末端记录发生的异常。何为exception末端?如果程序代码需要处理Exception,并且不再将Exception外抛,或者在RPC应用中,任何对外提供的调用的方法实现都应该称为Exception末端,都需要对Exception进行处理。这样的好处是,在日志文件中不会看到一个Exception有很多处重复出现,同时又保证对异常的跟踪有效。必须要说明一点的是,如果是RMI接口,如果接口声明有异常,难到还需要记录日志并重新处理吗?这是跟RMI的机制有关,由于我们的Exception可以被嵌套,比如在抛出ExceptionA时丢进去一个ExceptionB,这在Exception包装过程中是常用的伎俩,而且我们非常鼓励这样做,以便我们能发现问题的root cause。当客户端收到异常的时候,如果要打印这些异常信息,需要反序列化对于的异常,由于客户端只有ExceptionA类,无法找到ExceptionB类,这打印这样的异常将会导致NotFoundClassException。在我们DMS中RMI调用就遇到过类似情况。像web应用的mvc层,对外就不能再抛异常了,因为再往外抛就到用户那里了。而任何系统都不希望用户看到exception,都应该转换为用户友好的信息。
记什么?从我个人观点来看,如果遵循我上面的规则(如果catch了一个Exception,要么业务上能处理,并记日志,要么包装为其他异常,如果包装为其他异常,应该将异常嵌套进去),应该把Exception的调用栈打印处理。调用栈是我们分析问题最有效的信息。之所以说是按个人观点,我觉得这里应该有不同意见。而且我看到很多代码中没有完全遵循这种规则。
日志级别。如何记日志级别相对比较简单,能够在业务逻辑上处理掉的,显然不应该抛错,当然也就不用记,有些Exception是为了保护用,一旦catch这样异常,可能需要提醒系统人员可能有不应该出现的异常状态,但不影响业务逻辑,这样的异常打个warning级别就好了,其他情况都该打成Error级别。
分享到:
相关推荐
### Java UI组件使用心得 #### 1. 使用`UIManager`改变界面外观 在Java Swing中,`UIManager`提供了一种方式来控制组件的外观和感觉(LookAndFeel)。以下是一段示例代码: ```java UIManager.setLookAndFeel(...
- 在Java中,使用try-catch结构来处理可能发生的异常,如`Exception`。 - `Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver");`这行代码可能会抛出`ClassNotFoundException`,如果类路径中没有找到...
Java 异常处理学习心得 Java 异常处理是 Java 编程语言中的一个重要概念,它允许开发者捕获和处理程序中的异常,从而确保程序的可靠性和稳定性。在 Java 中,异常处理机制可以分为两种:可控式异常和运行时异常。 ...
通过理解Exception类、try-catch-finally结构,学习者可以编写出健壮的代码,处理运行时可能出现的问题。掌握异常处理机制,不仅能让程序运行更稳定,也是提高编程素质的重要标志。 总之,Java学习是一个系统的过程...
Java编程老鸟潜心写作,奉献高效率的Java学习心得 完全站在没有编程经验读者的角度,手把手教会读者学习Java 配16小时多媒体教学视频,高效、直观 一一击破Java入门可能会遇到的难点和疑惑 抽丝剥茧,层层推进,让...
Java编程老鸟潜心写作,奉献高效率的Java学习心得 完全站在没有编程经验读者的角度,手把手教会读者学习Java 配16小时多媒体教学视频,高效、直观 一一击破Java入门可能会遇到的难点和疑惑 抽丝剥茧,层层推进,让...
- **Exception**: Java中的异常分为两大类:**checked exception** 和 **unchecked exception**。前者需要在方法声明中抛出或捕获,后者则不需要。 - **IllegalStateException**: 当方法调用处于不合适的条件下时抛...
- **命名前缀**:抽象类命名应使用`Abstract`或`Base`开头,异常类以`Exception`结尾,测试类则以被测类名开始并以`Test`结尾。 - **正例**:`AbstractUser`, `BaseUser`, `LoginException`, `UserServiceTest` ##...
### Java JDBC编程总结 ... } catch (Exception e) { e.printStackTrace(); } } } ``` 以上步骤概述了使用JDBC进行数据库操作的基本流程。理解并掌握这些基本概念和操作步骤,对于进行数据库开发工作至关重要。
3. 字符流的由来和使用场景:由于文件编码的不同,字节流在处理文本文件时会遇到编码转换的问题,因此Java提出了字符流的概念。字符流读写操作是基于字符的,它在读取或写入字符时,会将字节转换为特定编码的字符,...
- 异常处理:通过try-catch-finally结构捕获和处理异常,Exception是所有检查异常的基类。 - 内部类:在类内部定义的类,包括成员内部类、局部内部类、匿名内部类等。 - 集合框架:如ArrayList、Vector、...
- **异常处理**:`java.lang.Throwable`及其子类,如Exception和Error,理解异常的层次结构和处理机制。 - **多线程**:`java.lang.Thread`和`java.util.concurrent`包,探究并发编程的实现,如锁、同步、线程池等...
**Berkeley DB Java Edition (JE)** 是Oracle公司提供的一个全Java实现的数据管理工具,特别适合于处理大量简单数据。它的核心特性包括: 1. **高效性能**:JE能够高效地处理从1到1百万条记录的数据库,其性能通常...
- **元数据处理**:例如处理注解(annotations),获取带有特定注解的类、方法等。 - **通用工具类**:例如深度拷贝、属性复制等,反射可以方便地在不同对象间复制属性。 以下是一个简单的反射使用示例: ```java ...
实验报告——Java异常处理 实验名称:Java异常处理实验 实验时间:2019年10月1日 学号:未提供 姓名:小雪 班级:未提供 成绩:未提供 一、实验目的 本次实验的主要目的是: 1. 掌握Java程序流程控制的关键元素,...
实验报告涵盖了三个主要的Java编程主题:异常处理、图形用户界面设计和输入输出。以下是这些主题的详细说明: **异常处理** 在Java中,异常处理是程序错误处理的关键部分。异常(Exception)是程序执行期间发生的非...
实验报告涵盖了Java编程中的几个关键知识点,包括异常处理、图形用户界面设计以及输入输出操作。以下是对这些主题的详细说明: 一、异常处理 在Java中,异常处理是一种处理程序运行时错误的方法。异常(Exception)...
- **异常捕获与处理**:用`try-catch`块处理可能出现的`ArithmeticException`或其他运行时异常。 6. **测试与调试** - **单元测试**:编写测试用例验证每个运算功能的正确性。 - **集成测试**:整体测试计算器的...
本资料“错误处理.zip”包含了作者在学习Java异常处理时的心得体会,结合了《编程简洁之道》的理念,为开发者提供了一份宝贵的参考资料。其中,主要文件“错误处理.pptx”很可能是一个详细的幻灯片教程,涵盖了以下...
3. **异常处理**:使用try-catch块来捕获并处理可能出现的异常,例如除以零时抛出的ArithmeticException。 4. **输入/输出**:如果是在控制台运行,可能用到System.in读取用户输入,并通过System.out打印结果。如果...