先如下代码:
1 @Test
2 public void testException1() {
3 System.out.println("begin...");
4 int[] arr = new int[]{1};
5 System.out.println(arr[1]);
6 }
如下是运行结果,异常是从第5行抛出来的,控制台里显示的异常堆栈信息也是始于第5行。
java.lang.ArrayIndexOutOfBoundsException: 1 at com.mytest.testException1(MyExceptionTest.java:5)
我们在写程序时往往要捕获异常。如下是画蛇添足型:
1 @Test
2 public void testException() {
3 try {
4 System.out.println("begin...");
5 int[] arr = new int[]{1};
6 System.out.println(arr[1]);
7 } catch (Exception ex) {
8 throw ex;
9 }
10 }
很明显,捕获了异常,却只是原样throw出去,没任何意义!
如下处理方式也不可取:
1 @Test
2 public void testException() {
3 try {
4 System.out.println("begin...");
5 int[] arr = new int[]{1};
6 System.out.println(arr[1]);
7 } catch (Exception ex) {
8 throw new MyException( ex.getMessage() );
9 }
10 }
异常的堆栈始于抛出异常的代码行。catch里经过这样中转后,导致堆栈信息的显示的行号不再是6,而是8。尤其当代码逻辑复杂时,这样做无疑增加了排障的难度。
当然,我们有时要在系统里通过自定义异常类型,来实现我们所需的逻辑控制(自定义异常是个好东西哦,《代码整洁之道》一书中多次提到使用异常来替代错误码)。如果真要通过MyExecption中转出去,可在throw之前将完整的异常信息记录到日志文件里。
catch里捕获到Exception后,先打印error log再做后续处理,会是个好习惯。
私自吞异常是要犯法的哦~~
类似如下代码是不是很熟悉?哈哈,去翻翻自己写的代码吧~
1 public int myBiz() {
2 int result;
3 try {
4 System.out.println("begin...");
5 int[] arr = new int[]{1};
6 result = arr[1];
7 return result;
8 } catch (Exception e) {
9 return 0;
10 }
11 }
有时,我们要借助try...finally
finally里的代码会在try子句或catch子句执行完成后执行。借助try...finally,我们可以设置日志埋点,比如:记录程序耗时,记录程序完结标记。也可以用来释放资源,例如:关闭IO、释放流。
1 public void testTryFinally() {
2 try {
3 System.out.println("begin...");
4 int[] arr = new int[]{1};
5 System.out.println(arr[1]);
6 }finally {
7 System.out.println("over");
8 }
9 }
利用ExceptionUtils记录异常
当程序运行过程中出现异常时,将异常的详细信息打印到日志文件是必不可少的,这将有助于我们排障。
.net程序员都知道,要打印出来一个异常的详细信息,直接ex.ToString()就可以了。
如果在java里,你还这样,那就out了。查看一下Exception的基类——Throwable的toString()方法,可知,ex.toString()返回的是异常类型和message。
这时,要打印完整的异常信息,apache commons-lang3包里的ExceptionUtils工具类可要派上用场了。 没错,用ExceptionUtils#getStackTrace(final Throwable throwable)
揭短ExceptionUtils
有些异常并没有root cause的,此时,调用ExceptionUtils的getRootCause(final Throwable throwable)返回值是null,而你调用其getRootCauseMessage(final Throwable th)时,反而有返回值。 查看getRootCauseMessage的代码实现,发现它做了二元判断,如果root cause是null,它就去取th本身的message。 前者返回null,后者有返回值,自我赶脚有些费解!what about you?
//public class ExceptionUtils { public static Throwable getRootCause(final Throwable throwable) { final List<Throwable> list = getThrowableList(throwable); return list.size() < 2 ? null : (Throwable)list.get(list.size() - 1); } public static String getRootCauseMessage(final Throwable th) { Throwable root = ExceptionUtils.getRootCause(th); root = root == null ? th : root; return getMessage(root); }