【码农每日一题】Java 异常(Part 2)相关面试题

284 阅读4分钟
关注一下嘛,又不让你背锅!

问:请简单描述下面方法的执行流程和最终返回值是多少?

  1. public static int test1(){

  2.    int ret = 0;

  3.    try{

  4.        return ret;

  5.    }finally{

  6.        ret = 2;

  7.    }

  8. }

  9. public static int test2(){

  10.    int ret = 0;

  11.    try{

  12.        int a = 5/0;

  13.        return ret;

  14.    }finally{

  15.        return 2;

  16.    }

  17. }

  18. public static void test3(){

  19.    try{

  20.        int a = 5/0;

  21.    }finally{

  22.        throw new RuntimeException("hello");

  23.    }

  24. }

答:本题旨在考察 try-catch-finally 块的用法踩坑经验,具体解析如下。

test1 方法运行返回 0,因为执行到 try 的 return ret; 语句前会先将返回值 ret 保存在一个临时变量中,然后才执行 finally 语句,最后 try 再返回那个临时变量,finally 中对 ret 的修改不会被返回。

test2 方法运行返回 2,因为 5/0 会触发 ArithmeticException 异常,但是 finally 中有 return 语句,finally 中 return 不仅会覆盖 try 和 catch 内的返回值且还会掩盖 try 和 catch 内的异常,就像异常没有发生一样(特别注意,当 finally 中没有 return 时该方法运行会抛出 ArithmeticException 异常),所以这个方法就会返回 2,而且不再向上传递异常了。

test3 方法运行抛出 hello 异常,因为如果 finally 中抛出了异常,则原异常就会被掩盖。

因此为避免代码逻辑混淆,我们应该避免在 finally 中使用 return 语句或者抛出异常,如果调用的其他代码可能抛出异常,则应该捕获异常并进行处理。

问:如果执行 finally 代码块之前方法返回了结果或者 JVM 退出了,这时 finally 块中的代码还会执行吗?

答:只有在 try 里面通过 System.exit(0) 来退出 JVM 的情况下 finally 块中的代码才不会执行,其他 return 等情况都会调用,所以在不终止 JVM 的情况下 finally 中的代码一定会执行。

问:分别说说下面代码片段都有什么问题?

  1. public static void func() throws RuntimeException, NullPointerException {

  2.    throw new RuntimeException("func exception");

  3. }

  4. public static void main(String args[]) {

  5.    try {

  6.        func();

  7.    } catch (Exception ex) {

  8.        ex.printStackTrace();

  9.    } catch (RuntimeException re) {

  10.        re.printStackTrace();

  11.    }

  12. }

上面代码段对于 func 方法后面 throws 列出的异常类型是不分先后顺序的,所以 func 方法是没问题的;对于 main 方法中在捕获 RuntimeException 类型变量 re 的地方会编译错误,因为 Exception 是 RuntimeException 的超类,func 方法执行的异常都会被第一个 catch 块捕获,所以会报编译时错误。

  1. public class Base {

  2.    public void func() throws IOException {

  3.        throw new IOException("Base IOException");

  4.    }

  5. }

  6. public class Sub extends Base {

  7.    public void func() throws Exception {

  8.        throw new Exception("Sub Exception");

  9.    }

  10. }

如上代码片段在编译时子类 func 方法会出现编译异常,因为在 java 中重写方法抛出的异常不能是原方法抛出异常的父类,这里 func 方法在父类中抛出了 IOException,所有在子类中的 func 方法只能抛出 IOExcepition 或是其子类,但不能是其父类。

  1. public static void func() {}

  2. public static void main(String args[]) {

  3.    try{

  4.        func();

  5.    }catch(IOException e) {

  6.        e.printStackTrace();

  7.    }

  8. }

上面代码段编译时在 IOException 时会出现编译错误,因为 IOException 是受检查异常,而 func 方法并没有抛出 IOException,所以编译报错,但是如果将 IOException 改为 Exception(或者 NullPointerException 等)则编译报错将消失,因为 Exception 可以用来捕捉所有运行时异常,这样就不需要声明抛出语句。

答:通过上面几个代码片段可以看出我们在书写多 catch 块时要保证异常类型的优先级书写顺序,要保证子类靠前父类靠后的原则;此外在 java 中重写方法抛出的异常不能是原方法抛出异常的父类;如果方法没有抛出受检查类型异常则在调用方法的地方就不能主动添加受检查类型异常捕获,但是可以添加运行时异常或者 Exception 捕获。

是的,这篇是这个系列的第二篇,续上篇,明天关于 Java 异常面试题 Part 3 就是异常相关完结篇了,欢迎持续关注~

本篇部分题目参考自下文:

http://www.importnew.com/7820.html

主人,别嫌短,长了你肯定不会看完的,所以这就是码农每日一题的宗旨~

看完分享一波嘛,和你的小伙伴一起讨论才更加有意思~

看个笑话放松一下

同事说,他在写 i++ 的时候总觉的自己写的是我艹。

There is only one success --- to be able to spend your life in your own way. 

只有一种成功,那就是能够用自己的方式度过自己的一生。