昨天的面试官居然是个小姐姐,讲了半个多小时的异常处理总之,这比我这个见不到太阳的出租屋好多了。渐渐走进来的脚步声打断了我的思绪。一位年轻的女士坐在我的面前,一股淡淡的香水味顿时钻入了我的鼻孔。小姐姐笑道:“大家好,我是今天的面试官,那我们开始吧?”我闭上直直的眼睛说:好吧。两个主要组件:抛出异常和捕获异常。那么抛出异常的两种类型是什么?”我马上回答:抛出异常分为显式和隐式。显式抛出异常是在代码中使用throw关键字手动抛出异常实例。隐式抛出异常是当Java虚拟机在执行过程中遇到无法继续执行的异常状态,自动抛出一个异常实例,比如我们经常遇到的NullPointerException。小姐姐说:“很好,那么捕获异常经常用什么关键字?”我立马回答:我们一般使用try、catch、finally等关键字。try用于标记需要异常监控的代码;catch用于捕获try监控的代码中触发的指定类型的异常,以及还可以定义如何处理异常类型;finally用于声明一段无论发生任何异常都必须运行的代码,它避免跳过一些关键的清理代码,例如:关闭打开的IO资源。女士说:“很好,如果三个关键字一起使用,代码执行的顺序是什么?”我立马回复:在正常执行的情况下,先执行try里的代码,再执行finally里的代码。如果try中的代码触发了异常,没有捕获到异常,则直接执行finally中的代码,执行完会重新抛出异常;如果异常被catch捕捉到,会先执行catch中的代码,再执行finally中的代码。如果catch中的代码也触发了异常,finally中的代码也会被执行,并抛出catch代码触发的异常。如果finally中的代码也触发了异常,则会中断当前finally代码的执行并抛出异常。小姐姐说:很好,在Java虚拟机中,如何实现异常处理呢?这道题有点难,想了想答道:主要是通过异常表。在编译后的字节码中,每个方法都附有一个异常表。异常表中可能有多条记录,每条记录包括一个from指针、一个to指针、一个目标指针和捕获的异常类型。这些指针的值就是字节码索引(bytecodeindex),用来定位字节码。其中from指针和to指针表示异常处理监听的范围,例如:try覆盖的范围。目标指针指向异常处理代码的起始位置,例如:catch代码的起始位置。如果触发异常,Java虚拟机从上到下遍历异常表中的所有记录。当触发异常的字节码的索引值在异常表记录的监控范围内时,Java虚拟机判断抛出的异常是否与该记录要捕获的异常匹配。如果匹配,控制流将移动到记录的目标指针指向的字节码。小姐姐说:有点抽象,能举个例子吗?“当然。”我说着,在纸上写着:publicclassOneMore{publicstaticvoidmain(String[]args){Stringstr="万茂学院";尝试{str=“尝试”;}catch(Exceptione){str="catch";}}}在这段代码的main方法中,我定义了一段try和catch代码。在编译后的字节码中,该方法的异常表中有一条记录:publicstaticvoidmain(java.lang.String[]);descriptor:([Ljava/lang/String;)Vflags:ACC_PUBLIC,ACC_STATICCode:stack=1,locals=3,args_size=10:ldc#2//String万猫学社2:astore_13:ldc#3//Stringtry5:astore_16:goto139:astore_210:ldc#5//Stringcatch12:astore_113:returnExceptiontable:fromtotargettype369类java/lang/Exception,其from指针和to指针分别为3和6,表示其监控范围是从索引为3的字节码开始,到索引为6的字节码结束(不包括6)。这条记录的目标指针是9,表示异常处理从索引为9的字节码开始。记录的最后一列表示异常处理捕捉到的异常类型是Exception。当触发异常的字节码索引值在3到6之间时,Java虚拟机判断抛出的异常是否为Exception。如果是,控制流将移动到索引为9的字节码开始执行。小姐姐满意的说道:“很好,我让HR跟你聊聊,如果顺利的话,今天就可以发offer了。”参考资料:《Java编程思想》《Java核心技术》《深入理解Java虚拟机:JVM高级特性与实践》这个我已经看过了。留下你的喜欢和关注,它会成为未来的一大利器。
