Java异常机制与 finally 与return的关系;

异常机制是指当程序出现错误后,程序如何处理。当出现错误后,程序执行的流程发生改变,程序的控制权转移到异常处理器。

Java异常体系图:
Java异常机制与 finally 与return的关系;_第1张图片

Throwable 类是 Java 语言中所有错误或异常的超类。上图是网上比较常见的一种图,给出了我们常见的几个异常;

  • Error:

Error表示程序在运行期间出现了十分严重、不可恢复的错误,在这种情况下应用程序只能中止运行,例如JAVA 虚拟机出现错误。Error是一种unchecked Exception,编译器不会检查Error是否被处理,在程序中不用捕获Error类型的异常。一般情况下,在程序中也不应该抛出Error类型的异常。

  • Checked Exception异常:

Checked Exception异常(上图中的粉色异常),所有继承自Exception 并且不是RuntimeException 的异常都是checked Exception,上图中的 IOException 和 ClassNotFoundException。JAVA 语言规定必须对checked Exception作处理,编译器会对此作检查,要么在方法体中声明抛出(throws)checked Exception,要么使用catch语句捕获checked Exception进行处理,不然不能通过编译。

  • RuntimeException异常:

RuntimeException 是一种Unchecked Exception,即表示编译器不会检查程序是否对RuntimeException作了处理,在程序中不必捕获RuntimException类型的异常,也不必在方法体声明抛出RuntimeException类。RuntimeException发生的时候,表示程序中出现了编程错误,所以应该找出错误修改程序,而不是去捕获RuntimeException。

异常处理的流程

  • 遇到异常,方法立即结束,同时,抛出一个异常对象,并不会返回一个值;
  • 调用该方法的程序也不会继续执行下去,而是搜索一个可以处理该异常的异常处理器,并执行其中的代码;

几点说明:

1、两种异常都可以捕获

运行时异常表示无法让程序恢复运行的异常,导致这种异常的原因通常是由于执行了错误的操作。一旦出现错误,建议让程序终止
受检查异常表示程序可以处理的异常。要么 try 要么 throws,否则调用会出错,连编译也无法通过。当然,这两种异常都是可以通过程序来捕获并处理的,例如:

public static int testNoFinally1(){
        int b = 20;
        int c=0;
        try {
            c = b/0;//这里抛出了运行时异常
            System.out.println("try block");
            return b += 80; 
        }
        catch (Exception e) {
            System.out.println("catch block");
        }
        finally {
            System.out.println("finally block");
        }
        System.out.println("after catch /0");
        return c;
    }

输出结果:

catch block
finally block
after catch /0
0
2、异常处理的语法规则:
  • try语句不能单独存在,可以和catch,finally组成 try...catch...finallytry...catchtry...finally三种结构,catch语句可以有一个或多个,finally语句最多一个,try、catch、finally这三个关键字均不能单独使用。
  • try、catch、finally 三个代码块中变量的作用域分别独立而不能相互访问。如果要在三个块中都可以访问,则需要将变量定义到这些块的外面。
  • 多个catch块时候,Java虚拟机会匹配其中一个异常类或其子类,就执行这个catch块,而不会再执行别的catch块。
  • throw语句后不允许有紧跟其他语句,因为这些没有机会执行。编译都通不过;
  • 如果一个方法调用了另外一个声明抛出异常的方法,那么这个方法要么处理异常,要么声明抛出。
3、 throw和throws关键字的区别

throw用来抛出一个异常,在方法体内。语法格式为:throw 异常对象。
throws用来声明方法可能会抛出什么异常,在方法名后,语法格式为:throws 异常类型1,异常类型2…异常类型n。

4、子类重写父类的方法时候不能声明抛出比父类大的异常

当我们子类要重写父类中的方法,如果父类的方法有异常声明,那么子类重写这个方法时候,所要声明的异常不应该比父类的大。只能是小等,或者可以没有。

Return 与 Finally
以下两种情况 finally 语句是不会被执行的:

(1)try语句没有被执行到,如在try语句之前就返回了比如在try之前抛出了异常,这样finally语句就不会执行,这也说明了finally语句被执行的必要而非充分条件是:相应的try语句一定被执行到。

(2)在try块中有System.exit(0);这样的语句,System.exit(0);是终止Java虚拟机JVM的,连JVM都停止了,所有都结束了,当然finally语句也不会被执行到。

Finally语句的执行与return的关系:不知道finally语句是在try的return之前执行还是之后执行?
答: finally 语句是在 try 的 return 语句执行之后 (return expression 中 expression表达式执行之后),return返回之前 (return返回expression结果之前)执行。

1. finally语句在return语句执行之后 return返回之前执行的。

public static int testFinalReturn1() {

        int b = 20;
        try {
            System.out.println("try block");
            //这个return 可以分两步 第一计算 b+=80; 第二步:返回 结果100;
            //在执行完第一步时,回去执行finally里面的东西,然后再执行第二步;
            //如果是return 100这样的表达式,可以把第一步看中100=100也会在执行完finally的东西执行返回;
            return b += 80;
        } catch (Exception e) {
            System.out.println("catch block");
        } finally {
            System.out.println("finally block");
            //由于第一步b+=80使得b=100所以这个判断肯定会执行
            if (b > 25) {
                System.out.println("b>25, b = " + b);
                //b=b-30; //不建议代码,我们在finally主要是做释放资源和善后的结果;
                //如果我们在这里修改了b的值 不会影响到return的b=100的结果,
            }
        }
        return b;
    }

输出结果:

System.out.println(testFinalReturn1());

try block
return statement
finally block
after return

2. finally块中的 return 语句会覆盖 try 块中的 return 返回;

public static int testFinalReturn2() {

        int b = 20;
        try {
            System.out.println("try block");
            return b += 80;
        } catch (Exception e) {
            System.out.println("catch block");
        } finally {
            System.out.println("finally block");
            if (b > 25) {
                System.out.println("b>25, b = " + b);
                b=b-30; //不建议代码,我们在finally主要是做释放资源和善后的结果;
            }
            return b;//这里的return语句会覆盖上面return语句的第二步所以b=100-30,
        }
        //return b;
    }

输出结果:

System.out.println(testFinalReturn2());

try block
finally block
b>25, b = 100
70

注意: 这说明finally里的return直接返回了,就不管try中是否还有返回语句,finally里加上return过后,finally外面的return b就变成不可到达语句了,也就是永远不能被执行到,所以需要注释掉否则编译器报错;

3. 在finally中对return expression中的expression值进行修改是否会影响到return的返回值;

3.1 对于基本类型及其包装类不会修改其原始值
public static int testFinalReturnModify1() {

        int b = 20;
        try {
            System.out.println("try block");
            //在此Step1 b=100, Step2 return 100;
            return b += 80;
        } catch (Exception e) {
            System.out.println("catch block");
        } finally {
            System.out.println("finally block");

            if (b > 25) {
                System.out.println("b>25, b = " + b);
            }
            //对b的值进行了修改;不会影响到原来值,
            b = 150;
        }
        //由于上面已在Step2中返回来了这里就不会再执行;
        return 2000;

    }

输出结果:

System.out.println(testFinalReturnModify1());

try block
finally block
b>25, b = 100
100
public static Integer testFinalReturnModify2() {

        Integer b = new Integer(20);
        Integer c = new Integer(80);
        try {
            System.out.println("try block");
            //在此自动拆箱Step1 b=100, Step2 return 100;
            return b += c;
        } catch (Exception e) {
            System.out.println("catch block");
        } finally {
            System.out.println("finally block");

            if (b > 25) {
                System.out.println("b>25, b = " + b);
            }
            //对b的值进行了修改;不会影响到原来值,
            b = 150;
        }
        //由于上面已在Step2中返回来了这里就不会再执行;
        return 2000;

    }

输出结果

System.out.println(testFinalReturnModify2());

try block
finally block
b>25, b = 100
100
3.2 对于String 类型不会修改其原始值,但是在finally执行完再进行拼接;
public static String testFinalReturnModify4() {

        String str = "hello";
        try {
            System.out.println("try block");
            //这个比较特殊,限制性finally 在进行拼接返回;
            return str+" world";
        } catch (Exception e) {
            System.out.println("catch block");
        } finally {
            System.out.println("finally block");

            System.out.println("str = " + str);//hello

            if ("hello world".equals(str)) {//false
                System.out.println("str = " + str);
            }
            //对 str 的值进行了修改;不会影响到原来值,
            str = "abc";
        }
        //由于上面已在Step2中返回来了这里就不会再执行;
        return "def";

    }

输出结果:

System.out.println(testFinalReturnModify4());

try block
finally block
str = hello
hello world
3.3 对于引用 类型会修改其原始值:
public static Map testFinalReturnModifyRef(){
        Map<String, String> map = new HashMap<String, String>();
        //在此对KEY赋予初始值 INIT;
        map.put("KEY", "INIT");
        try {
            map.put("KEY", "TRY");
            return map;
        }
        catch (Exception e) {
            map.put("KEY", "CATCH");
        }
        finally {
            map.put("KEY", "FINALLY");
            //将这个引用赋为 null;
            map = null;
        }
        return map;
    }

输出结果:

System.out.println(testFinalReturnModifyRef());
{KEY=FINALLY}
static class Student{
        private String name;
        private int age;
        public Student(){}
        public Student(String name,int age){
            this.name = name;
            this.age = age;
        }

        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }
        @Override
        public String toString() {
            return "Student [name=" + name + ", age=" + age + "]";
        }

    }
    public static Student testFinalReturnModifyRef2(){
        Student stu = new Student("zhangsan",30);
        //在此对KEY赋予初始值 INIT;

        try {
            return stu;
        }
        catch (Exception e) {
            System.out.println("catch..");
            stu.setName("catch");
        }
        finally {
            System.out.println("finally -->"+stu );
            System.out.println("finally..");    
            //编译不通过 意思是说这个stu不可以访问;
            stu.setName("finally");
        }
        return stu;
    }

输出结果:

finally -->Student [name=zhangsan, age=30]
finally..
Student [name=finally, age=30]

4. try块里的return语句在异常的情况下不会被执行,这样具体返回哪个看情况。

public static int test4() {
        int b = 20;
        try {
            System.out.println("try block");
            b = b / 0;//抛出运行时异常
            //下面这个语句不会再执行,而是直接执行处理完异常以后的语句;
            return b += 80;
        } catch (Exception e) {

            b += 15;
            System.out.println("catch block: b+=15 ->" + b);
        } finally {

            System.out.println("finally block");

            if (b > 25) {
                System.out.println("b>25, b = " + b);
            }

            b += 50;
        }
    //处理完异常以后,从异常处理完结束的地方继续往下执行:
        return 204;
    }

输出结果:

System.out.println(test4());

try block
catch block: b+=1535
finally block
b>25, b = 35
204
5. 当发生异常后,catch中的return执行情况与未发生异常时try中return的执行情况完全一样。
public static int test5() {
        int b = 20;
        try {
            System.out.println("try block");
            //抛出异常
            b = b /0;
            //下面这个语句用于执行不了了;
            return b += 80;
        } catch (Exception e) {

            System.out.println("catch block");
            //step 1 b=20+15 step2 return 35;
            return b += 15;
        } finally {

            System.out.println("finally block");

            if (b > 25) {
                System.out.println("b>25, b = " + b);
            }
            //不会影响到上面的35;
            b += 50;
        }
        //这里执行不到,不需要写 编译不通过;
        //return b;
    }

输出结果:

System.out.println(test5());

try block
catch block
finally block
b>25, b = 35
35

在此特别鸣谢以下前辈给出的经验和知识的分享;

Java常见的异常小结:
异常的深入研究与分析:
Java异常体系详解:
Java finally语句到底是在return之前还是之后执行 重点感谢

你可能感兴趣的:(JAVA-SE)