《Java异常深度剖析:体系、自定义与处理之道》
第一部分 认识异常
1.1 什么是异常?
在学习异常之前我们先来看看异常到底是什么?
异常:是代码在编译或执行过程中可能出现的错误。
比如在程序中出现了问题:
int[] arr = {1, 2, 3, 4, 5};
System.out.println(arr[5]);
在上述的代码中arr数组arr[5]会造成数组访问越界。这就是很常见的异常
还比如:
System.out.println(10/0);
分母不能为零,也是一种异常
还有一些比如:“读取的文件不在了啊”“读取网络数据断网了”.....等
这些都是异常~
1.2 Java的异常体系
Java.lang.Throwable: 为所有异常最顶级的父类(java中所有的问题)
Error: 一般都是系统中的问题,我们不用太管它,也解决不了
Exception: 又分出来了两类,一个叫运行时的异常RuntimeException(即编译时不会提示错误,当程序跑起来的时候才会报错,比如前面举的例子 数组访问越界);另一个叫编译时的异常其他异常(即编译时就会报错)
1.2.1 运行时的异常
下面我们举实例进行讲解:
上面是很典型的运行时异常(数组访问越界),上面我们讲到它是继承于RuntimeException类的,我们可以查看源码来验证:(按住ctrl点击此处)
ArrayIndexOutOfBoundsException:数组索引越界异常
我们可以看到它又继承于IndexOutOfBoundsException:索引越界异常
继续查看下一层源码
这里就可以看到的确是继承于RuntimeException类
这个数字操作异常也是属于运行时异常:
同样也是继承于RuntimeException类
还有一个经典的就是空指针异常:
同样时继承于RuntimeException类
其实大多数运行时的异常是你自己的编码能力不行~哈哈
1.2.2 编译时的异常
编译时的异常作用就是提醒程序猿这里的代码很容易出错,要注意!
比如在上述代码中就是在提醒程序员str字符串中的日期格式与解析日期对象中的格式可能不一样,就会出现解析不了的情况。
而编译时的异常是别人担心你的水平不行~提醒你的代码可能会写错哦
1.2.3 异常的基本处理
第一种:可以大家全都抛(throw)出去,都不管它
show2()方法先把异常抛出去,show2()中就不会报错了,
接着main方法中的show2()语句就会报错,于是再alt+回车 把异常再抛出去,整个程序就不会报错了(在编译时!!)
如果本身没有问题的话,就正常运行:
但如果确实有问题,在运行时还是会报错 如下:
第二种:通过try/catch 去监控、拦截异常
可以在原处直接try/catch, 也可以先从原处抛出,在main中的调用语句处进行try/catch
或
在后面我们直接抛Exception,catch也是拦截Exception
小总结:
1.3 异常的作用
作用1:用来定位程序bug的关键信息
作用2:可以用来作为方法内部的一种特殊返回值,以便通知上层调用者,方法的执行问题
这里我们讲一下作用2:
这里如果分母是0的话,就会出现异常;而在原来我们会在方法中加上一个if的判断语句if(b == 0){ sout("分母不能为0,输入有误~");return ?}
问题来了这里我们是return啥回去嘞?返回值是整数,返回-1?(原来我们确实是这样的,但是没有实际的业务作用,而且有可能是a=1 b=-1,确实结果是-1,所以我们不能这样,上层不知道到底是什么情况)
所以我们要:返回一个异常对象给上层调用者,返回的异常还能告知上层 底层是执行成功了还是失败了!(下面这个代码截图很干!仔细理理!)
就是如果b == 0,就抛出这个异常:
然后在main方法中进行监控,如果没有问题便输出结果+底层方法成功执行,但如果有问题就会被catch给拦截,转换成异常处理对象Exception e,并打印异常信息+底层方法执行失败;
总结:在今后如果一个方法中的某些情况不能正常的返回,就可以使用异常处理进行返回给上级(替代return),上级一旦监控到异常就知道失败了并且可以返回异常信息,若没监控到异常就知道底层执行成功啦,以便知道接下来怎么做
第二部分 自定义异常
● Java无法为这个世界上的全部问题都提供异常类来代表, 如果企业自己的某种问题(比如:不是VIP用户),想通过异常来表示, 以便用异常来管理该问题, 那就需要自己来定义异常类了。
而我们自定义异常也分为两类,自定义运行时异常、自定义编译时异常;
自定义运行时异常:
● 定义一个异常类继承RuntimeException. ● 重写构造器。 ● 通过throw new 异常类(xxx)来创建异常对象并抛出。 特点:编译阶段不报错,运行时才可能出现!提 醒不属于激进型。
自定义编译时异常:
● 定义一个异常类继承Exception. ● 重写构造器。 ●通过throw new 异常类(xxx)创建异常对象并抛出。 特点:编译阶段就报错,提醒比较激进
总结:如果你想强烈提醒别人小心出错就自定义编译时异常,反之用自定义运行时异常,但是现在Java的规范是禁止使用编译时异常,运行时异常用的更多!!!
第三部分 异常的处理方案
第一种:
底层有两个异常直接往上抛(这里直接抛Exception,因为如果有上百个异常,难道我一个个抛出去?当然不)然后再main中进行监视 拦截 打印出异常
第二种:
如下定义一个方法用来让用户输入商品的定价(Double类型)
但就可能有人会瞎输:比如asdada...乱七八糟的
这样程序就会把这个方法中的异常往上面抛出去,外面的再抛给虚拟机,程序就直接死了、断了。这就没有满足程序的健壮性(怎么都搞不死~)
于是我们就在main中调这个方法的语句处进行try/catch包裹
包裹:ctrl+alt+T
除此之外,还要在这团代码外面再包裹一个while死循环,用户成功设置处设置一个出口break;
这下你的程序就巨牛逼 咋也死不了~,直到输入正确(诶~这就很有意义了)
所以今后你调某个功能一旦出了问题,你就try/catch,管他出什么问题就try/catch
小结: 在开发中异常的常见处理方式是:底层的异常抛出去给最外层,最外层集中捕获处理。还有就是在开发中其实也不是每次每调一个方法都要去往外抛、try/catch,只是理论上最完美的是每次写方法都应该把异常抛出去到最外层集中捕获处理,但如果你写的程序,觉得没啥问题,不抛不try也可以,只要程序没问题 都可以~
异常就到此结束啦~byebye
“只要一直在跑,就肯定错不了”peace~
转载自:https://juejin.cn/post/7398177677670760474