java 异常详解

2018-09-16  本文已影响22人  tracy_668

     先看一道题,输出啥?

package Test; 
   
public class TestException { 
    public TestException() { 
    } 
   
    boolean testEx() throws Exception { 
        boolean ret = true; 
        try { 
            ret = testEx1(); 
        } catch (Exception e) { 
            System.out.println("testEx, catch exception"); 
            ret = false; 
            throw e; 
        } finally { 
            System.out.println("testEx, finally; return value=" + ret); 
            return ret; 
        } 
    } 
   
    boolean testEx1() throws Exception { 
        boolean ret = true; 
        try { 
            ret = testEx2(); 
            if (!ret) { 
                return false; 
            } 
            System.out.println("testEx1, at the end of try"); 
            return ret; 
        } catch (Exception e) { 
            System.out.println("testEx1, catch exception"); 
            ret = false; 
            throw e; 
        } finally { 
            System.out.println("testEx1, finally; return value=" + ret); 
            return ret; 
        } 
    } 
   
    boolean testEx2() throws Exception { 
        boolean ret = true; 
        try { 
            int b = 12; 
            int c; 
            for (int i = 2; i >= -2; i--) { 
                c = b / i; 
                System.out.println("i=" + i); 
            } 
            return true; 
        } catch (Exception e) { 
            System.out.println("testEx2, catch exception"); 
            ret = false; 
            throw e; 
        } finally { 
            System.out.println("testEx2, finally; return value=" + ret); 
            return ret; 
        } 
    } 
   
    public static void main(String[] args) { 
        TestException testException1 = new TestException(); 
        try { 
            testException1.testEx(); 
        } catch (Exception e) { 
            e.printStackTrace(); 
        } 
    } 
}

如果你一眼就能看出答案,那么这篇文章你就不用浪费时间看啦。输出结果为:

   i=2
  i=1
  testEx2, catch exception
  testEx2, finally; return value=false
  testEx1, finally; return value=false
  testEx, finally; return value=false

为什么要使用异常?

      在没有异常机制的时候比如C语言,我们是这样处理的:通过函数的返回值来判断是否发生了异常(这个返回值通常是已经约定好了的),调用该函数的程序负责检查并且分析返回值。虽然可以解决异常问题,但是这样做存在几个缺陷:

  1. 容易混淆。如果约定返回值为-11111时表示出现异常,那么当程序最后的计算结果真的为-1111呢?
  2. 代码可读性差。将异常处理代码和程序代码混淆在一起将会降低代码的可读性。
  3. 由调用函数来分析异常,这要求程序员对库函数有很深的了解
          在java中提供了异常处理机制能够有效将正常业务代码和异常代码分离开来。

使用异常机制它能够降低错误处理代码的复杂度,如果不使用异常,那么就必须检查特定的错误,并在程序中的许多地方去处理它,而如果使用异常,那就不必在方法调用处进行检查,因为异常机制将保证能够捕获这个错误,并且,只需在一个地方处理错误,即所谓的异常处理程序中。这种方式不仅节约代码,而且把“概述在正常执行过程中做什么事”的代码和“出了问题怎么办”的代码相分离。总之,与以前的错误处理方法相比,异常机制使代码的阅读、编写和调试工作更加井井有条。

异常分类

      在java中,所有的异常都有一个共同的祖先,Throwable,它有两个重要的子类,Exception(异常)和Error(错误)。Error是指程序无法处理的错误,通常是指jvm运行时出现的问题,比如当jvm不再具有继续执行操作所需的内存资源时,将抛出OutOfMemory,当error发生时,jvm一般选择终止线程。

      Exception是指由于程序逻辑本身的问题,程序本身可以处理的异常。RuntimeException是其一个重要子类。
      java异常通常又分为可查的异常和不可查的异常,可查的异常在一定程度上是可以预计的,在编译阶段就需要处理,否则编译通不过。在Exception中,除了RuntimeException及其子类外,其他Excetpion及其子类都属于可查异常。不可检查异常包括运行时异常(RuntimeException)和错误(Error)。

try catch finally 字节码分析

static int inc(){
    int x;
    try {
        x = 1;
        return x;
    } catch (Exception e){
        x = 2;
        return x;
    } finally {
        x = 3;
    }
}
static int inc();
descriptor: ()I
flags: ACC_STATIC
Code:
  stack=1, locals=4, args_size=0
     0: iconst_1  //try 块中的 x = 1;
     1: istore_0  //保存栈顶元素到局部变量表中索引为 0 的 slot 中
     2: iload_0   //加载局部变量表中索引为 0 的值到栈中
     3: istore_1  //保存栈顶元素到局部变量表中索引为 1 的 slot 中
     4: iconst_3  //finally 块中的 x = 3;
     5: istore_0  //保存栈顶元素到局部变量表中索引为 0 的 slot 中,x 的值存在这里。
     6: iload_1  //加载局部变量表中索引为 1 的值到栈中
     7: ireturn  //返回栈顶元素,即 x = 1;正常情况下函数运行到这里就结束了,如果出现异常根据异常表跳转到指定的位置
     8: astore_1 //给 catch 块中定义的 Exception e 赋值,存储在 slot1 中。
     9: iconst_2 //catch 块中的 x = 2;
    10: istore_0
    11: iload_0
    12: istore_2
    13: iconst_3 //finally 块中的 x = 3;
    14: istore_0
    15: iload_2
    16: ireturn //此时返回的是 slot2 中的值,即 x = 2
    17: astore_3 //如果出现不属于 java.lang.Exception 及其子类的异常,才会根据异常表中的规则跳转到这里。
    18: iconst_3 //finally 块中的 x = 3;
    19: istore_0
    20: aload_3 //将异常加载到栈顶,
    21: athrow //抛出栈顶的异常
  Exception table:
     from    to  target type
         0     4     8   Class java/lang/Exception
         0     4    17   any
         8    13    17   any

      可以看到,Java 的异常处理是通过异常表的方式来决定代码执行的路径。而finally的实现是通过在每个路径的最后加入finally块中的字节码实现的。

try catch 会不会影响性能

try {
    for(int i = 0; i < max; i++) {
        String myString = ...;
        float myNum = Float.parseFloat(myString);
        myFloats[i] = myNum;
    }
} catch (NumberFormatException ex) {
    return null;
}
for(int i = 0; i < max; i++) {
    String myString = ...;
    try {
        float myNum = Float.parseFloat(myString);
    } catch (NumberFormatException ex) {
        return null;
    }
    myFloats[i] = myNum;
}

      上述性能区别在哪?想当然就觉得try catch重复执行了这么多次肯定比只执行了一次跑得肯定慢,空间消耗肯定更大,实际情况是不是就是这样呢?
       从上小节字节码可知,每个类会跟随一张异常表(exception table),每一个try catch都会在这个表里添加行记录,每一个记录都有4个信息(try catch的开始地址,结束地址,异常的处理起始位,异常类名称)。
      当代码在运行时抛出了异常时,首先拿着抛出位置到异常表中查找是否可以被catch(例如看位置是不是处于任何一栏中的开始和结束位置之间),如果可以则跑到异常处理的起始位置开始处理,如果没有找到则原地return,并且copy异常的引用给父调用方,接着看父调用的异常表。。。以此类推。
      也就是说,异常如果没发生,也就不会去查表,也就是说你写不写try catch 也就是有没有这个异常表的问题,如果没有发生异常,写try catch对性能是木有消耗的,所以不会让程序跑得更慢。try 的范围大小其实就是异常表中两个值(开始地址和结束地址)的差异而已,也是不会影响性能的。

异常处理机制

      当一个方法出现错误引发异常时,运行时系统创建异常对象,该对象包括了异常类型和堆栈信息等,接着运行时系统负责寻找合适的异常处理器(exception handler),潜在的异常处理器是异常发生时依次存留在调用栈中的方法的集合,当异常处理器所能处理的异常类型与方法抛出的异常类型相符时,就是合适的异常处理器,运行时系统从发生异常的方法开始,依次回查调用栈中的方法,直至找到含有合适异常处理器的方法并执行。当运行时系统遍历调用栈而未找到合适 的异常处理器,则运行时系统终止。同时,意味着Java程序的终止。

异常的使用建议

OutputStreamWriter out = null;
        java.sql.Connection conn = null;
        try {            //   ---------1
            Statement stat = conn.createStatement();
            ResultSet rs = stat.executeQuery("select *from user");
            while (rs.next()){
                out.println("name:" + rs.getString("name") + "sex:"
                        + rs.getString("sex"));
            }
            conn.close();         //------2
            out.close();
        } 
        catch (Exception ex){    //------3
            ex.printStackTrace();    //------4
        }
上一篇 下一篇

猜你喜欢

热点阅读