记一道代码分析题

来自极米的暑期服务端实习生笔试卷

/**
 * @author yao 2022/7/22
 * 输出是:22、34、17
 */
public class Test {
    public static void main(String[] args) {
        System.out.println(new B().getValue());
    }

    static class A {
        protected int value;

        public A(int v) {
            setValue(v);
        }

        public void setValue(int value) {
            this.value = value;
        }

        public int getValue() {
            try {
                value++;
                return value;
            } finally {
                this.setValue(value);
                System.out.println(value);
            }
        }
    }

    static class B extends A {
        public B() {
            super(5);
            setValue(getValue() - 3);
        }

        public void setValue(int value) {
            super.setValue(2 * value);
        }
    }
}

执行分析:

  1. 主函数执行,压栈去new一个B对象
  2. 调用B的构造函数,B的构造函数里面有构造了一个A对象是什么意思?

啊啊啊啊——protect可以被子类访问,default不能,又记混了

那么此时B继承了A的value值=5,

此时是B对象在执行,此时value值为10

  1. 调用A构造,A又调用setValue()函数,但是根据上面的倒推,这里实际调用的是被重写的B的setValue()函数,B的setValue()函数又去调A的setValue()函数,最终把value变成了10

这里到底有没有A对象实例被创建?

  1. setValue()函数又去调getValue()函数

  2. getValue()函数首先把value++=11,然后在return前去执行了finally代码块

  3. finally代码块中this仍然是B对象实例,调用B自己的setValue()函数,value值被加倍=22,这里是第一个输出

  4. getValue()方法退出,返回22-3=19作为参数传给B的setValue方法,value=38,这里返回值是11?!,-3setValue获得的参数为8,结果是加倍value=16,B对象实例创建完成

那么也就是说:这里的return在finally之前就执行了

  1. 又调getValue()方法,value++=17,在setValue()方法被调用后加倍,输出34,这里是第二个输出

  2. 但是返回值仍然是17,这里是main方法中的输出,至此执行完毕

后记

疯狂挖坑

  • 坑1:protect修饰,子类可访问,default不能
  • 坑2:子类构造函数中必须通过super()显示调用父类构造函数,必须是第一句,这一句中仍然是B对象在执行
  • 坑3:return语句在finally代码块之前执行

回头

上面第三点应该不对,finally代码块应该是在return语句前执行的

    public static void main(String[] args) {

        tempTest t = new tempTest();
        System.out.println(t.doSome());

    }

    public String doSome(){

        try{
            System.out.println("try代码块中代码执行");
            return "返回";
        }finally {
            System.out.println("finally代码块中代码执行");
        }
    }

这边的输出是:

try代码块中代码执行
finally代码块中代码执行
返回

可以看出,确实是在finally代码块执行完后才return的

那么为什么会出现上面代码中的情况呢?

public class FinallyTest {
    public int method(){
        int x = 1;

        try{
            ++x;
            return x;
        }finally {
            x+=5;
        }
    }

    public static void main(String[] args) {
        FinallyTest test = new FinallyTest();
        System.out.println(test.method());
    }
}

这段代码输出是2

网上有这么一句话

大意就是如果在try中return的情况下,先把try中将要return的值先存到一个本地变量中,即本例中的x=2将会被保存下来。接下来去执行finally语句,最后返回的是存在本地变量中的值,即返回x=2.
还有一点要注意的,如果你在finally里也用了return语句,比如return ++x。那么程序返回值会是3。因为规范规定了,当try和finally里都有return时,会忽略try的return,而使用finally的return。

总结

finally块的语句在trycatch中的return语句执行之后返回之前执行,且finally里的修改语句可能影响也可能不影响trycatchreturn已经确定的返回值,若finally里也有return语句则覆盖trycatch中return语句直接返回。

posted @ 2022-07-22 17:15  YaosGHC  阅读(49)  评论(0)    收藏  举报