原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/7191280.html

1、概述

  Java代码中的异常处理是非常重要的一环,从代码中可以看到,它的使用已经和业务逻辑紧密的结合在一起,部分业务逻辑还是依靠异常来完成的,更多的时候进行异常处理可以完善逻辑,避免可能的出错,规避小错误引发的大停顿。

  在一般的项目之中,都会自定义运行时异常,用以适应项目的需要,这种异常可被捕捉,也可不被捕捉,它们不会导致整个系统挂掉,但是很多情况下,不捕捉处理就会导致业务出错。

  在这里我们模拟几种情况,点明异常捕捉的使用时机。

2、情况分析

  先来看没有任何处理的代码

 public class ExceptionTests01 {

     public static void main(String[] args) {
System.out.println("---1---");
invoke();
System.out.println("---2---"); } public static void invoke(){
System.out.println("---11---");
int i = 1/0;
System.out.println("---12---");
}
}

  其执行结果如下:

---1---
---11---
Exception in thread "main" java.lang.ArithmeticException: / by zero
at com.donghao.test1.ExceptionTests01.invoke(ExceptionTests01.java:14)
at com.donghao.test1.ExceptionTests01.main(ExceptionTests01.java:7)

  解析:main方法调用invoke方法,在执行到第12行时出错,产生算法异常,此时由于无任何异常处理手段,结果就是,程序执行到这里之后直接中断,执行结果中输出的异常堆栈信息是Java内部默认的异常处理机制处理的结果。

  改造一:我们在invoke方法内部加上异常捕捉机制,代码如下:

 public class ExceptionTests01 {

     public static void main(String[] args) {
System.out.println("---1---");
invoke();
System.out.println("---2---"); } public static void invoke(){
try{
System.out.println("---11---");
int i = 1/0;
}catch(Exception e){
System.out.println("---12---");
}
System.out.println("---13---");
}
}

执行结果:

---1---
---11---
---12---
---13---
---2---

  结果解析:我们在invoke方法的执行代码外围添加异常捕捉代码,捕捉Exception异常,这是所有异常的基类,当然也包含这里的算法异常,那么这个捕捉机制就会将1/0产生的异常捕捉到,捕捉到这个异常之后,就会跳转到catch语句块中执行针对这个异常的处理语句,执行完成后,会继续执行try...catch语句块之后的代码,这样的好处显而易见,一处的小错误并不会阻挡整个代码的持续执行,当然如果是严重问题,我们确实需要暂停执行的,就不能使用这种情况,使用之前的代码就行,所以异常处理机制的执行时机完全是由项目的业务情况而定的,是非常灵活的,不是固定的死板的。我们要根据实际的业务场景来合理的使用才是正理。

  改造二:我们在main方法中也添加异常捕捉

 public class ExceptionTests01 {

     public static void main(String[] args) {
try{
System.out.println("---1---");
invoke();
}catch(Exception e){
System.out.println("---2---");
}
System.out.println("---3---");
} public static void invoke(){
try{
System.out.println("---11---");
int i = 1/0;
}catch(Exception e){
System.out.println("---12---");
}
System.out.println("---13---");
}
}

其执行结果如下:

---1---
---11---
---12---
---13---
---3---

  结果几乎与之前的完全一致,不同之处在于2没有输出,一是我改变了2的输出位置,并新增了3的输出,现在3相当于之前2的位置,2没有输出的原因是因为任何一个异常只能被捕捉一次,一旦被捕捉处理,那么之后就不会再次被捕捉,即使我在main方法中将异常类型改成算法异常,也不会捕捉到,异常只会被距离它最近的包含该异常的异常捕捉到,这里的两个异常捕捉其实就是一个嵌套的异常捕捉,而且二者捕捉的异常还是一致的,一般情况我们是不会这么使用的,因为毫无意义。但不是说它就完全不会出现,可能invoke中的代码较长,会有多处异常情况出现,我们可以在main方法中统一捕捉,而invoke中的异常捕捉只针对单一异常,表示这个异常的出现不会影响invoke方法后面的代码执行,没有异常捕捉的代码一旦出现异常就会中断其后方所有代码的执行(同一代码块内),这个异常会被main方法中的异常捕捉机制捕捉到并执行处理,这样main方法中调用invoke之后的代码仍然可以执行,不会被调用发生异常而中断。

  但是如果我们再将invoke方法中的异常捕捉改变如下:

 public class ExceptionTests01 {

     public static void main(String[] args) {
try{
System.out.println("---1---");
invoke();
}catch(Exception e){
System.out.println("---2---");
}
System.out.println("---3---");
} public static void invoke(){
try{
System.out.println("---11---");
int i = 1/0;
}catch(NullPointerException e){
System.out.println("---12---");
}
System.out.println("---13---");
}
}

  执行结果发生了变化:

---1---
---11---
---2---
---3---

  为什么呢?正是因为我们更改了invoke方法中捕捉的异常类型,之前是异常基类型Exception,现在改成具体的空指针异常,那么这个异常捕捉就只能捕捉空指针异常,它对此处发生的算法异常就会视而不见(由于异常类型的不对口,那么这个异常捕捉相当于没有添加,可以想象成没有异常捕捉的情况),这样就导致invoke方法中在1/0发生异常之后的所有代码全部不会执行,而我们在main方法中新增的异常捕获却能捕获到这种算法异常,所以12和13都不会输出,而是在异常发生后直接就跳转到main方法中进行异常捕捉,执行catch语句块处理语句输出2,然后是3。

  在看一个特殊的情况:

 public class ExceptionTests01 {

     public static void main(String[] args) {
System.out.println("---1---");
invoke();
System.out.println("---2---");
} public static void invoke(){
for(int i = -2;i < 3;i++){
System.out.println("---11---");
System.out.println("12/"+i+"="+12/i);
System.out.println("---12---");
}
System.out.println("---13---");
}
}

  invoke方法中是一个循环输出,当第12行发生异常时,循环中断,默认的异常处理机制打印异常堆栈:

---1---
---11---
12/-2=-6
---12---
---11---
12/-1=-12
---12---
---11---
Exception in thread "main" java.lang.ArithmeticException: / by zero
at com.donghao.test1.ExceptionTests01.invoke(ExceptionTests01.java:14)
at com.donghao.test1.ExceptionTests01.main(ExceptionTests01.java:7)

改造一:在invoke方法的for循环外部添加try...catch:

 public class ExceptionTests01 {

     public static void main(String[] args) {
System.out.println("---1---");
invoke();
System.out.println("---2---");
} public static void invoke(){
try{
for(int i = -2;i < 3;i++){
System.out.println("---11---");
System.out.println("12/"+i+"="+12/i);
System.out.println("---12---");
}
System.out.println("---13---");
}catch(Exception e){
System.out.println("---14---");
}
System.out.println("---15---");
}
}

结果:

---1---
---11---
12/-2=-6
---12---
---11---
12/-1=-12
---12---
---11---
---14---
---15---
---2---

  查看结果,发现循环还是中断了,当i=0时,第13行产生异常,之后循环中断,然后异常才会被for循环之外的异常捕捉到,这种场景也会在实际项目中出现,但不多见,具体场景为,针对循环进行异常捕捉,一旦循环中某一环产生异常,则整个循环终止,处理异常。

  改造二:在循环体中加入try...catch块

 public class ExceptionTests01 {

     public static void main(String[] args) {
System.out.println("---1---");
invoke();
System.out.println("---2---");
} public static void invoke(){
for(int i = -2;i < 3;i++){
try{
System.out.println("---11---");
System.out.println("12/"+i+"="+12/i);
System.out.println("---12---");
}catch(Exception e){
System.out.println("---13---");
}
System.out.println("---14---");
}
System.out.println("---15---");
}
}

执行结果:

---1---
---11---
12/-2=-6
---12---
---14---
---11---
12/-1=-12
---12---
---14---
---11---
---13---
---14---
---11---
12/1=12
---12---
---14---
---11---
12/2=6
---12---
---14---
---15---
---2---

  这种情况比较多见,我们将异常捕捉内置到for循环内部,只针对循环体进行异常捕捉,这样当某一次循环体执行时产生了异常,也能私下处理好,不会影响整个循环的继续执行。在循环中还可以结合continue和break关键字进行更加复杂的关系控制,来达到特定的业务需求。

  这里我来展示一种情况,也是刚刚做过的一个业务场景:

 public class ExceptionTests01 {

     public static void main(String[] args) {
System.out.println("---1---");
invoke();
System.out.println("---2---");
} public static void invoke(){
for(int i = -2;i < 3;i++){
try{
System.out.println("---11---");
System.out.println("12/"+i+"="+12/i);
System.out.println("---12---");
}catch(Exception e){
System.out.println("---13---");
continue;
}
System.out.println("---14---");
}
System.out.println("---15---");
}
}

  你没看错,只是添加了一个continue;控制信息的展示,当发生异常之后,执行catch块代码,输出13后,不再执行输出14的操作,而是直接开始新的循环。

  有必要做个总结:

    1-异常的捕捉是有方向性和类型针对性的,异常会被距离异常发生点最近的包含或者就是捕捉该类型异常的捕捉点捕捉到。这样我们在做嵌套异常捕捉和多异常捕捉时,就一定要注意要将小范围的异常类型放置到靠进try块的位置,避免大类型劫持异常,导致你设置的异常类型无法生效。

    2-我们将一段代码try...catch包裹,就可以将这段代码从这个方法体中隔离出来,将其影响度降到最低,即使其发生异常,也不会影响到后续代码的执行。

    3-throw关键字的配合使用,我们可以在catch块中使用,表示将捕捉到的异常再次抛出,这里不做处理,这样就必须在方法的调用处再次进行捕捉,可以持续抛出,但是直到最终的方法时,必须要进行处理(对应明确抛出的异常一定要进行捕捉处理,不论你抛几次)。

3、项目的异常处理

异常的转换

  项目中我们都会自定义异常,这些异常一般带有较为明确的目的,甚至我们可能会在项目中的每一层级定义不同的异常,这时候就会涉及到异常的转换,其实转换很简单,只要将异常进行捕捉,在catch块中将一行捕捉住,并重新抛出(throw)一个新的异常,将之前的异常信息e作为新异常的参数。

    try{
int i = 1/0;
}catch(Exception e){
throw new RuntimeException(e);
}

  如上面的例子中,第2行会抛出一个异常,该异常将会被catch块捕捉到,然后内部消化,重新抛出一个RuntimeException,并将原来的异常信息作为新异常的异常信息(即保留原异常信息)。

java基础系列--Exception异常处理的更多相关文章

  1. Java基础(七)--Exception异常处理

    发现错误的理想时机是程序运行之前(编译期),然后不太现实,很多异常无法被发现(特别是业务上的数据),需要在运行时解决. 错误恢复机制保证代码健壮性的方式,异常处理在程序中很常见,也是必须的,必须考虑有 ...

  2. 夯实Java基础系列10:深入理解Java中的异常体系

    目录 为什么要使用异常 异常基本定义 异常体系 初识异常 异常和错误 异常的处理方式 "不负责任"的throws 纠结的finally throw : JRE也使用的关键字 异常调 ...

  3. 夯实Java基础系列11:深入理解Java中的回调机制

    目录 模块间的调用 多线程中的"回调" Java回调机制实战 实例一 : 同步调用 实例二:由浅入深 实例三:Tom做题 参考文章 微信公众号 Java技术江湖 个人公众号:黄小斜 ...

  4. 夯实Java基础系列15:Java注解简介和最佳实践

    Java注解简介 注解如同标签 Java 注解概述 什么是注解? 注解的用处 注解的原理 元注解 JDK里的注解 注解处理器实战 不同类型的注解 类注解 方法注解 参数注解 变量注解 Java注解相关 ...

  5. 2015年12月28日 Java基础系列(六)流

    2015年12月28日 Java基础系列(六)流2015年12月28日 Java基础系列(六)流2015年12月28日 Java基础系列(六)流

  6. Java基础系列--static关键字

    原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/8477914.html 一.概述 static关键字是Java诸多关键字中较常使用的一个,从 ...

  7. Java基础系列-ArrayList

    原创文章,转载请标注出处:<Java基础系列-ArrayList> 一.概述 ArrayList底层使用的是数组.是List的可变数组实现,这里的可变是针对List而言,而不是底层数组. ...

  8. Java基础系列-Collector和Collectors

    原创作品,可以转载,但是请标注出处地址:https://www.cnblogs.com/V1haoge/p/10748925.html 一.概述 Collector是专门用来作为Stream的coll ...

  9. Java基础系列-二进制操作

    原创文章,转载请标注出处:<Java基础系列-二进制操作> 概述 Java源码中涉及到大量的二进制操作,非常的复杂,但非常的快速. Java二进制表示法 首先了解下二进制,二进制是相对十进 ...

随机推荐

  1. UX2内核浏览加速技术纲要

    UX2内核是本人负责主要开发的浏览服务项目,其主要目的是为开发者提供一个简单好用.轻便的网络浏览服务.UX2内核的安卓端是基于WebView进行深度优化的,同时欢迎大家使用这个内核用于app页面或浏览 ...

  2. Configure Always On Availability Group for SQL Server on RHEL——Red Hat Enterprise Linux上配置SQL Server Always On Availability Group

    下面简单介绍一下如何在Red Hat Enterprise Linux上一步一步创建一个SQL Server AG(Always On Availability Group),以及配置过程中遇到的坑的 ...

  3. Java对【JSON数据的解析】--Gson解析法

    Gson和fastjson分别为谷歌和阿里巴巴对JSON数据进行处理封装的jar包 两者异同点: 相同点:都是根据JSON数据创建相应的类 不同点: 1.调用方式区别 谷歌:方法都是非静态的,需要先创 ...

  4. 天气正好,hello world!

    两个多月,稀里糊涂的回来了,内心很迷茫,回来一周了,明知道还需要有一大堆东西需要去学,但是却找不到之前学习的状态,在寝室,在实验室,看着自己一向不喜欢的电视剧,无目的的逛着淘宝,刷着头条和知乎,就这么 ...

  5. uibutton颜色设置

     UIColor *color = [UIColor colorWithRed:100 / 255.0 green:20 / 255.0 blue:50 / 255.0 alpha:1.0]; 

  6. sql 经典面试题

     如果数据库里两个日期型字段d1,d2,怎样用sql语句列出按月的所有区间,比如表结构如下localid    d1          d21         2014-1-15    2014-3- ...

  7. charles抓包,打断点,连接手机抓包

    写给我自己: 如果是使用charles抓包.一定要tm的保证手机和电脑连的是一个网. charles抓本地包的操作 1.打开charles, 2.打开浏览器访问某网页,就可抓到对应的包 charles ...

  8. 安装npm及cnpm(Windows)

    [工具官网] Node.js : http://nodejs.cn/ 淘宝NPM: https://npm.taobao.org/ [安装步骤] 一.安装node.js 1.前往node.js官网下载 ...

  9. C# 计时器写法

        刚才一个交流群里有人问计时器怎么写,正好我也不太熟,就写了个demo,和大家分享一下这个是参考师傅的写的! 计时器有好多种写法,这里给大家推荐一个性能比较好的,用dispatchertimer ...

  10. 一步一步教你将开源项目上传到jcenter

    最近闲着看了一下jcenter的使用,也想将自己使用频率比较高的东西抽成类库,然后通过compile来使用,提高开发效率,本来以为挺简单的,但是网上介绍的比较模糊,很多博客也比较老了,不适用,花了我一 ...