处理异常的9个方法

原文 https://www.cnblogs.com/kcher90/p/7468512.html

异常处理是一件很头疼的事情。
到底是需要【处理掉】还是需要【抛出去】。

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);
    }
}

finally块中的语句一定会被执行。因此所有开启的资源都能够确保被关闭。
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);
            }
        }
    }
}

JDK7的Try-With-Resource

资源实现了AutoCloseable接口的话,可以使用try-with-resource语句。
如果你在try-with-resource语句中声明了一个资源,
它将会在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. 优先使用更明确的异常

你的代码会被别人使用,所以异常要明确,
public void doNotDoThis() throws Exception { ... }    
public void doThis() throws NumberFormatException { ... }

3. 在文档里记录你的异常

1. 在方法签名处指定一个异常,应该同时将它记录到Javadoc里。
2. 给方法调用者提供尽可能多的信息,让他可以避免触发异常或者方便的他处理异常。
3. 确保在Javadoc里边添加@throws声明并且描述什么样的情况会导致异常。
/** * This method does something extremely useful ... * * @param input * @throws MyBusinessException if ... happens */
public void doSomething(String input) throws MyBusinessException { ... }

4. 将异常与它的描述信息一起抛出

1. 并不是给方法调用者提供信息,当异常信息被打印到日志文件时,需要查看的人能够获得足够多的信息
2. 提供足够多的信息来描述出现了什么问题,发生了什么异常。
//给java.lang.Long的构造函数提供一个错误格式的String类型参数时
//便会抛出NumberFormatException
try {
    new Long("xyz");
} catch (NumberFormatException e) {
    log.error(e);
}

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

5. 优先捕获更明确的异常

NumberFormatException是IllegalArgumentException的子类
先捕获NumberFormatException,再捕获IllegalArgumentException。
IDE的提示就是这样的
public void catchMostSpecificExceptionFirst() {
    try {
        doSomething("A message");
    } catch (NumberFormatException e) {
        log.error(e);
    } catch (IllegalArgumentException e) {
        log.error(e)
    }
}

6. 不要捕获Throwable

* Throwable是所有异常(Exception)和错误(Error)的父类。
* 在catch从句中使用了Throwable,它将不仅捕获所有异常,它还将捕获所有错误,
错误是由JVM抛出的,用来表明不打算让应用来处理的严重错误。
* OutOfMemoryError和StackOverflowError便是典型的例子,
它们都是由于一些超出应用处理范围的情况导致的。
public void doNotCatchThrowable() {
    try {
        // do something
    } catch (Throwable t) {
        // don't do this!
    }
}

7. 别忽略异常

* 忽略异常导致不完整的bug报告,非常难以分析。
* 你认为这里永远不会抛出异常,加了一个不处理且不打印日志的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. 不要打印异常日志的同时将其抛出

导致一个异常被打印多次
它的调用者也打印
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)

* 异常消息应该描述异常事件,堆栈信息告诉你异常抛出的所在类、方法、行数。
* 如果你需要添加额外的信息,你应该将异常捕获并将其包在你的自定义异常中,
* 但要确保遵循第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. 包裹某个一场的同时不要丢弃它原本的信息

有时候我们需要捕获一个标准异常并用自定义异常包裹住它,
一个典型的例子便是比如某个应用或者框架的指明业务异常,
它允许你添加额外的信息,你也可以实现特别的异常处理方法。
当你这么做的时候,要确保将原本的异常作为原因设置到自定义异常里面,
Exception类提供指定的构造方法可以接收Throwable类的对象作为参数,
否则你将会丢失堆栈信息和原异常的消息,这将会令异常分析变得什么的困难。
public void wrapException(String input) throws MyBusinessException {
    try {
        // do something
    } catch (NumberFormatException e) {
        throw new MyBusinessException("A message that describes the error.", e);
    }
}
全部评论

相关推荐

点赞 收藏 评论
分享
牛客网
牛客企业服务