Java异常机制

前言:

写了两周的博客了,深的浅的都有,总有人问我写博客有什么用?能出书吗?写的太浅有人看吗?对找工作有什么帮助吗?不想争辩,也不愿把自己说的多么高大上,分享本身是一件简单的小事,但是能给我带来快乐,我享受别人看过我博客之后有种觉得有收获那种感觉,就这么简单。还有那些害怕写博客被别人质疑太浅的,其实大可不必,总有一些人掌握的知识没有你多,也许你的一句话就解决了他们的疑问。言归正传,今天给大家带来Java异常体系。

1、Java异常体系结构图

Java异常体系图
1.1、结构角度分类

Error类:系统错误,我们最熟悉的OutOfMemeroyError、StackOverFlowError、NoClassDefoundError等都是该类的子孙,这些错误主要与机器有关。值得注意的是Error我们也可以通过try catch捕获到,尽管捕获到意义不大,比如OutOfMemeroyError我们捕获到要进行处理是不可能的,因为系统已经不工作了。
Exception:异常类,一个Exception发生了那么证明程序员写的代码出问题了,而不是机器的问题。

1.2、Java对异常处理要求分类

Checked Exception类:
A:体系图中的红色部分,编译期间Java编译器会强制要求程序员对该类异常进行处理,要么try catch、要么throw抛出去给调用层处理,否则无法执行。Eclipse里面那些给我们提示的实际上都是这类异常。
B:为什么会存在这种异常呢?简单来说就是对客观上存在的潜在错误进行预处理,就像我们可能并不是每个人都会得天花,但是我们必须接种天花疫苗。Java号称平台无关,程序员写的代码可能会出现在各种环境中,拿IOException来说,假如代码运行的机器断网了,那么网络IO就有可能出现异常,如果不处理那么我们的程序就崩了,可检查异常一般都是大概率出现的异常,因此需要提前处理。

Unchecked Exception类:
A:体系图中蓝色部分,主要包括Error和RuntimeException,编译器不会检查该类型异常,也检查不到,比如我们最讨厌的NullPointerException,编译器不会强制程序员处理这种异常。当然,程序员可以主动try catch捕获这种异常然后进行处理,但是假如你写了除0的代码,与其花时间和代码量去做捕获处理,还不如从根本上杜绝这个问题,RuntimeException很大程度上能反应一个程序员代码的健壮性。
B:既然无法捕获,这种异常有什么存在的必要呢?这就不得不说异常的另一个重要属性,那就是传递信息的属性,一个异常本身就是一种信息,它会告诉程序员哪一行代码抛出了异常,调用链是什么样子的,异常是什么原因导致的,常见的异常如下。

//这一行冒号左边标注了异常类型,冒号右边标注了异常原因
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 1
        //这一行是异常抛出位置,在代码的第15行,c方法里
    at com.huo.demos.test.Test.c(Test.java:15)
        //以下三行是方法异常传递路径,即方法调用路径
    at com.huo.demos.test.Test.b(Test.java:10)
    at com.huo.demos.test.Test.a(Test.java:6)
    at com.huo.demos.test.Test.main(Test.java:19)

2、自定义异常

2.1、什么时候需要自定义异常?
  • 情况一:系统定义的异常信息和不够充分。拿常见的NullPointerException来说,该提示只会给出哪里出现了空指针异常,而如果我们自定义类似的异常我们就可以给出是哪个变量因为哪种原因在哪里抛出了该异常。
  • 情况二:团队约定。比如团队开发一个API,要统一对外的异常展示。
  • 情况三:程序可能出现错误但无法预知谁会调用该程序。比如入参要求是电话号码,调用方可能会输入英文字符,这个时候我们有两种选择,一种是做判断之后返回一个error code,此时的方法返回值就会被限制为int类型,而且需要给调用方出示一个error code含义表;另一种方法就是抛出异常,我不用考虑调用者是谁,也不用限定返回值,调用方只要调用了我的代码编译器就会提示他必须对这种可能的错误进行预处理。


    image.png
2.2、如何自定义异常?
  • A:继承Exception:
    Exception没有强制要求子类实现其方法,但最好还是实现一下。当然如果只为给一个简单的提示或者根据异常名称就可以快速知道发生了什么,也可以不用定义这些。
public class EException extends Exception {
    public EException() {
        super();
    }
    public EException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }
    public EException(String message, Throwable cause) {
        super(message, cause);
    }
    public EException(String message) {
        super(message);
    }
}
  • B:继承RuntimeException
public class RException extends RuntimeException {
    public RException() {
        super();
    }
    public RException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }
    public RException(String message, Throwable cause) {
        super(message, cause);
    }
    public RException(String message) {
        super(message);
    }
    public RException(Throwable cause) {
        super(cause);
    }
}
  • C:两者区别:
    首先:继承Exception的异常调用方必须抛出或者try catch处理,这是编译器强制的,而继承RuntimeException的异常不会这样。


    继承Exception的异常

    继承RuntimeException的异常

然后:继承Exception的异常应该出现在这种情况里,即我的代码极有可能出现这种异常,因为我无法考虑到所有环境,因此我抛出给调用方根据自己的环境进行对应处理。继承RuntimeException的异常应该出现在此种环境里,即我的代码出现这种异常的可能性不是特别大,但是还是有出现的可能,而在出现这种异常的时候我能够给调用方以足够的提示信息告知他发生了什么。

3、异常捕获机制

3.1、catch捕获范围

从第一个catch开始到最后一个catch形成捕获队列,按照队列的顺序谁先捕获到就归谁处理。这个过程要考虑多态的问题,由于Exception是许多异常类的父类,因而如果处在队列前面,后面的队列的声明就没有意义了。

public static int bMethod() {
        try {
            return cMethod(true);
        } catch (EException e) {   //第一个捕获
            e.printStackTrace();
        } catch (Exception e) {  //第一个没捕获到,第二个来捕获
            e.printStackTrace();
        } finally {
            return 1;
        }
    }
3.2、finally的执行问题

finally的中文意思是最终,也就是段try catch finally代码的try catch该执行的都执行完之后最终要执行finally里的代码。简单起见可以把try catch finally看成一个代码块。
为了更好的解释finally运行机制,我们先了解一下虚拟机栈帧。

虚拟机栈帧

我们知道,每个线程都有一个自己的栈空间,实际上,当方法执行的时候JVM会把方法制作成栈帧压入到操作栈中,每个方法都对应着一个栈帧,当前执行的栈帧总是位于栈顶。
栈帧包含局部变量表、操作数栈、动态连接、方法返回地址。这里的方法返回地址实际上是在方法遇到ret指令之后返回值存放的位置的地址,也就是说返回值并没有直接返回给方法的调用者,而是中间又多了一步将返回值存放到栈的某一个位置。在真正把返回值交给上层调用者之前,如果遇到break、continue、return、抛出异常等情况JVM会清除栈中的返回值

  • 例子1,返回100
public static int method1() {
        int k = 0;
        try {
            k = 1;
            //返回的k=1首先存储到栈中的返回值位置,如果顺利完成就把该位置的值返回,如果遇到特殊指令如break、continue、return、抛异常就清除
            return k;                         
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //清除原来的返回值,把100放到返回值的位置上,如果顺利结束就返回,否则清除
            return 100;                  
        }
    }
  • 例子2,返回100
public static int method2() {
        int k = 1;
        while (true) {
            try {
                return k;
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                //遇到break,返回值位置的值被清除
                break;
            }
        }
        k=100;
        return k;
    }
  • 例子3,说明finally不是一定会执行
public static int method1() {
        int k = 1;
        //此处抛异常或者try和catch里调用system.exit()方法finally就不会执行。
        Integer.parseInt(null);
        try {
            return k;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            return k;
        }
    }

总结

其实日常开发我们遇到最多的就是NPE问题,NPE问题即是小问题又是破坏健壮性的大问题,我们应该更好的防范一下。我刷leetcode和进行一些日常开发总结了一些经验,供大家参考下:

  • 1、入参是对象的时候应该对入参进行判空操作
  • 2、调用的其他人写的方法返回的是对象的时候应该对该对象进行判空操作
  • 3、调用数据库或者远程调用的返回值应该进行判空操作
  • 4、保证调用者为已知对象。str1.equals(str2)方法,str1应该是已知对象,str2可以为空;String.valueOf()和Integer/Long/Double.toString()返回值相同使用前者
  • 5、自己写的方法尽量不要返回空值
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,193评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,306评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,130评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,110评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,118评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,085评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,007评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,844评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,283评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,508评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,667评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,395评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,985评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,630评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,797评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,653评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,553评论 2 352

推荐阅读更多精彩内容