Java: 关于try-catch-finally中catch语句块里return和throw的差别问题

下面的例子中

Student s = new Student("lily", 3);  //name, age
    try{
        int i = 3;
        int j = i/0;
        return s.toString();
    }catch (Exception e){
        Student t = s;
        return t.toString();               // 语句1
    }finally {
        s.setName("baga");
        System.out.println(s.toString());   // 语句2
    }
    

语句2先于语句1执行,结果是 Name is baga age is: 3
语句1返回的是:
Name is lily age is: 3
这说明return的是s的一个深拷贝,s不能被finally块影响了

语法糖try with resource

    try(FileInputStream fis = new FileInputStream(file)){
        fis.read();
    } catch (IOException e3){
        ...
    }
    
反编译其实就是

    try{
        FileInputStream fis = new FileInputStream(file);
        Throwable t = null;
        try{
            fis.read();
        }catch (Throwable t1){
            t = t1;
            throw t1;
        }finally {
            if(fis != null){
                if(t != null){
                    try {
                        fis.close();
                    }catch (Throwable t2){
                        t.addSuppressed(t2); 
                    }
                }
                else {
                    fis.close();
                }
            }
        }
    }catch (IOException e3){
        ...
    }
   

其中我们看到catch块中

t=t1;
throw t1;
      

然后下面:

t.addSuppressed(t2);

t2能被抛出的t1携带,说明 throw t1保留的是一个浅拷贝,能被finally块影响

这个区别是为何?

阅读 2k
2 个回答

不是这样的。其实是catch块里面的s.toString()先执行,再执行finally块里的内容,然后再执行return。所以,最后返回的内容其实是为执行finally块之前的s的内容。

clipboard.png

clipboard.png

这两张图可以清楚的看出来这两者之间的区别了,第一个例子中,在areturn这个指令之前,已经计算出了最后的返回值,setName是在areturn这个指令之后调用;第二个例子,就是正常执行,addSuppressed是在return之前调用。建议题主从jvm指令这个角度分析一下。

贴出我的代码:

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

import org.junit.Test;

public class TestExample {
    public static class Student {
        private String name;
        private int age;

        public Student(String name, int age) {
            this.name = name;
            this.age = age;
        }

        @Override
        public String toString() {
            // TODO Auto-generated method stub
            return "Name is " + name + " age is " + age;
        }

        public void setName(String string) {
            this.name = string;
        }

    }

    public String get() {
        Student s = new Student("lily", 3); // name, age
        try {
            int i = 3;
            int j = i / 0;
            return s.toString();
        } catch (Exception e) {
            Student t = s;
            return t.toString(); // 语句1
        } finally {
            s.setName("baga");
            System.out.println(s.toString()); // 语句2
        }
    }

    @Test
    public void test1() {
        try {
            File file = new File("");
            FileInputStream fis = new FileInputStream(file);
            Throwable t = null;
            try {
                fis.read();
            } catch (Throwable t1) {
                t = t1;
                throw t1;
            } finally {
                if (fis != null) {
                    if (t != null) {
                        try {
                            fis.close();
                        } catch (Throwable t2) {
                            t.addSuppressed(t2);
                        }
                    } else {
                        fis.close();
                    }
                }
            }
        } catch (IOException e3) {

        }
    }

    @Test
    public void test() {
        String result = get();
        System.out.print(result);
    }

}
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题