绕过 Java 编译器检查,在任何地方抛出受检异常

1 Java 异常简介

        众所周知,Java 的所有异常都派生自 Throwable 类,在继承结构上,从 Throwable 派生出了 Error 和 Exception 两大类。其中,Error 表示系统级别的严重程序错误,一般由 JVM 抛出,我们也不应该捕获这类异常,用户自定义的异常一般都派生自 Exception 类。

       从是否被编译器强制检查一点,异常又可分为受检异常(Checked Exception)和未受检异常(Unchecked Exception)。未受检异常派生自 Error 或者 RuntimeException,表示不可恢复的程序错误,典型例子有 AssertionError、NullPointerException 等,编译器不会强制我们捕获这类异常。受检异常则是除了 Error/RuntimeException 之外,派生自 Throwable 或者 Exception 的其他异常,比如 IOException、SQLException 等。

       如果一个方法声明自己可能抛出受检异常,那么编译器会强制它的调用者必须使用 try-catch 捕获此异常,或者在自己的方法中加上 throws 声明将异常继续传播给外界除非抛出RuntimeException或其子类,可在外部进行异常捕获。

        多年以来,Java 中受检异常的设计一直颇受争议,反对者认为,受检异常容易破坏方法声明的兼容性,会使代码的可读性降低,还增加开发的工作量等等。当然也有一些支持者,他们认为受检异常可以强迫程序员去思考,有助于他们写出更健壮的代码,可以参考王垠的文章「Kotlin 和 Checked Exception」。

2 java 中的异常处理实例

直接上代码,先贴下面测试需要调用的方法:
// catch 后续处理工作
public static boolean catchMethod() {
    System.out.print("call catchMethod and return  --->>  ");
    return false;
}
// finally后续处理工作
public static void finallyMethod() {
    System.out.println();
    System.out.print("call finallyMethod and do something  --->>  ");
}


1. 抛出 Exception,没有 finally,当 catch 遇上 return

public static boolean catchTest() {
  try {
      int i = 10 / 0;   // 抛出 Exception,后续处理被拒绝
      System.out.println("i vaule is : " + i);
      return true;    // Exception 已经抛出,没有获得被执行的机会
  } catch (Exception e) {
      System.out.println(" -- Exception --");
      return catchMethod();    // Exception 抛出,获得了调用方法并返回方法值的机会
  }
}
后台输出结果:
-- Exception --
call catchMethod and return  --->>  false


2. 抛出 Exception,当 catch 体里有 return,finally 体的代码块将在 catch 执行 return 之前被执行
public static boolean catchFinallyTest1() {
  try {
      int i = 10 / 0; // 抛出 Exception,后续处理被拒绝
      System.out.println("i vaule is : " + i);
      return true;   // Exception 已经抛出,没有获得被执行的机会
  } catch (Exception e) {
      System.out.println(" -- Exception --");
      return catchMethod();  // Exception 抛出,获得了调用方法的机会,但方法值在 finally 执行完后才返回
  }finally{
    finallyMethod();  // Exception 抛出,finally 代码块将在 catch 执行 return 之前被执行
  }
}
后台输出结果:

 -- Exception --
call catchMethod and return  --->>  
call finallyMethod and do something  --->>  false

3. 不抛 Exception,当 finally 代码块里面遇上 return,finally 执行完后将结束整个方法

public static boolean catchFinallyTest2() {
    try {
        int i = 10 / 2;  // 不抛出 Exception
        System.out.println("i vaule is : " + i);
        return true;   // 获得被执行的机会,但执行需要在 finally 执行完成之后才能被执行
    } catch (Exception e) {
        System.out.println(" -- Exception --");
        return catchMethod();
    }finally{
        finallyMethod();
        return false; // finally 中含有 return 语句,这个 return 将结束这个方法,不会在执行完之后再跳回 try 或 catch 继续执行,方法到此结束,返回 false
    }
}


后台输出结果:
i vaule is : 5
call finallyMethod and do something  --->>  false


4. 不抛 Exception,当 finally 代码块里面遇上 System.exit() 方法 将结束和终止整个程序,而不只是方法

public static boolean finallyExitTest() {
    try {
        int i = 10 / 2;  // 不抛出 Exception
        System.out.println("i vaule is : " + i);
        return true;   // 获得被执行的机会,但由于 finally 已经终止程序,返回值没有机会被返回
    } catch (Exception e) {
        System.out.println(" -- Exception --");
        return true;
    }finally {
        finallyMethod();
        System.exit(0);// finally 中含有 System.exit() 语句,System.exit() 将退出整个程序,程序将被终止
    }
}


后台输出结果:
i vaule is : 5
call finallyMethod and do something  --->>  


5. 抛出 Exception,当 catch 和 finally 同时遇上 return,catch 的 return 返回值将不会被返回,finally 的 return 语句将结束整个方法并返回

public static boolean finallyTest1() {
    try {
        int i = 10 / 0; // 抛出 Exception,后续处理被拒绝
        System.out.println("i vaule is : " + i);
        return true;   // Exception 已经抛出,没有获得被执行的机会
    } catch (Exception e) {
        System.out.println(" -- Exception --");
        return true;  // Exception 已经抛出,获得被执行的机会,但返回操作将被 finally 截断
    }finally {
        finallyMethod();
        return false;  // return 将结束整个方法,返回 false
    }
}


后台输出结果:
 -- Exception --
call finallyMethod and do something  --->>  false


6. 不抛出 Exception,当 finally 遇上 return,try 的 return 返回值将不会被返回,finally 的 return 语句将结束整个方法并返回

public static boolean finallyTest2() {
    try {
        int i = 10 / 2;  // 不抛出 Exception
        System.out.println("i vaule is : " + i);
        return true;   // 获得被执行的机会,但返回将被 finally 截断
    } catch (Exception e) {
        System.out.println(" -- Exception --");
        return true;
    }finally {
        finallyMethod();
        return false; // return 将结束这个方法,不会在执行完之后再跳回 try 或 catch 继续执行,返回 false
    }
}
后台输出结果:
i vaule is : 5
call finallyMethod and do something  --->>  false


结语:

java 的异常处理中(假设方法需要返回值):

  1. 在不抛出异常的情况下,程序执行完 try 里面的代码块之后,该方法并不会立即结束,而是继续试图去寻找该方法有没有 finally 的代码块,
  2. 如果没有 finally 代码块,整个方法在执行完 try 代码块后返回相应的值来结束整个方法;
  3. 如果有 finally 代码块,此时程序执行到 try 代码块里的 return 语句之时并不会立即执行 return,而是先去执行 finally 代码块里的代码,
  4. 若 finally 代码块里没有 return 或没有能够终止程序的代码,程序将在执行完 finally 代码块代码之后再返回 try 代码块执行 return 语句来结束整个方法;
  5. 若 finally 代码块里有 return 或含有能够终止程序的代码,方法将在执行完 finally 之后被结束,不再跳回 try 代码块执行 return。

在抛出异常的情况下,原理也是和上面的一样的,你把上面说到的 try 换成 catch 去理解就 OK 了 *_*

###################

java异常处理-finally中使用return和throw语句

java异常语句中的finally块通常用来做资源释放操作,如关闭文件、关闭网络连接、关闭数据库连接等。正常情况下finally语句中不应该使用return语句也不应该抛出异常,以下讨论仅限于java语言设计本身,正常编码时应避免。

finally块中使用return会覆盖method的返回值

以下代码的返回值为:1

public static int div(){
        try {
            return 3;
        }catch (ArithmeticException e){
            System.out.println("catch in div");
            return 2;
        }
        finally {
            System.out.println("finally in div");
            return 1;
        }
    }

以下代码的返回值同样是:1

 public static int div(){
        try {
            return 3/0;
        }catch (ArithmeticException e){
            System.out.println("catch in div");
            return 2;
        }
        finally {
            System.out.println("finally in div");
            return 1;
        }
    }

finally块中使用return会抑制异常的冒泡传输

即:只要finally中使用了return语句,调用者便认为该方法正常返回

以下代码

/**
 * Created by Administrator on 2017/11/27.
 */

public class Test {

    public static void main(String[] args) {
        adapter();
    }

    public static void adapter() {
        try {
            div();
        } catch (ArithmeticException e) {
            System.out.println("catch in adapter");
        } finally {
            System.out.println("finally in adapter");
        }
    }

    public static int div() {
        try {
            int a = 5 / 0;
            return a;
        } catch (ArithmeticException e) {
            System.out.println("catch in div");
            throw e;  // 重新将异常抛出给调用者
        } finally {
            System.out.println("finally in div");
        }
    }
}

输出为

catch in div
finally in div
catch in adapter
finally in adapter

 但如果在 div 的finally块中添加了return语句

    public static int div(){
        try {
            int a =  5/0;
            return a;
        }catch (ArithmeticException e){
            System.out.println("catch in div");
            throw  e; // 重新将异常抛出给调用者,但是抛出会被忽略
        }
        finally {
            System.out.println("finally in div");
            return 1;
        }
    }

则代码的输出为

catch in div
finally in div
finally in adapter

即:finally块中的return语句会阻止异常的栈调用传输,使caller认为该方法已经正常返回

finally块中的throw语句会覆盖try和catch语句中的异常

以下代码

/**
 * Created by Administrator on 2017/11/27.
 */

public class Test {

    public static void main(String[] args) {
        adapter();
    }

    public static void adapter() {
        try {
            div();
        } catch (Exception e) {
            System.out.println(String.format("catch in adapter: %s",e.getMessage()));
        } finally {
            System.out.println("finally in adapter");
        }
    }

    public static int div() throws Exception{
        try {
            int a = 5 / 0;
            return a;
        } catch (ArithmeticException e) {
            System.out.println("catch in div");
            throw new Exception("Exception in div"); // 抛出新的异常
        } finally {
            System.out.println("finally in div");
            throw new Exception("Exception in Finally");  // 抛出新的异常
        }
    }
}

输出是:

catch in div
finally in div
catch in adapter: Exception in Finally
finally in adapter

即,catch块中抛出的异常北finally块抛出的异常替换了

修改div方法为

 public static int div() throws Exception{
        try {
            int a = 5 / 0;
            return a;
        }  finally {
            System.out.println("finally in div");
            throw new Exception("Exception in Finally");  // 抛出新的异常
        }
    }

输出为:

finally in div
catch in adapter: Exception in Finally
finally in adapter

即,try块中捕获的异常北finally块抛出的异常替换

finally块和普通代码块一样,无法同时使用return语句和throw语句,因为无法通过编译

发布了120 篇原创文章 · 获赞 125 · 访问量 107万+

猜你喜欢

转载自blog.csdn.net/yangyangye/article/details/102569437