前言

人非圣贤,孰能无过,更何况是程序员.所以排错也是我猿的一项基本技能.

一.剖析异样品种

排错要先晓得有哪些错,先上图,而后一项一项剖析!

1.都继承于Throwable

这个类的接口根本决定着java所有异样的行为,比方罕用在catch中getMessage(),printStackTrace().其余具体如下

返回类型

办法名称

办法阐明

Throwable

fillInStackTrace()

在异样堆栈跟踪中填充。

《2020最新Java根底精讲视频教程和学习路线!》

Throwable

getCause()

返回此 throwable 的 cause;如果 cause 不存在或未知,则返回 null。

String

getLocalizedMessage()

创立此 throwable 的本地化形容。

String

getMessage()

返回此 throwable 的具体音讯字符串。

StackTraceElement[]

getStackTrace()

提供编程拜访由 printStackTrace() 输入的堆栈跟踪信息。

Throwable

initCause(Throwable cause)

将此 throwable 的 cause 初始化为指定值。

void

printStackTrace()

将此 throwable 及其追踪输入至规范谬误流。

void

printStackTrace(PrintStream s)

将此 throwable 及其追踪输入到指定的输入流。

void

printStackTrace(PrintWriter s)

将此 throwable 及其追踪输入到指定的 PrintWriter。

void

setStackTrace(StackTraceElement[] stackTrace)

设置将由 getStackTrace() 返回,并由 printStackTrace() 和相干办法输入的堆栈跟踪元素。

String

toString()

返回此 throwable 的简短形容。

2.继承上分类

  • error:零碎级别的异样.该谬误像运行时堆溢出,属于代码级不可控领域.

    • 非受检异样
  • exception:利用级异样,属于代码级可控异样.所以自定义的异样都须要继承这个接口. 同时exception也包含如下两类.

    • 运行时异样(非受检异样)
    • 受检异样

3.何为受检异样跟非受检异样

  • 受检异样:编译器要查看此类异样,也就是编辑器会被动爆红提醒减少try catch.
  • 非受检异样:编译器不查看此类异样,理论开发时须要开发人员本人逻辑躲避.

二.异样捕获

妇孺皆知,java中通过try catch来捕获异样,打到日志里为排错提供根据.上面对try catch的一些场景进行剖析,照例先上一段案例代码

public class TestTryCatch {    public static void main(String[] args) {        System.out.println(returnInt());    }    public static int returnInt(){        int x;        try {            x = 1;            return x;        } catch (NullPointerException e) {            x = 2;            return x;        } finally {            x = 3;        }    }} 

1.通过字节码查看returnInt()逻辑

jvm的try catch 逻辑是通过Exception table条件来实现的,在from到to的计数行数中爆出的异样从上向下比对type类型,合乎的则批改程序计数器中的行数到target批示的行数.

public static int returnInt();    descriptor: ()I    flags: (0x0009) ACC_PUBLIC, ACC_STATIC    Code:      stack=1, locals=4, args_size=0         0: iconst_1   //取1放到栈顶                                                ---->是try块中的开始         1: istore_0   //从栈顶将数值放到本地变量(局部变量表第0个变量)          2: iload_0    //保留x到returnValue中                                      ---->是try块中的完结         3: istore_1                                                                                                                    ---->是finally块中的开始(没报错)         4: iconst_3   //取3放到栈顶             5: istore_0   //从栈顶将数值放到本地变量(局部变量表第0个变量)               ---->是finally块中的完结(没报错)         6: iload_1         7: ireturn   //返回后果         8: astore_1  //给catch中定义的Exception e赋值                              ---->是catch块中的开始         9: iconst_2  //catch块中的x=2        10: istore_0        11: iload_0        12: istore_2  //                                                         ---->是catch块中的完结        13: iconst_3  //finaly块中的x=3                                           ---->是finally块中的开始(catch到异样)        14: istore_0          15: iload_2        16: ireturn   //返回后果                                               ---->是finally块中的完结(catch到异样)        17: astore_3  //如果呈现了不属于java.lang.NullPointerException及其子类的异样才会走到这里         18: iconst_3  //finaly块中的x=3                                      ---->是finally块中的开始(没catch到异样)        19: istore_0            20: aload_3   //将异样搁置到栈顶,并抛出        21: athrow    //                                                    ---->是finally块中的完结(没catch到异样)      Exception table:         from    to  target type             0     4     8   Class java/lang/NullPointerException             0     4    17   any             8    13    17   any 

2.列出后果

同时从字节码能够看出有如下后果(都假如在x赋值后,return前报错):

  1. 如果try中没有bug,代码会先执行try中的x=1而后执行finally中的x=3,return 3
  2. 如果try中有bug,且被catch到,catch中无异样的话会先执行x=1,而后再执行catch中的x=2,最初执行finally中的x=3,return 3
  3. 如果try中有bug,且被catch到,catch中有异样的话会先执行x=1,而后再执行catch中的x=2,后执行finally中的x=3,最初抛出异样
  4. 如果try中有bug,然而没有被catch到则执行 x=1,而后执行x = 3,最初抛出异样

3.得出结论

从上能够得出结论

  • 捕捉异样与抛异样,必须是齐全匹配,或者捕捉异样是抛异样的父类
  • 须要思考到finally最终会执行,须要思考try中对象会被批改的问题.
  • 不要在 finally 块中应用 return. 通过测试如果finally中加了retrun x;则字节码中不会呈现athrow,而是变为如下ireturn,所以应用的时候须要留神finally中如果有return那么异样将不会被抛出.

     ......    23: astore_3        24: iconst_3        25: istore_0        26: iload_0        27: ireturn      Exception table:         from    to  target type             0     7    11   Class java/lang/NullPointerException             0     7    23   any            11    19    23   any   

    在阿里泰山版开发手册中也有阐明.

  • try catch中的逻辑都是不平安的,都有可能两头跳出,然而finally必定会执行,所以能够在finally中将资源对象、流对象进行敞开
  • 字节码中Exception table能够看出:捕捉异样与抛异样,必须是齐全匹配,或者捕捉异样是抛异样的父类,不然逻辑进不到catch当中

三.异样小工具

1.addSuppressed

例如这样一个场景:try中呈现了报错,然而进入到finally执行敞开io的时候也报了错,那么后果是办法只会抛出finally中报的错.addSuppressed能够用来解决这个问题,能够同时将try中的谬误跟finally中的谬误都抛出.代码举例如下:

public static int returnInt() throws IOException{        IOException e = null;        int x = 0;        try {            x = 1;              dothing();            return x;        }catch (IOException e1){            e = e1;            x = 2;            return x;        }finally {            x = 3;            try {                dothing();            }catch (IOException e2){                if(e != null){                    //留神这里                    e.addSuppressed(e2);                }else{                    e = e2;                }            }            if(e != null){                throw e;            }        }    } 

2.try-catch-resource

不止各位看官是否感觉下面addSuppressed的书写办法特地的繁琐呢,1.7版本的try-catch-resource通过让资源实现AutoClosable中的close来实现无需手写,主动调用敞开逻辑的性能;举例代码:

public class Connection implements AutoCloseable {  public void sendData() {    System.out.println("正在发送数据");  }  @Override  public void close() throws Exception {    System.out.println("正在敞开连贯");  }}---------------------------------------------------public class TryWithResource {  public static void main(String[] args) {    try (Connection conn = new Connection()) {      conn.sendData();    }    catch (Exception e) {      e.printStackTrace();    }  }} 

而后将代码放在idea外面查看反编译代码

public class TryWithResource {    public TryWithResource() {    }    public static void main(String[] args) {        try {            Connection conn = new Connection();            Throwable var2 = null;            try {                conn.sendData();            } catch (Throwable var12) {                var2 = var12;                throw var12;            } finally {                if (conn != null) {                    if (var2 != null) {                        try {                            conn.close();                        } catch (Throwable var11) {                            var2.addSuppressed(var11);                        }                    } else {                        conn.close();                    }                }            }        } catch (Exception var14) {            var14.printStackTrace();        }    }} 

看吧,原来try-catch-resource就是本质上try-catch-finally加addSuppressed的组合

3.lombok的@SneakyThrows

退出有场景不须要精密判断,而是须要梭哈异样的状况下这个注解能够很不便的帮忙你主动生成try-catch,如下代码

import lombok.SneakyThrows;/** * @author wuzt */public class TryWithResource {    @SneakyThrows    public static void main(String[] args) {        Connection conn = new Connection();        conn.sendData();    }} 

应用idea反编译后:

public class TryWithResource {    public TryWithResource() {    }    public static void main(String[] args) {        try {            Connection conn = new Connection();            conn.sendData();        } catch (Throwable var2) {            throw var2;        }    }} 

由上发现,其实这个注解的意义就是抛出所有异样

利用的的场景比拟合乎的像是阿里开发手册中的如下

完结

求各位看客老爷们点个赞再走啊.

链接:https://juejin.cn/post/690186...