java异常机制

1、异常机制概述

异常机制是java程序设计中非常重要的一部分,在一定程度上保证了java程序的鲁棒性。程序执行过程中,遇到异常时会以throw形式抛出或者系统内部抛出,如下所示:

if (arg == null){
	throw new NullPointerException();
}

有抛出异常,就有捕获异常,以try{}catch(){}形式捕获,如下所示:

try {
	// code that might generate exceptions
}
catch (Type e0){}
catch (Type e1){}
1.1 标准异常分类及层次结构

Java核心库内建了一些通用的异常类,这些异常库均继承于Throwable类,如Exception类。


Throwable类层次结构如下图所示:


Throwable主分为Error和Exception类。

Error:系统产生,应用程序(程序员编写的应用层程序)无法捕获,程序员在程序运行时是没有办法处理Error的。

Exception:异常处理的核心,应用程序可以捕获,程序员可根据程序运行期间抛出的Exception种类进行针对性处理。

上述Throwable又可分为受检查异常和不受检查异常:

不受检查异常:包含Error和RuntimeException;不受检查的异常自动被java虚拟机抛出。

受检查异常:除了Error和RuntimeException。

2、异常控制块和return

2.1 异常使用语法   

异常控制块,也就是try..catch...finally,下图是常用的异常捕获结构:

try {
    // code that might generate exceptions
    throw new xxxException();// might throw by kernal 
}
catch (ArithmeticException e){
    // handle
}
catch (ArrayIndexOutOfBounds e){
    // handle
}
finally {
    // handle
}

try语句块中抛出的异常可能来自java内核,也可能来自定义的异常对象。

控制块执行顺序:先执行try块,如try块抛出异常,则执行相应的catch块,最后执行finally块,如下程序所示:

public class ExceptionTest {
    
    private static int count = 0;
    
    public static void main(String[] args){
        while (true){
            try {
                if (count++ == 0){
                    int a = 10/0;
                }
            }
            catch (ArithmeticException e){
                System.out.println("ArithmeticException");
            }
            finally {
                System.out.println("count = " + count);
                if (count == 2){
                    break;
                }
            }
        }
    }
}

程序输出为:

ArithmeticException
count = 1
count = 2

上述例子表明,正常情况下,finally最终均会得到执行;其实无论try块或catch块中是否存在return语句,finally块均会得到执行。

但是,finally真的总会执行吗?

public class ExceptionTest {
    
    private static int count = 0;
    
    public static void main(String[] args){
        while (true){
            try {
                System.exit(0);
                if (count++ == 0){
                    int a = 10/0;
                }
            }
            catch (ArithmeticException e){
                System.out.println("ArithmeticException");
            }
            finally {
                System.out.println("count = " + count);
                if (count == 2){
                    break;
                }
            }
        }
    }
}

程序没有任何打印输出,说明finally并没有执行。

其实,当jvm退出或控制块中有System.exit()存在时,finally并不会执行。

2.2    嵌入return语句

return语句的嵌入,会稍微复杂一些,但理解了异常处理机制,就可迎刃而解。

return语句嵌入指的是在try..catch..finally控制块中嵌入return语句,嵌入的位置可能在try块、catch块以及finally块。

下面通过三个用例来说明嵌入return语句对控制块执行的影响。

例1:

public class ExceptionTest {
    int a = 0;
    public int f() {
        try {
            a = 100;
            int b = 10/0;
            return a;
        }
        catch (ArithmeticException e) {
            a = 200;
            System.out.println("ArimthmeticException");
            return a;
        }
        finally {
            a = 300;
        }
    }
    
    public static void main(String[] args){
        ExceptionTest exceptionTest = new ExceptionTest();
        System.out.println(exceptionTest.f());
    }
}

程序输出:

ArimthmeticException
200

程序输出结果可以看出,虽然finally最终修改了a值,但返回的结果仍是catch块中的a的结果。原因是try块、catch块、finally块在执行完块内程序时,如果有return返回,会将该返回值压入栈中,做一个值拷贝。

上述程序中,虽然finally修改了a的值,a的值确实也变成了300,但是catch块中已经保存了a=200时的一份拷贝,因此返回的是拷贝后的值,而不是最终真实的a值。

例2:

public class ExceptionTest {
    int a = 0;
    public int f() {
        try {
            a = 100;
            int b = 10/0;
            return a;
        }
        catch (ArithmeticException e) {
            a = 200;
            System.out.println("ArimthmeticException");
            return a;
        }
        finally {
            a = 300;
            return a;
        }
    }
    
    public static void main(String[] args){
        ExceptionTest exceptionTest = new ExceptionTest();
        System.out.println(exceptionTest.f());
    }
}

程序执行结果为:

ArimthmeticException
300

例2说明try块、catch块、finally块中若有return语句,则执行return顺序优先级为finally块 > catch块 > try块。

例3:

public class ExceptionTest {
    int a = 0;
    public int f() throws Exception{
        try {
            a = 100;
            int b = 10/0;
            return a;
        }
        catch (ArithmeticException e) {
            a = 200;
            System.out.println("ArimthmeticException");
            throw new Exception();
        }
        finally {
            a = 300;
            return a;
        }
    }
    
    public static void main(String[] args){
        ExceptionTest exceptionTest = new ExceptionTest();
        try{
            System.out.println(exceptionTest.f());
        }
        catch(Exception e){
            e.printStackTrace();
            System.out.println("截获异常catch");
        }finally{
            System.out.println("异常处理finally");
        }
    }
}

程序执行结果为:

ArimthmeticException
300
异常处理finally

程序结果中没有“截获异常catch”输出,说明f()方法没有抛出异常。

本例说明如果异常控制块中有return语句,会覆盖该方法最终的异常抛出。

总结:

例1、例2目的:异常控制块中的return语句会对最后返回的值产生影响。

例3目的:异常控制块中return语句可能会覆盖最终的异常抛出。

例3的结果表明:finally控制块中尽可能不要使用return语句。


3、异常处理表

java程序抛出异常时,是如何响应对应的分支的呢?

其实,catch块是通过一个异常处理表查询对应的异常处理分支,每个方法在编译生成字节码时,均会生成一个异常处理表,如下所示:

public class ExceptionTest {
    int a = 0;
    public void f(){
        try {
            a = 100;
            int b = 10/0;
        }
        catch (ArithmeticException e) {
            a = 200;
        }
        catch (NullPointerException e){
            a = 300;
        }
        catch (ArrayIndexOutOfBoundsException e){
            a = 400;
        }
        finally {
            System.out.println(a);
        }
    }
    
    public static void main(String[] args){
        ExceptionTest exceptionTest = new ExceptionTest();
        exceptionTest.f();
    }
}

对应f()方法的部分字节码如下所示:

	  66: astore_1      
	  67: aload_0       
	  68: sipush        400
	  71: putfield      #2                  // Field a:I
	  74: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
	  77: aload_0       
	  78: getfield      #2                  // Field a:I
	  81: invokevirtual #4                  // Method java/io/PrintStream.println:(I)V
	  84: goto          100
	  87: astore_2      
	  88: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
	  91: aload_0       
	  92: getfield      #2                  // Field a:I
	  95: invokevirtual #4                  // Method java/io/PrintStream.println:(I)V
	  98: aload_2       
	  99: athrow        
	 100: return        
	 Exception table:
	   from    to  target type
		   0    11    24   Class java/lang/ArithmeticException
		   0    11    45   Class java/lang/NullPointerException
		   0    11    66   Class java/lang/ArrayIndexOutOfBoundsException
		   0    11    87   any
		  24    32    87   any
		  45    53    87   any
		  66    74    87   any
		  87    88    87   any


可以看到,异常处理表中包含所有catch块的异常分支,当try块抛出异常时,会查询异常处理表,找到对应的异常响应分支,执行对应的处理程序,异常处理表中包含try块的区域等信息。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值