为什么要设计JAVA异常

早期没有异常处理机制时。错误的处理模式往往是会返回某个特殊值或设置某个标志,并且假定调用者将对这个返回值或标志进行检查,以判定是否发生了错误。然而随着时间的推移,人们发现,高傲的程序员们更倾向于:“错误也许会发生,但那是别人造成的,不关我的事”。所以不检查的情况就不足为奇了。有这样的一部分函数,往往处理这种检查很无聊,比如:println。如果的确每次调用方法都彻底检查,代码将会变得难以阅读。并且需要定义每种包含错误信息的数据结构作为函数的返回类型。

  • 如果没有异常,那么就必须检查特定错误,并在程序中处理它,这样代码的复用率会降低。
  • 有了异常机制,就可以把“描述在正常执行过程中做什么事”的代码和“出了问题怎么办”的代码分离。

所以设计异常的好处:能够解放判断返回值的方式,提供一种上抛的处理机制,降低代码的判断复杂度,并能保证捕获这个异常,集中处理,增强代码复用率

异常类的结构Throwable.jpeg

不受检查异常Unchecked Exception
1.Error和RuntimeException。
2.不希望程序捕获或者无法处理的错误。

检查性异常CheckedException
1.非运行时异常。
2.用户程序可能捕获的异常情况或者程序可以处理的异常。

异常的概念

Java把异常当作对象来处理,并定义一个基类java.lang.Throwable作为所有异常的超类。
Throwable分成了两个不同的分支
一个分支是Error,它表示不希望被程序捕获或者是程序无法处理的错误。
一个分支是Exception,它表示用户程序可能捕捉的异常情况或者说是程序可以处理的异常。其中异常类Exception又分为运行时异常(RuntimeException)和非运行时异常。

Exception:在Exception分支中有一个重要的子类RuntimeException(运行时异常),该类型的异常自动为你所编写的程序定义ArrayIndexOutOfBoundsException(数组下标越界)、NullPointerException(空指针异常)、ArithmeticException(算术异常)、MissingResourceException(丢失资源)、ClassNotFoundException(找不到类)等异常,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生;而RuntimeException之外的异常我们统称为非运行时异常,类型上属于Exception类及其子类,从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如IOException、SQLException等以及用户自定义的Exception异常,一般情况下不自定义检查异常。

Error:Error类对象由 Java 虚拟机生成并抛出,大多数错误与代码编写者所执行的操作无关。
Error和Exception的区别:Error通常是灾难性的致命的错误,是程序无法控制和处理的,当出现这些异常时,Java虚拟机(JVM)一般会选择终止线程;Exception通常情况下是可以被程序处理的,并且在程序中应该尽可能的去处理这些异常。

异常处理机制

Java异常处理涉及到五个关键字,分别是:try、catch、finally、throw、throws。下面将骤一介绍,通过认识这五个关键字,掌握基本异常处理知识。

• try用于监听。将要被监听的代码(可能抛出异常的代码)放在try语句块之内,当try语句块内发生异常时,异常就被抛出。
• catch用于捕获异常。catch用来捕获try语句块中发生的异常。
• finally 语句块总是会被执行。它主要用于回收在try块里打开的物力资源(如数据库连接、网络连接和磁盘文件)。只有finally块,执行完成之后,才会回来执行try或者catch块中的return或者throw语句,如果finally中使用了return或者throw等终止方法的语句,则就不会跳回执行,直接停止。
• throw用于抛出异常。
• throws用在方法签名中,用于声明该方法可能抛出的异常。

多重catch

image.png
使用多重的catch语句:很多情况下,由单个的代码段可能引起多个异常。处理这种情况,我们需要定义两个或者更多的catch子句,每个子句捕获一种类型的异常,当异常被引发时,每个catch子句被依次检查,第一个匹配异常类型的子句执行,当一个catch子句执行以后,其他的子句将被旁路。

finally和return的顺序

If the try clause executes a return, the compiled code does the following: Saves the return value (if any) in a local variable. Executes a jsr to the code for the finally clause. Upon return from the finally clause, returns the value saved in the local variable.
如果try语句里有return,那么代码的行为如下: 1.如果有返回值,就把返回值保存到局部变量中 2.执行jsr指令跳到finally语句里执行 3.执行完finally语句后,返回之前保存在局部变量表里的值

1.finally块的语句在try或catch中的return语句执行之后返回之前执行
2.finally里的修改语句可能影响也可能不影响try或catch中 return已经确定的返回值,不可变实例不影响,可变类影响。
3.finally里也有return语句则覆盖try或catch中的return语句直接返回。

  1. package com.emar.dealer.manager;
  2. import java.util.HashMap;
  3. import java.util.Map;
  4. public class LocalTest {
  5. public static void main(String[] args) {
  6. LocalTest localTest = new LocalTest();
  7. Integer result = localTest.mm1();
  8. System.out.println(result);
  9. // System.out.println(getMap().get("KEY").toString());
  10. }
  11. public Integer mm1(){
  12. Integer i = 10;
  13. try{
  14. System.out.println("return : ");
  15. return i += 3;
  16. }catch (Exception e){
  17. System.out.println("catch block");
  18. }finally {
  19. /**
  20. * 不可变类,所以finally不会影响返回结果。
  21. */
  22. i += 5;
  23. System.out.println("finally : ");
  24. }
  25. return i;
  26. }
  27. public static Map<String, String> getMap() {
  28. Map<String, String> map = new HashMap<String, String>();
  29. map.put("KEY", "INIT");
  30. try {
  31. map.put("KEY", "TRY");
  32. return map;
  33. }
  34. catch (Exception e) {
  35. map.put("KEY", "CATCH");
  36. }
  37. finally {
  38. map.put("KEY", "FINALLY");
  39. /**
  40. * finally的map.put("KEY", "FINALLY")起了作用,而map = null;却没起作用呢?
  41. * 这就是Java到底是传值还是传址的问题了,简单来说就是:Java中只有传值没有传址,这也是为什么map = null这句不起作用。
  42. * 因此return x实际返回的是return指令执行时,x在操作数栈顶的一个快照或者叫副本,而不是x这个值。
  43. */
  44. map = null;
  45. }
  46. return map;
  47. }
  48. }

try-with-resource

  1. public class TryWithResource {
  2. public static void main(String[] args) {
  3. try (BufferedInputStream bin = new BufferedInputStream(new FileInputStream(new File("test.txt")));
  4. BufferedOutputStream bout = new BufferedOutputStream(new FileOutputStream(new File("out.txt")))) {
  5. int b;
  6. while ((b = bin.read()) != -1) {
  7. bout.write(b);
  8. }
  9. }
  10. catch (IOException e) {
  11. e.printStackTrace();
  12. }
  13. }
  14. }

资源必须实现AutoClosable接口。该接口的实现类需要重写close方法

public class Connection implements AutoCloseable {
     public void sendData() {
         System.out.println("正在发送数据");
     }
     @Override
     public void close() throws Exception {
         System.out.println("正在关闭连接");
     }
}

原理
编译器会帮我们添加finally并且还添加addSuppressed。
1.7开始,Throwable类新增了addSuppressed方法,支持将一个异常附加到另一个异常身上,从而避免异常屏蔽。

package com.codersm.trywithresource;
public class TryWithResource {
     public TryWithResource() {
     }
     public static void main(String[] args) {
         try {
             Connection conn = new Connection();
             Throwable var2 = null;
             try {
                conn.sendData();
             } catch (Throwable var12) {
                 var2 = var12;
                 throw var12;
             } finally {
                 if (conn != null) {
                     if (var2 != null) {
                         try {
                             conn.close();
                         } catch (Throwable var11) {
                            var2.addSuppressed(var11);
                         }
                    } else {
                        conn.close();
                    }
                }
             }
         } catch (Exception var14) {
             var14.printStackTrace();
         }
     }
}

解决了异常屏蔽的问题。