今天继续讲解java中的异常处理机制,主要介绍Exception家族的主要成员,自定义异常,以及异常处理的正确姿势。

Exception家族

  一图胜千言,先来看一张图。

  Exception这是一个父类,它有两个儿子,IOException和RuntimeException,每个儿子都很能生,所以它有着一堆的孙子,但其实,Exception家族还有一个大家伙,那就是Throwable,这是一个接口,看名字就知道意思,就是“可被抛出”嘛,它还有一个同父异母的哥哥,那就是Error,这家伙可厉害了,Error类一般是指与虚拟机相关的问题,如系统崩溃,虚拟机错误,内存空间不足,方法调用栈溢出等。catch语句里,不仅可以catch住Exception,还能catch住Error(什么?你真的打算catch Error??程独秀同学,你先坐下。)一般情况下,是不能捕获Error的,对于这类错误,Java编译器不去检查他们。对于这类错误的导致的应用程序中断,仅靠程序本身无法恢复和预防,遇到这样的错误,建议让程序终止。除非你有把握能正确处理,否则程独秀同学还是坐下吧(滑稽)。

Unchecked Exception和Checked Exception

  你也许会一脸懵逼,???,这是啥?异常也是分派别的,Unchecked Exception表示“未检查异常“,Checked Exception自然就是”已检查异常“,派生于Error或者RuntimeException的异常称为unchecked异常,所有其他的异常成为checked异常。那问题来了,为啥要区分这两种异常?

  我们可以再看看上面那个图,可以看出,RuntimeException和Error都是由程序内部引发的错误,比如上一篇里所说的空指针和算术异常。而Checked Exception则大都是由外部因素导致的,如文件无法找到异常,这是虚拟机无法掌控的情况,当出现异常,虚拟机也只能一脸懵逼,不知道该如何是好,所以当有可能发生时,就必须要使用try..catch去捕获它,而对于Unchecked Exception 时,大部分是由于代码引发的,所以只要代码写的足够完善,是不会抛出这样的异常的,所以也不强制要求捕获。

  所以原因其实很简单,编译器将检查你是否为所有的已检查异常提供了异常处理机制,比如说我们使用Class.forName()来查找给定的字符串的class对象的时候,如果没有为这个方法提供异常处理,编译是无法通过的。已检查异常的意义就在于让你知道,这地方是有可能抛异常的,你要注意了,赶紧捕获了。

自定义异常

  那么如何自定义一个异常呢?其实很简单,只需要继承Exception类就好了。看下面的栗子:

public class MyException extends Exception {
public MyException() {
super();
} public MyException(String message) {
super(message);
} public MyException(String message, Throwable cause) {
super(message, cause);
} public MyException(Throwable cause) {
super(cause);
} protected MyException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}

  MyException继承了Exception类,重写了构造函数,并没有加自己的逻辑,只是调用了父类的方法。你看,自定义一个异常其实很简单吧。看到这你也许又疑惑了,这尼玛好像就是给Exception换了个名字,有啥用???

  别急,别急,你忘了吗,Exception不仅是可以捕获的,还是可以主动抛出的,所以当遇到某些特定的情况时,我们就可以主动抛出异常,然后在调用时去捕获它,获取异常信息,如果直接用Exception的话,那么捕获的时候,会把所有的异常,该捕获不该捕获的都一起捕获了,那么就没法区分哪些是我们主动抛出来的异常了,这样就无法对那些异常进行特殊处理了。

异常处理的正确姿势  

  接下来要简单介绍一个实际使用中常用的异常处理方法——异常链化处理。

  在一些大型的,模块化的软件开发中,一旦一个地方发生异常,则如骨牌效应一样,将导致出现一连串的异常。假设B模块需要调用A模块的方法,如果A模块发生异常,则B也将不能完成而发生异常,但是B在抛出异常时,会将A的异常信息掩盖掉,这将使得异常的根源信息丢失。而使用异常的链化可以将多个模块的异常串联起来,使得异常信息不会丢失。

  异常链化就是用一个异常对象为参数构造新的异常对象。新的异对象将包含先前异常的信息。这项技术主要是异常类的一个带Throwable参数的函数来实现的。这个当做参数的异常,我们叫他根源异常(cause)。如果你细心一点的话,会发现上面的栗子里也有一个叫做cause的东西,没错,说的其实就是它,在new一个新的异常时,将之前的异常信息传入构造函数即可。下面再用一个简单的栗子进行说明:

public class Test {
public static void main(String[] args) {
System.out.println("请输入2个加数");
int result;
try {
result = add();
System.out.println("结果:"+result);
} catch (Exception e){
e.printStackTrace();
}
} /**
* 执行加法计算
*/
private static int add() throws Exception {
int result;
try {
List<Integer> nums =getInputNumbers();
result = nums.get(0) + nums.get(1);
}catch(InputMismatchException immExp){
//链化:以一个异常对象为参数构造新的异常对象。
throw new Exception("计算失败",immExp);
}
return result;
} /**
* 获取输入的整数
*/
private static List<Integer> getInputNumbers() {
List<Integer> nums = new ArrayList<>();
Scanner scan = new Scanner(System.in);
try {
int num1 = scan.nextInt();
int num2 = scan.nextInt();
nums.add(new Integer(num1));
nums.add(new Integer(num2));
}catch(InputMismatchException immExp){
throw immExp;
}finally {
scan.close();
}
return nums;
}
}

  输出如下:

请输入2个加数
d d
java.lang.Exception: 计算失败
at com.frank.chapter17.Test.add(Test.java:35)
at com.frank.chapter17.Test.main(Test.java:18)
Caused by: java.util.InputMismatchException
at java.util.Scanner.throwFor(Scanner.java:864)
at java.util.Scanner.next(Scanner.java:1485)
at java.util.Scanner.nextInt(Scanner.java:2117)
at java.util.Scanner.nextInt(Scanner.java:2076)
at com.frank.chapter17.Test.getInputNumbers(Test.java:47)
at com.frank.chapter17.Test.add(Test.java:31)
... 1 more

  可以看到,当输入的不是整数时,发生了异常,在getInputNumbers方法里没有处理这个异常,而是将它继续抛出,在add方法里捕获了异常之后,以该异常为构造参数,重新抛出了一个异常,从打印输出的信息可以看到,不仅仅有第二次抛出的异常信息,第一次的输出信息不匹配异常的详细信息也包含在了里面,衔接在Caused by之后,形成了一条异常链,这样可以方便我们更快的排查问题所在。

  至此,异常就讲解完毕了,希望能给大家带来一些启发和思考,如果觉得还算ok的话,记得动动小手点推荐,让更多人可以看到,也欢迎关注我的博客,会持续更新的。如果有什么讲的不好的地方。。。emmmmmm,你倒是来打我呀(逃)

【Java入门提高篇】Day16 Java异常处理(下)的更多相关文章

  1. 【Java入门提高篇】Java集合类详解(一)

    今天来看看Java里的一个大家伙,那就是集合. 集合嘛,就跟它的名字那样,是一群人多势众的家伙,如果你学过高数,没错,就跟里面说的集合是一个概念,就是一堆对象的集合体.集合就是用来存放和管理其他类对象 ...

  2. 【Java入门提高篇】Day21 Java容器类详解(四)ArrayList源码分析

    今天要介绍的是List接口中最常用的实现类——ArrayList,本篇的源码分析基于JDK8,如果有不一致的地方,可先切换到JDK8后再进行操作. 本篇的内容主要包括这几块: 1.源码结构介绍 2.源 ...

  3. 【Java入门提高篇】Day1 抽象类

    基础部分内容差不多讲解完了,今天开始进入Java提高篇部分,这部分内容会比之前的内容复杂很多,希望大家做好心理准备,看不懂的部分可以多看两遍,仍不理解的部分那一定是我讲的不够生动,记得留言提醒我. 好 ...

  4. 【Java入门提高篇】Day13 Java中的反射机制

    前一段时间一直忙,所以没什么时间写博客,拖了这么久,也该更新更新了.最近看到各种知识付费的推出,感觉是好事,也是坏事,好事是对知识沉淀的认可与推动,坏事是感觉很多人忙于把自己的知识变现,相对的在沉淀上 ...

  5. 【Java入门提高篇】Day28 Java容器类详解(十)LinkedHashMap详解

    今天来介绍一下容器类中的另一个哈希表———>LinkedHashMap.这是HashMap的关门弟子,直接继承了HashMap的衣钵,所以拥有HashMap的全部特性,并青出于蓝而胜于蓝,有着一 ...

  6. 【Java入门提高篇】Day27 Java容器类详解(九)LinkedList详解

    这次介绍一下List接口的另一个践行者——LinkedList,这是一位集诸多技能于一身的List接口践行者,可谓十八般武艺,样样精通,栈.队列.双端队列.链表.双向链表都可以用它来模拟,话不多说,赶 ...

  7. 【Java入门提高篇】Day26 Java容器类详解(八)HashSet源码分析

    前面花了好几篇的篇幅把HashMap里里外外说了个遍,大家可能对于源码分析篇已经讳莫如深了.别慌别慌,这一篇来说说集合框架里最偷懒的一个家伙——HashSet,为什么说它是最偷懒的呢,先留个悬念,看完 ...

  8. 【Java入门提高篇】Day20 Java容器类详解(三)List接口

    今天要说的是Collection族长下的三名大将之一,List,Set,Queue中的List,它们都继承自Collection接口,所以Collection接口的所有操作,它们自然也是有的. Lis ...

  9. 【Java入门提高篇】Day31 Java容器类详解(十三)TreeSet详解

    上一篇很水的介绍完了TreeMap,这一篇来看看更水的TreeSet. 本文将从以下几个角度进行展开: 1.TreeSet简介和使用栗子 2.TreeSet源码分析 本篇大约需食用10分钟,各位看官请 ...

随机推荐

  1. rhel6.4 安装 mysql-5.6

    rhel6.4 安装 mysql-5.6 下载(临时地址, 如不可用,请到oracle官网下载) 采用rpm安装. mysql服务端要安装: ftp://pepstack.com/pub/rpm/My ...

  2. Dynamics CRM2013 Lookup Filtering using addCustomFilter

    dynamics crm中对lookup视图的过滤是一个很平常性的需求,在2011的时候都是用添加自定义视图的方式例如下面这段示例代码 <span style="font-size: ...

  3. (NO.00001)iOS游戏SpeedBoy Lite成形记(四)

    下面我们来实现选手从起点开始移动到终点的代码. 首先在GameScene.h接口中添加matchRun方法: #import "CCNode.h" @interface GameS ...

  4. 干掉头疼的finished with non-zero exit value 2

    很多次会出现 finished with non-zero exit value 2  . Error:Execution failed for task ':app:dexDebug'. > ...

  5. markdown简易快速的编辑格式(易读易写)

    实现简单快速书写,格式指定简便.易读易写 讲解http://wowubuntu.com/markdown/ 简单使用的讲解:http://www.ituring.com.cn/article/23 代 ...

  6. Dynamics CRM 修改自定义实体名字及属性前缀(架构名称)

    本文章以2013为例(2011同理,4.0比较简单直接在系统设置的自定义中设置即可). 1.设置-->自定义项-->发布者,下图中的红框内的即为需要设置的前缀. 2.设置-->解决方 ...

  7. 【Android 应用开发】Android屏幕适配解析 - 详解像素,设备独立像素,归一化密度,精确密度及各种资源对应的尺寸密度分辨率适配问题

    . 作者 :万境绝尘 转载请注明出处 : http://blog.csdn.net/shulianghan/article/details/19698511 . 最近遇到了一系列的屏幕适配问题, 以及 ...

  8. LeetCode之“树”:Balanced Binary Tree

    题目链接 题目要求: Given a binary tree, determine if it is height-balanced. For this problem, a height-balan ...

  9. SpriteBuilder中的CCSprite9Slice是个什么鬼?

    CCSprite大家都知道,但是加上后面那一串又变成了神马呢? 我们可以首先到官方的API文档网站查一下,如下: http://www.cocos2d-swift.org/docs/api/Class ...

  10. OpenShift实战(七):OpenShift定制镜像S2I

    1.基础镜像制作 由于公司的程序是Java开发,上线发布使用的是maven,如果使用openshift自带的S2I,每次都会全量拉取代码(代码比较多,每次全量拉太慢),然后每次打包都会再一次下载mav ...