1.捕获异常:

在Java中,凡是可能抛出异常的语句,都可以用try...catch捕获。把可能发生异常的语句放在try{...}中,然后使用catch捕获对应的Exception及其子类。

1.多catch语句

可以使用多个catch语句,每个catch分别捕获对应的Exception及其子类。JVM在捕获到异常后,会从上到下匹配catch语句,匹配到某个catch后,执行catch代码块,然后不再继续匹配。简单地说就是多个catch语句只有一个能被执行。例如:

public static void main(String[] args) {try {process1();process2();process3();} catch (IOException e) {System.out.println(e);} catch (NumberFormatException e) {System.out.println(e);}
}

存在多个catch的时候,catch的顺序非常重要,子类必须写在前面,例如:

public static void main(String[] args) {try {process1();process2();process3();} catch (IOException e) {System.out.println("IO error");} catch (UnsupportedEncodingException e) { // 永远捕获不到System.out.println("Bad encoding");}
}

对于上面的代码,UnsupportedEncodingException异常是永远捕获不到的,因为它是IOException的子类,当抛出UnsupportedEncodingException异常时,会被catch(IOException e){...}捕获并执行。因此,正确的做法应该是把子类一场放到前面:

public static void main(String[] args) {try {process1();process2();process3();} catch (UnsupportedEncodingException e) {System.out.println("Bad encoding");} catch (IOException e) {System.out.println("IO error");}
}

2.finally语句

无论是否有异常发生,我们都希望执行一些语句,例如清理工作,可以把执行语句写若干遍:正常执行的放在try中,每个catch再写一遍,例如:

public static void main(String[] args) {try {process1();process2();process3();System.out.println("中国抗击疫情必将胜利!!!");    //这行代码是我们想必须执行的} catch (UnsupportedEncodingException e) {System.out.println("Bad encoding");System.out.println("END");} catch (IOException e) {System.out.println("IO error");System.out.println("END");}
}

但是这行代码是我们想必须执行的System.out.println("中国抗击疫情必将胜利!!!");  但是如果抛出前面调用方法如果产生异常的话,就不会执行这一行代码了,那我们可以利用finally语句块保证有无错误都会执行,可改写上述代码为:

public static void main(String[] args) {try {process1();process2();process3();} catch (UnsupportedEncodingException e) {System.out.println("Bad encoding");} catch (IOException e) {System.out.println("IO error");} finally {System.out.println("中国抗击疫情必将胜利!!!");    //这行代码是我们想必须执行的}
}

注意finally的几个特点:

1.finally语句不是必须的,可以写可以不写;

2.finally总是最后执行的;

如果没有没有发生异常,就正常执行try{...}语句块,然后执行finally。如果发生了异常,就中断执行try{...}语句块,然后跳转执行匹配的catch语句块,最后执行finally。可见finally是用来保证一些代码必须执行的。

某些情况下,我们也可以没有catch,只是用try...finally结构的,例如:

void process(String file) throws IOException {try {...} finally {System.out.println("中国抗击疫情必将胜利!!!");    //这行代码是我们想必须执行的}
}

3.捕获多种异常:

如果某些异常的处理逻辑相同,但是异常本身之前又不存在继承关系,那么就需要编写多条catch语句,例如:

public static void main(String[] args) {try {process1();process2();process3();} catch (IOException e) {System.out.println("Bad input");} catch (NumberFormatException e) {System.out.println("Bad input");} catch (Exception e) {System.out.println("Unknown error");}
}

因为处理IOException和NumberFormatException的代码时相同的,所以我们可以把她它两用 | 合并到一起,例如:

public static void main(String[] args) {try {process1();process2();process3();} catch (IOException | NumberFormatException e) { // IOException或NumberFormatExceptionSystem.out.println("Bad input");} catch (Exception e) {System.out.println("Unknown error");}
}

                使用try...catch...finally捕获异常小结:

                        1.多个catch语句的匹配顺序非常重要,子类必须放在前面。

                        2.finally语句保证了有无异常都会执行,它时可选的。

                        3.一个catch语句也可以匹配多个非继承关系的异常。

2.抛出异常:

1.异常的传播路线:

当某个方法抛出了异常时,如果当前方法没有捕获异常,异常就会被抛到上层调用方法,直到遇到某个try...catch被捕获为止:

public class Main {public static void main(String[] args) {try {process1();} catch (Exception e) {e.printStackTrace();}}static void process1() {process2();}static void process2() {Integer.parseInt(null); // 会抛出NumberFormatException}
}

通过printStackTrace()可以打印出方法的调用栈,例如:

java.lang.NumberFormatException: nullat java.base/java.lang.Integer.parseInt(Integer.java:614)at java.base/java.lang.Integer.parseInt(Integer.java:770)at Main.process2(Main.java:16)at Main.process1(Main.java:12)at Main.main(Main.java:5)

printStackTrace()对于调试错误非常有用,上述信息表示:NumberFormatException是在java.lang.Integer.parseInt方法中被抛出的,从下往上看,调用层依次是:

1.main()调用process1();

2.process1()调用process2();

3.process2()调用Integer.parseInt(String);

4.Integer.parseInt(String)调用Integer.parseInt(String,int);

查看Integer.java源码可知,抛出异常方法的代码如下:

public static int parseInt(String s, int radix) throws NumberFormatException {if (s == null) {throw new NumberFormatException("null");}...
}

2.抛出异常

当发生错误时,比如用户输入了 非法的字符,我们就可以抛出异常,如何抛出异常呢?参考上面Integer.parseInt()方法,抛出异常分两步:

1.创建某个Exception的实例;

2.使用throw语句抛出;

例如:

void process2(String s) {if (s==null) {NullPointerException e = new NullPointerException();throw e;//throw new NullPointerException();}
}

实际上绝大部分抛出异常的代码会合成一行如上述注释掉的那样或者下述这样:

void process2(String s) {if (s==null) {throw new NullPointerException();}
}

如果一个方法捕获了某个异常后,又在catch自居中抛出新的异常,就相当于把抛出的异常类型转换了:

void process1(String s) {try {process2();} catch (NullPointerException e) {throw new IllegalArgumentException();}
}void process2(String s) {if (s==null) {throw new NullPointerException();}
}

当process2()抛出NullPointerException后,被process1()捕获,然后抛出IllegalArgumentException()。如果在main()中捕获IllegalArgumentException,wine吧打印异常栈看看,如下:

public class Main {public static void main(String[] args) {try {process1();} catch (Exception e) {e.printStackTrace();}}static void process1() {try {process2();} catch (NullPointerException e) {throw new IllegalArgumentException();}}static void process2() {throw new NullPointerException();}
}

打印出的异常栈类似于:

java.lang.IllegalArgumentExceptionat Main.process1(Main.java:15)at Main.main(Main.java:5)

这说明新的异常丢失了原始异常或者说根本异常的信息,我们已经看不到原始异常NullPointerException的信息了。为了能够追踪到完整的异常栈信息,在构造异常的时候,把原始的Exception实例传进去,新的Exception就可以持有原始Exception信息了,对上述代码修改如下:

public class Main {public static void main(String[] args) {try {process1();} catch (Exception e) {e.printStackTrace();}}static void process1() {try {process2();} catch (NullPointerException e) {throw new IllegalArgumentException(e);}}static void process2() {throw new NullPointerException();}
}

运行上述代码,打印出的异常栈类似于,如下:

java.lang.IllegalArgumentException: java.lang.NullPointerExceptionat Main.process1(Main.java:15)at Main.main(Main.java:5)
Caused by: java.lang.NullPointerExceptionat Main.process2(Main.java:20)at Main.process1(Main.java:13)

注意到:Caused by:Xxx,说明捕获的IllegalArgumentException并不是造成问题的根本原因,根本原因在于NullPointerException,时在Main.process2()方法中抛出的。在代码中获取原始异常信息可以使用Throwable.getCause()方法。如果返回null,则说明该异常已经是产生问题根本异常了。有了完整的异常栈信息,我们才可以快速的定位并修改代码解决问题。

捕获异常并再次抛出时,一定要保留原始异常,以便于更快的发现第一案发现场!!!

如果我们在try或者catch语句块中抛出异常,finally语句块是否还会执行呢?例如:

public class Main {public static void main(String[] args) {try {Integer.parseInt("abc");} catch (Exception e) {System.out.println("catched");throw new RuntimeException(e);} finally {System.out.println("finally");}}
}

上述代码执行结果如下:

catched
finally
Exception in thread "main" java.lang.RuntimeException: java.lang.NumberFormatException: For input string: "abc"at Main.main(Main.java:8)
Caused by: java.lang.NumberFormatException: For input string: "abc"at ...

第一行打印了catchd,说明进入了catch语句块,第二行打印了finally,说明执行了finally语句块,因此我们可以得知,在catch中抛出异常时,不会影响finally的执行,JVM会先执行finally,然后再抛出了异常。

3.异常屏蔽:

如果在执行finally语句时抛出异常,那么catch语句块还能否继续抛出呢? 例如:

public class Main {public static void main(String[] args) {try {Integer.parseInt("abc");} catch (Exception e) {System.out.println("catched");throw new RuntimeException(e);} finally {System.out.println("finally");throw new IllegalArgumentException();}}
}

执行上述代码发现异常信息如下:

catched
finally
Exception in thread "main" java.lang.IllegalArgumentExceptionat Main.main(Main.java:11)

控制台的输出说明finally抛出异常后,原来在catch中准备抛出的异常就"消失"了,因为只能抛出一个异常。没有被抛出的异常称之为"被屏蔽"的异常(Suppressed Exception)。在极少数情况下,我们需要获知所有的异常,那该如何保存所有的异常信息呢?方法是先用变量保存原始一场,然后调用Throwable.addSuppressed(),把原始一场添加进来,然后在finally中抛出,如下:

public class Main {public static void main(String[] args) throws Exception {Exception origin = null;try {System.out.println(Integer.parseInt("abc"));} catch (Exception e) {origin = e;throw e;} finally {Exception e = new IllegalArgumentException();if (origin != null) {e.addSuppressed(origin);}throw e;}}
}

当catch和finally都抛出了异常时,虽然catch的异常被屏蔽了,但是finally抛出的异常仍然包含了它:

Exception in thread "main" java.lang.IllegalArgumentExceptionat Main.main(Main.java:11)
Suppressed: java.lang.NumberFormatException: For input string: "abc"at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)at java.base/java.lang.Integer.parseInt(Integer.java:652)at java.base/java.lang.Integer.parseInt(Integer.java:770)at Main.main(Main.java:6)

通过Throwable.getSuppressed()可以获取所有的Suppressed Exception。绝大多数情况下,在finally中不要抛出异常。因此,我们通常不关心Suppressed Exception。

抛出异常小结:

                        1.调用printStackTrace()可以打印异常的传播栈,对于调试程序非常有效;

                        2.捕获异常并再次抛出新的异常时,应该持有原始异常信息;

                        3.通常不要在finally中抛出异常。如果在finally中抛出异常,应该将原始异常加入到哦原有异常中,调用方可通过Throwable.getSuppressed()获取所有添加的Suppressed Exception。

3.自定义异常:

当我们在代码中需要抛出异常时,尽量使用JDK已经定义的异常。例如:参数类型不合法,我们应该抛出IllegalArgumentException:

static void process1(int age) {if (age <= 0) {throw new IllegalArgumentException();}
}

当然在大型项目中,可以自定义新的异常类型,但是,保持一个合理的一场继承体系是非常重要的,通常的做法是自定义一个异常作为"根异常",然后,派生出各种业务类型的异常。根异常需要从一个适合的Exception派生,通常建议从RuntimeException派生:

public class BaseException extends RuntimeException {
}

其他异常类型都可以从根异常派生:

public class UserNotFoundException extends BaseException {
}public class LoginFailedException extends BaseException {
}...

自定义的根异常应该是需要提供多个构造方法的(最好是跟RuntimeException对应):

public class BaseException extends RuntimeException {public BaseException() {super();}public BaseException(String message, Throwable cause) {super(message, cause);}public BaseException(String message) {super(message);}public BaseException(Throwable cause) {super(cause);}
}

在上述构造方法参考RuntimeException原生实现。这样,抛出异常的时候,就可以选择合适的构造方法。

                自定义异常小结:

                        1.抛出异常时,尽可能复用JDK已经定义好的异常类型;

                        2.自定义异常体系时,建议从RuntimeException类派生根异常,然后从根异常在派生出其他类型的异常。

                        3.自定义异常时,尽量模仿RuntimeException类,提供多种构造方法。

异常全家桶来咯,异常捕获,异常抛出,自定义异常相关推荐

  1. ORACLE 存储过程异常捕获并抛出

    ORACLE 存储过程异常捕获并抛出 参考文章: (1)ORACLE 存储过程异常捕获并抛出 (2)https://www.cnblogs.com/wdw31210/p/7009731.html 备忘 ...

  2. .NET 指南:捕获并且抛出标准的异常类型

    下列指导方针为 .NET Framework 所提供的一些最常用的异常而描述了最佳的实践.关于 .NET Framework 所提供的完整的异常类列表,请参考:[.NET Framework 类库参考 ...

  3. Python程序异常处理:try、except、else、finally,捕获指定异常类型、捕获多个异常类型、捕获所有异常类型、捕获异常信息、异常的传递、raise抛出自定义异常

    输入与预期不匹配,触发异常,程序退出: 一.异常处理:使用try.except进行错误处理 为了保证程序运行的稳定性,错误应该被程序捕捉并合理控制 Python使用保留字try和except进行异常处 ...

  4. Spring捕获AOP抛出的异常

    Spring捕获AOP抛出的异常 背景 解决过程 最初方案 失败探索 添加AOP 继承SDK的AOP类 修改AOP生效条件 最终解决方案--BeanPostProcessor 总结 背景 在最近开发中 ...

  5. 【java基础】java异常,捕获与抛出

    java异常,捕获与抛出 1.概念: 在java里,所有的异常都有一个共同的祖先Throwable(可抛出). Throwable:有两个重要的子类:Exception(异常)和Error(错误). ...

  6. oracle异常抛出,ORACLE 存储过程异常捕获并抛出

    for tab_name in tables loop execute immediate 'drop table '||tab_name; --此处可能会报错 end loop; 当前情况是,循环表 ...

  7. java 异常 不抛,java中不捕获或抛出的异常

    java中不捕获或抛出的异常 发布时间:2020-06-25 14:29:16 来源:亿速云 阅读:137 作者:Leah 这期内容当中小编将会给大家带来有关java中不捕获或抛出的异常,文章内容丰富 ...

  8. java抛出自定义异常_10 个深恶痛绝的 Java 异常。。

    异常是 Java 程序中经常遇到的问题,我想每一个 Java 程序员都讨厌异常,一 个异常就是一个 BUG,就要花很多时间来定位异常问题. 什么是异常及异常的分类请看这篇文章:一张图搞清楚 Java ...

  9. 【C++ 语言】异常 ( 抛出字符串异常 | 抛出异常对象 | 抛出任意对象 | 抛出自定义异常 )

    文章目录 I 异常处理 II 字符串异常 III 异常对象 IV 抛出任意类型对象 V 自定义异常 VI 异常相关代码 I 异常处理 1. 异常处理 : 使用 " try{}catch(){ ...

最新文章

  1. 云从科技在渝布局人工智能大数据平台
  2. [转载]IE678兼容性前缀区分
  3. OpenCv 009---色彩空间与色彩空间转换
  4. Python:非常详细的解释如何判断某个变量的类型以及代码实现
  5. 【C】关于main()函数参数的问题;
  6. soapui 测试soap_使用SoapUI调用不同的安全WCF SOAP服务-基本身份验证,第二部分
  7. 预祝大家2011农历新年快乐,宏“兔”大展,心想事成~
  8. async/await
  9. python flask api 统计_python之restful api(flask)获取数据
  10. AI 秒杀人类!OpenAI 击败 DOTA2 世界冠军 OG
  11. HTTP 1.1 协议规范
  12. Tomcat8.0进入tomcat manager的方法
  13. 【Redis高手修炼之路】客户端工具——客户端工具以及常用命令
  14. 华为海思芯片自立自强!
  15. 河南自考本科英语可用计算机代替,河南自考选考2至3门专业课可代替英语课程...
  16. excel表格中怎样让某一行数据一直显示
  17. 如何装计算机网络驱动,网卡驱动安装,详细教您电脑网卡驱动怎么安装
  18. 解决Spring文件下载时文件损毁问题
  19. python 列表转换成字符串输出
  20. windows git bash 设置多个php版本和composer版本

热门文章

  1. 云个人计算机,把个人电脑变成你的云主机
  2. 2021河南科技大学计算机考研科目,2021河南科技大学考研参考书目
  3. JpGrap引入以及中文乱码问题
  4. Python安装第三方库出现失败并出现”Cannot find existing Pyqt5 plugin directories”的解决方案
  5. BigDL:分布式开放源码Apache SCAP深度学习库
  6. 会计科目 分类 说明
  7. minio对象存储单机部署并设置开机自启动及集成spring boot进行(创建删除桶)(上传下载删除文件)
  8. 联想G460笔记本触摸板驱动 For Windows 7 x64
  9. 计算机关闭应用窗口的方法,电脑怎么从后台关闭程序应用
  10. 【以太坊】什么是雷电网络 Raiden network