Java异常最佳实践笔记
引用
《如何善用Java异常》
https://juejin.im/post/5bacd8975188255c69780e7b
笔记
- Java中的异常分为两种:
- 无法捕获处理的系统级别Error.
- 可以被认为处理的检测异常Exception.
- 重点是他们都继承了Throwable接口
- 其中需要我们处理的只有Exception的子类。Exception的子类异常都是【可以】被我们的捕获处理的。可以被我们处理的意思,即可不处理,也可以处理。(即,可以处理,但没必要)
- 其中Exception的子类RuntimeException则允许我们不对他处理,它是一种特殊Exception,特殊之处在于允许我们不对它做捕获处理, 这种异常叫运行时异常,意思就是运行期间才会产生的,理论上大部分异常都是在运行期间才会产生了。也就是说大部分异常我们都可以置之不理,因为他是运行期间才会产生的,运行期间发现的意外大部分都是我们无法考虑到的(就像我们的生活,“意外”总比“预料”多。)
- 异常最佳实践:
- 能用runtimeExcepiton,用runtimeException。把异常抛给运行时处理,毕竟大部分的异常都是无法认为处理的,直接tryCatch也无法处理的异常,只能抛给运行时了。
- 能tryCatch的异常就不算异常,因为既然能tryCatch证明你能处理它,比如IOException,你TryCatch了这个IO异常,就表示有默认值返回(比如Null),如果无法返回默认值,请tryCatch后抛出运行时异常。
- 关于异常的日志问题,无论什么异常处理最好都logger打印一下日志,并且写明原因,如果有必要最好,Logger.info(“错误信息”,ex); 或者 log.XXX(Object obj,Thowable e) 的形式打印一下日志,这样就可以顺带打印出exceptionStackTrace.
- 其次就是不要使用ex.printStacktrace(),因为这是不好的习惯,请用log.XXX(Object obj,Thowable e) 的形式的代替他
- 理由:1. 你很难确定它会打印到哪个文件,让logger框架管理比较好。2.它有可能打印给终端用户页面一堆乱七八糟的信息。
- 定义一个全局的异常捕捉器(spring的全局异常捕捉器ErrorController),对所有错误异常进行解析处理成页面返回给终端用户页面。具体怎么实现呢?查一下《spring全局异常处理》相关资料即可。
关于异常比较好的知识点:
- Java7与异常Java7对异常做了两个改进。第一个是try-with-resources,第二个是catch多个异常。
- try-with-resources主要是针对IOException基本都是需要关闭资源的,所以用try-with-resources来关闭资源,可以让代码更好看。
不使用try-with-resources,我们在使用io等资源对象时,通常是这样写的:String getReadLine() throws IOException {
BufferedReader br = new BufferedReader(fileReader);
try {
return br.readLine();
} finally {
if (br != null) br.close();
}
}
复制代码使用try-with-recources的写法:String getReadLine() throws IOException {
try (BufferedReader br = new BufferedReader(fileReader)) {
return br.readLine();
}
}
显然,编绎器自动在try-with-resources后面增加了判断对象是否为null,如果不为null,则调用close()函数的的字节码。
只有实现了java.lang.AutoCloseable接口,或者java.io.Closable(实际上继随自java.lang.AutoCloseable)接口的对象,才会自动调用其close()函数。
有点不同的是java.io.Closable要求一实现者保证close函数可以被重复调用。而AutoCloseable的close()函数则不要求是幂等的。
具体可以参考Javadoc。但是,需要注意的是try-with-resources会出现异常覆盖的问题,也就是说catch块抛出的异常可能会被调用close()方法时抛出的异常覆盖掉。我们会在下面的小节讲到异常覆盖。
多异常捕捉直接上代码:public static void main(String[] args) {
try {
int a = Integer.parseInt(args[0]);
int b = Integer.parseInt(args[1]);
int c = a / b;
System.out.println("result is:" + c);
} catch (IndexOutOfBoundsException | NumberFormatException | ArithmeticException ie) {
System.out.println("发生了以上三个异常之一。");
ie.getMessage();
// 捕捉多异常时,异常变量默认有final修饰,
// 所以下面代码有错:
// ie = new ArithmeticException("test");
}
}
作者:xy的技术圈
链接:https://juejin.im/post/5bacd8975188255c69780e7b
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
什么是异常覆盖正如我们前面提到的,在finally块调用资源的close()方法时,是有可能抛出异常的。与此同时我们可能在catch块抛出了另一个异常。那么catch块抛出的异常就会被finally块的异常“吃掉”。看看这段代码,调用test()方法会输出什么?void test() {
try {
overrideException();
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
void overrideException() throws Exception {
try {
throw new Exception("A");
} catch (Exception e) {
throw new Exception("B");
} finally {
throw new Exception("C");
}
}
复制代码会输出C。可以看到,在catch块的B被吃掉了。JDK提供了Suppressed的两个方法来解决这个问题:// 调用test会输出:// C// Avoid test() {
try {
overrideException();
} catch (Exception e) {
System.out.println(e.getMessage());
Arrays.stream(e.getSuppressed())
.map(Throwable::getMessage)
.forEach(System.out::println);
}
}
void overrideException() throws Exception {
Exception catchException = null;
try {
throw new Exception("A");
} catch (Exception e) {
catchException = e;
} finally {
Exception exception = new Exception("C");
exception.addSuppressed(catchException);
throw exception;
}
}
复制代码异常链你可以在抛出一个新异常的时候,使用initCause方法,指出这个异常是由哪个异常导致的,最终形成一条异常链。详情请查阅公众号之前的关于异常链的文章。
作者:xy的技术圈
链接:https://juejin.im/post/5bacd8975188255c69780e7b
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。