原创作品,可以转载,但是请标注出处地址: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. iOS安全攻防之结构体保护使用

    Objective-C 代码很容易被 hook,因此需要对一些重要的业务逻辑进行保护,可以改用结构体的形式,把函数名隐藏在结构体里,以函数指针成员的形式存储.这样编译后只留了下地址,去掉了名字和参数表 ...

  2. hadoop 2.7.3 集群安装

    三台虚拟机,centos6.5 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 :: loca ...

  3. Error getting nested result map values for 'company'. Cause: java.sql.SQLException: Invalid value for getInt() - 'NFHK188'

    我今天遇到一个我不解的问题,是mybatis多对一关系查询出问题了,但是我自己还是解决了,在网上也查过那个错误,可是找不到我想要的.不知道你们遇到过没有,我接下来分享给大家.希望我这个第一篇博客能帮助 ...

  4. Zxing 的集成 ---- Maven 对应 Gradle 的写法

    Zxing 的集成 ---- Maven 对应 Gradle 的写法 刚刚想耍耍二维码,想到了zxing和zbar,又想到zxing是Google老爹的,想想就算了吧,虽然zbar快但是识别错误率也高 ...

  5. JVM-2.Class文件结构

    1.Class文件 (1)无关性:除了平台无关性,JVM还支持语言无关性:目前Clojure.Groovy.JRuby.Jyphon.Scala等语言可以在JVM上运行.实现语言无关性的原理仍然是字节 ...

  6. map,zip,reduce函数

    lt=range(5,10) lw=range(8,13) def mul(a,b): return a*b def mul_list(param1,param2): return_list=[] f ...

  7. 封装TableView有可能用到的数据结构和UITableViewCell的一个继承类

    最近4年的时间,我已经做了5个App完全独立开发, 工作经历5个App, 维护了两个App. 在这期间用的最多的是UITableView, 因此也有许多感觉可以封装的. 现在就是我封装的. RXCel ...

  8. dedecms 动态tab写法

    项目要求要dedecms动态添加选项卡然后自己写了一个 现在需要些tab的栏目下创建子栏目 (如果是首页需要顶级栏目) 如图我在案例下添加了3个子栏目 然后每个子栏目里面添加需要在tab里面输出的内容 ...

  9. Nginx——在Windows环境下安装

    下载 Nginx是开源软件,用户可以访问 http://nginx.org/ 网站获取源码包或Windows二进制文件下载.其中1.13.x版本为开发版本,1.12.0版本为稳定版本.开发版本分支会较 ...

  10. RSA加密通信小结(三)--生成加解密所需的SSL命令与流程

    在iOS中使用RSA加密解密,需要用到.der和.p12后缀格式的文件,其中.der格式的文件存放的是公钥(Public key)用于加密,.p12格式的文件存放的是私钥(Private key)用于 ...