通过实践了解如何处理Java异常

大多数团队都使用了几种最佳实践。以下是帮助你入门或改进异常处理的9个最重要的内容。

1.在finally块中清理资源或使用Try-With-Resource语句
在try块中使用资源是很频繁的,比如InputStream,之后需要关闭它。这些情况中的一个常见错误是在try块结束时关闭资源。

public void doNotCloseResourceInTry() {

FileInputStream inputStream = null;

try {

File file = new File("./tmp.txt");

inputStream = new FileInputStream(file);

// use the inputStream to read a file

// do NOT do this

inputStream.close();

} catch (FileNotFoundException e) {

log.error(e);

} catch (IOException e) {

log.error(e);

}

}

问题是只要没有抛出异常,这种方法似乎完全正常。try块中的所有语句都将被执行,资源将被关闭。

但是你添加了try块是有原因的。你调用一个或多个可能抛出异常的方法,或者你自己抛出异常。这意味着你可能无法到达try块的末尾。因此,你将不会关闭资源。

因此,你应该将所有清理代码放入finally块或使用try-with-resource语句。

使用Finally块
与try块的最后几行相比,finally块始终执行。这可以在成功执行try块之后或在catch块中处理异常之后发生。因此,你可以确保清理所有已打开的资源。

public void closeResourceInFinally() {

FileInputStream inputStream = null;

try {

File file = new File("./tmp.txt");

inputStream = new FileInputStream(file);

// use the inputStream to read a file

} catch (FileNotFoundException e) {

log.error(e);

} finally {

if (inputStream != null) {

try {

inputStream.close();

} catch (IOException e) {

log.error(e);

}

}

}

}

Java 7的Try-With-Resource
另一种选择是try-with-resource语句,我在Java异常处理的介绍中对此进行了更详细的解释。

如果资源实现AutoCloseable接口,则可以使用它。这就是大多数Java标准资源所做的事情。当你在try子句中打开资源时,它将在try块执行后自动关闭,或者处理异常。

public void automaticallyCloseResource() {

File file = new File("./tmp.txt");

try (FileInputStream inputStream = new FileInputStream(file);) {

// use the inputStream to read a file

} catch (FileNotFoundException e) {

log.error(e);

} catch (IOException e) {

log.error(e);

}

}

2.特定异常
抛出的异常越具体越好。请记住,不明白你代码的同事,或者你可能在几个月后需要调用你的方法并处理异常。

因此,请务必提供尽可能多的信息。这使你的API更易于理解。因此,你的方法的调用者将能够更好地处理异常或通过额外的检查来避免它。

因此,总是尝试找到最适合你的异常事件的类,例如抛出NumberFormatException而不是IllegalArgumentException。并避免抛出非特定的异常。

public void doNotDoThis() throws Exception {

...

}

public void doThis() throws NumberFormatException {

...

}

3.记录你声明的异常
无论何时在方法签名中指定异常,都应该在Javadoc中记录它。这与以前的最佳实践具有相同的目标:为调用者提供尽可能多的信息,以便他可以避免或处理异常。

因此,请确保向Javadoc 添加@throws声明并描述可能导致异常的情况。

/**

* This method does something extremely useful ...

*

* @param input

* @throws MyBusinessException if ... happens

*/

public void doSomething(String input) throws MyBusinessException {

...

}

4.使用描述信息抛出异常
这种最佳实践背后的想法类似于前两种实践。但是这次,你不向调用方提供有关方法的信息。每个必须了解在日志文件或监视工具中抛出异常时发生了什么的人都会读取异常的消息。

因此,它应该尽可能准确地描述问题,并提供最相关的信息来理解异常事件。

别误会我的意思; 你不应该写一段文字。但是你应该用1-2个简短的句子来解释这个例外的原因。这有助于你的运营团队了解问题的严重性,还可以让你更轻松地分析任何服务事件。

如果抛出一个特定的异常,它的类名很可能已经描述了那种错误。因此,你无需提供大量其他信息。一个很好的例子是NumberFormatException。它会被类java.lang.Long的构造函数抛出,当你以错误的格式提供String参数。

try {

new Long("xyz");

} catch (NumberFormatException e) {

log.error(e);

}

NumberFormatException类的名称已经告诉你问题的类型。它的消息只需要提供导致问题的输入字符串。如果异常类的名称不具有表现力,则需要在消息中提供所需的信息。

17:17:26,386 ERROR TestExceptionHandling:52 - java.lang.NumberFormatException: For input string: "xyz"

5.优先捕获最具体的异常
大多数IDE都可以帮助你实现这一最佳实践。当你尝试首先捕获不太具体的异常时,它们提示无法访问的代码块。

问题是只有匹配异常的第一个catch块才会被执行。因此,如果首先捕获IllegalArgumentException,则永远不会到达应该处理更具体的NumberFormatException的catch块,因为它是IllegalArgumentException的子类。

始终优先捕获最具体的异常类,并将不太具体的catch块添加到列表的末尾。

你可以在以下代码段中看到此类try-catch语句的示例。第一个catch块处理所有的NumberFormatException,第二个处理所有不是NumberFormatException的IllegalArgumentException 异常。

public void catchMostSpecificExceptionFirst() {

try {

doSomething("A message");

} catch (NumberFormatException e) {

log.error(e);

} catch (IllegalArgumentException e) {

log.error(e)

}

}

6.Don't Catch Throwable
Throwable是所有异常和错误的超类。你可以在catch子句中使用它,但你永远不应该这样做!

如果在catch子句中使用Throwable,它不仅会捕获所有异常; 它还会捕获所有错误。JVM抛出错误以指示应用程序无法处理的严重问题。典型的例子是OutOfMemoryError或StackOverflowError。两者都是由应用程序无法控制的情况引起的,无法处理。

所以,最好不要抓住Throwable,除非你完全确定你处于一个特殊情况,你可以或者需要处理错误。

public void doNotCatchThrowable() {

try {

// do something

} catch (Throwable t) {

// don't do this!

}

}

7.Don't Ignore Exceptions
你是否曾经分析过只有用例第一部分被执行的错误报告?

这通常是由忽略的异常引起的。开发人员可能非常确定它永远不会被抛出并添加了一个不处理或记录它的catch块。当你找到这个代码块时,你很可能甚至会发现一个著名的“This will never happen

”的评论。

public void doNotIgnoreExceptions() {

try {

// do something

} catch (NumberFormatException e) {

// this will never happen

}

}

好吧,你可能正在分析一个不可能发生的问题。

所以,请永远不要忽视异常。你不知道代码将来会如何变化。有人可能会删除阻止异常事件的验证而不会认识到这会产生问题。或者抛出异常的代码会被更改,现在抛出同一个类的多个异常,并且调用代码不会阻止所有这些异常。

你至少应该写一条日志消息,告诉大家不可思议的事情刚刚发生,而且有人需要检查它。

public void logAnException() {

try {

// do something

} catch (NumberFormatException e) {

log.error("This should never happen: " + e);

}

}

8.Don't Log and Throw
这可能是此列表中最常被忽略的最佳做法。你可以找到许多代码片段,甚至是catch,log和重新throw异常的库。

try {

new Long("xyz");

} catch (NumberFormatException e) {

log.error(e);

throw e;

}

在发生异常时记录异常可能会感觉很直接,然后重新抛出它以便调用者可以适当地处理它。但它会为同一个异常写出多条错误消息。

17:44:28,945 ERROR TestExceptionHandling:65 - java.lang.NumberFormatException: For input string: "xyz"

Exception in thread "main" java.lang.NumberFormatException: For input string: "xyz"

at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)

at java.lang.Long.parseLong(Long.java:589)

at java.lang.Long.(Long.java:965)

at com.stackify.example.TestExceptionHandling.logAndThrowException(TestExceptionHandling.java:63)

at com.stackify.example.TestExceptionHandling.main(TestExceptionHandling.java:58)

其他消息也不添加任何信息。如最佳实践#4中所述,异常消息应描述异常事件。堆栈跟踪告诉你抛出异常的类,方法和行。

如果需要添加其他信息,则应捕获异常并将其包装在自定义异常中。但请务必遵循最佳做法9。

public void wrapException(String input) throws MyBusinessException {

try {

// do something

} catch (NumberFormatException e) {

throw new MyBusinessException("A message that describes the error.", e);

}

}

因此,如果你想要处理它,只捕获异常。否则,在方法签名中指定它并让调用者处理它。

9.在没有消费的情况下包装异常
有时候捕获标准异常并将其包装成自定义异常会更好。此类异常的典型示例是应用程序或框架特定的业务异常。这允许你添加其他信息,还可以为异常类实现特殊处理。

执行此操作时,请确保将原始异常设置为cause。该异常类提供了接受一个特定的构造方法的Throwable作为参数。否则,你将丢失原始异常的堆栈跟踪和消息,这将导致难以分析导致异常的异常事件。

public void wrapException(String input) throws MyBusinessException {

try {

// do something

} catch (NumberFormatException e) {

throw new MyBusinessException("A message that describes the error.", e);

}

}

总结
正如所看到的,当你抛出或捕获异常时,你应该考虑许多不同的事情。其中大多数都旨在提高代码的可读性或API的可用性。

异常通常同时是错误处理机制和通信媒介。因此,您应该确保与同事讨论要应用的最佳实践和规则,以便每个人都能理解通用概念并以相同的方式使用它们。

(0)

相关推荐

  • Java异常处理中的各种细节汇总

    前言 今天我们来讨论一下,程序中的错误处理. 在任何一个稳定的程序中,都会有大量的代码在处理错误,有一些业务错误,我们可以通过主动检查判断来规避,可对于一些不能主动判断的错误,例如 RuntimeException,我们就需要使用 try-catch-finally 语句了. 有人说,错误处理并不难啊,try-catch-finally 一把梭,try 放功能代码,在 catch 中捕获异常.处理异常,finally 中写那些无论是否发生异常,都要执行的代码,这很简单啊. 处理错误的代码,确实并

  • 详解Java异常处理最佳实践及陷阱防范

    前言 不管在我们的工作还是生活中,总会出现各种"错误",各种突发的"异常".无论我们做了多少准备,多少测试,这些异常总会在某个时间点出现,如果处理不当或是不及时,往往还会导致其他新的问题出现.所以我们要时刻注意这些陷阱以及需要一套"最佳实践"来建立起一个完善的异常处理机制. 异常分类 首先,这里我画了一个异常分类的结构图. 在JDK中,Throwable是所有异常的父类,其下分为"Error"和"Exception&

  • Java异常处理的12条军规总结

    异常的概念 异常是程序中的一些错误,但并不是所有的错误都是异常,并且错误有时候是可以避免的. 比如说,你的代码少了一个分号,那么运行出来结果是提示是错误java.lang.Error:如果你用System.out.println(11/0),那么你是因为你用0做了除数,会抛出java.lang.ArithmeticException的异常. 异常发生的原因有很多,通常包含以下几大类: •用户输入了非法数据. •要打开的文件不存在. •网络通信时连接中断,或者JVM内存溢出. 这些异常有的是因为用

  • Java异常处理与throws关键字用法分析

    本文实例讲述了Java异常处理与throws关键字用法.分享给大家供大家参考,具体如下: Java异常处理 认识异常: 1.异常是导致程序中断运行的一种指令流,如果不对异常进行正确处理,则可能导致程序的中断执行,造成不必要的损失. 2.异常范例 空指针异常 Exc e=null; System.out.println(e.i); 除0异常 int a=10; int b=0; System.out.println(a/b); 3.处理异常 异常格式: try{ 异常语句: } catch(Exc

  • 如何利用Retrofit+RxJava实现网络请求的异常处理

    通常情况下我们在与服务器进行通信的时候,不一定就不会出错,有时会出现其他的错误,这个时候我们只要和服务器约定好各种异常,在返回结果处进行判断,到底是执行错误,还是返回正常数据.具体的思路大致就是这样.这里我们定义ExceptionHandle,这里我参考网上的东西,然后稍微做了一些改动. ExceptionHandle public class ExceptionHandle { private static final int UNAUTHORIZED = 401; private stati

  • 通过实践了解如何处理Java异常

    大多数团队都使用了几种最佳实践.以下是帮助你入门或改进异常处理的9个最重要的内容. 1.在finally块中清理资源或使用Try-With-Resource语句 在try块中使用资源是很频繁的,比如InputStream,之后需要关闭它.这些情况中的一个常见错误是在try块结束时关闭资源. public void doNotCloseResourceInTry() { FileInputStream inputStream = null; try { File file = new File("

  • 全面了解java异常

    目录 异常的概念 异常体系 异常的分类 异常产生的过程分析 异常的处理 抛出异常throw 声明异常throws 捕获异常try-catch 捕获异常语法 如何获取异常信息: finally代码块 try catch 语句中有return 的各类情况 异常注意事项 自定义异常 概述 自定义异常演示 异常的概念 异常,在程序中的意思是:程序在执行过程中,出现的非正常的情况,最终会导致JVM的非正常停止. Java的异常机制主要依赖于try.catch.finally.throw和throws五个关

  • 超详细讲解Java异常

    目录 一.Java异常架构与异常关键字 Java异常简介 Java异常架构 1.Throwable 2.Error(错误) 3.Exception(异常) 4.受检异常与非受检异常 Java异常关键字 二.Java异常处理 声明异常 抛出异常 捕获异常 如何选择异常类型 常见异常处理方式 1.直接抛出异常 2.封装异常再抛出 3.捕获异常 4.自定义异常 5.try-catch-finally 6.try-with-resource 三.Java异常常见面试题 1.Error 和 Excepti

  • java 异常详解及应用实例

    java  异常 异常的使用实例(异常分类:Error(是由JVM调用系统底层发生的,只能修改代码) 和 Exception(是JVM发生的,可以进行针对性处理)) 1.如果一个方法内可能出现异常,那么可以将异常通过throw的方式new 出相应的异常类,并在方法上   声明throws可能抛出的异常类抛给调用者,调用者可以进行异常捕获,或者继续抛出异常由 上层调用者继续处理,    如果整个过程都没有将异常进行任何处理,那么将由JVM虚拟机进行默认的处理 2.调用者可以对异常进行try()ca

  • java异常(Exception)处理机制详解

    一. 异常的定义 在<Java编程思想>中这样定义 异常:阻止当前方法或作用域继续执行的问题.虽然java中有异常处理机制,但是要明确一点,决不应该用"正常"的态度来看待异常.绝对一点说异常就是某种意义上的错误,就是问题,它可能会导致程序失败.之所以java要提出异常处理机制,就是要告诉开发人员,你的程序出现了不正常的情况,请注意. 记得当初学习java的时候,异常总是搞不太清楚,不知道这个异常是什么意思,为什么会有这个机制?但是随着知识的积累逐渐也对异常有一点感觉了.举一

  • java异常与错误处理基本知识

    异常与错误:异常: 在Java中程序的错误主要是语法错误和语义错误,一个程序在编译和运行时出现的错误我们统一称之为异常,它是VM(虚拟机)通知你的一种方式,通过这种方式,VM让你知道,你(开发人员)已经犯了个错误,现在有一个机会来修改它.Java中使用异常类来表示异常,不同的异常类代表了不同的异常.但是在Java中所有的异常都有一个基类,叫做Exception.错误:它指的是一个合理的应用程序不能截获的严重的问题.大多数都是反常的情况.错误是VM的一个故障(虽然它可以是任何系统级的服务).所以,

  • Java异常退出条件的判断示例代码

    无论是功能性代码还是算法性代码,程序都是一系列流程的合集 既然是流程就分为:一般流程和异常流程: 一般流程保证了基本功能: 异常流程则是对程序稳定性的保证,不能因为一些非法输入,项目就挂了: 注意,布尔表达式的先后顺序,有时不可以交换 if (null == instance || instance.isEmpty()) 0. 常见异常退出条件 参数为空: 表示长度,表示索引的整型为负数,或者超出待索引数组或容器的范围: 1. String 的 startsWith 函数 首先来看 String

  • java异常继承何类,运行时异常与一般异常的区别(详解)

    一.基本概念 Throwable是所有异常的根,java.lang.Throwable Error是错误,java.lang.Error Exception是异常,java.lang.Exception Throwable: 有两个重要的子类:Exception(异常)和 Error(错误),二者都是 Java 异常处理的重要子类,各自都包含大量子类. Error(错误):是程序无法处理的错误,表示运行应用程序中较严重问题.大多数错误与代码编写者执行的操作无关,而表示代码运行时 JVM(Java

  • 深入分析Java异常

    java异常分为两大类,Checked异常和Runtime异常,Checked异常都是在编译阶段可以被处理的异常. Checked异常和Runtime异常的区别和联系 Checked异常都是可以被处理的异常,在程序中必须显式地处理Checked异常,如果没有处理,那么编译就会报错.而Runtime异常可以不被显式的处理: 都是Exception的子类,继承了RuntimeException的就是Runtime异常,其他的就是Checked异常. 常见异常类 列举几个常见的运行时异常Runtime

  • 一文给你通俗易懂的讲解Java异常

    什么是异常? 最简单的,看一个代码示例: public static void main(String[] args) { int a = 1; int b = 0; System.out.println(a / b); } 这段代码有什么问题?简单,除数不能为0对吧,我们打印输出: 显而易见,程序出问题了,不能正常执行了,这里出现了一些爆红的信息,这些就是异常提示,这就是Java中提供的异常机制,当你的程序存在问题的情况下,会给你打印输出一些信息,这个就叫做异常信息. 字面意思上去理解,所谓"

随机推荐