内容简介:记一道 Java 综合面试题
昨天看了一道关于异常处理的题: 由一道 Java finally 执行顺序的题引发的思考 ,今天又在牛客网看到一道更深的题,这次加上了一个子父类继承关系的知识点。
原题
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; } catch (Exception e) { System.out.println(e.toString()); } finally { this.setValue(value); System.out.println(value); } return value; } } static class B extends A { public B() { super(5); setValue(getValue() - 3); } public void setValue(int value) { super.setValue(2 * value); } } }
题解
这道题考的就是代码执行顺序,比较直观的方法是使用单步调试,来一起看下执行过程吧:
首先从 main 方法开始执行,new 了一个对象 B,没什么可说的。

接下来执行到了 B 类的构造方法,也没什么问题。

然后执行到了父类的有参构造。

这里要执行 setValue(v)
方法了,那么这里的 setValue()
方法应该是执行 B 类的,还是 A 类的呢?

其实这个结果是有些出乎我意料, 怎么会执行子类 B 的 setValue()
方法呢?
因为这里正在执行 B 的构造方法中,所以调用的方法就默认是 B 的,当 B 中没有,才会去找它的父类 A 中的方法。
下一步是 super.setValue()
,这里明确指明了调用父类的方法,所以应该没啥问题。

嗯,这里将 A 类的 value 属性设值为 10。

然后还回到 A 的构造方法中。

再回到 B 的构造方法中,接下来应该调用 getValue
。

由于子类中没有,所以调用父类的 getValue
方法。

接下来将 A 中的 value 自增 1,然后到了 return value
,由于下面还有 finally
语句块,所以先执行 finally
语句块,再返回。

又到了一个 setValue
方法,那么这里调用的是子类 B 的,还是父类 A 的呢?
可以看到,这里调用的是子类 B 的构造方法,还是刚才的那个结论:
因为这里正在执行 B 的构造方法中,所以调用的方法就默认是 B 的,当 B 中没有,才会去找它的父类 A 中的方法。
接下来应该会执行 super.setValue()
也就是它父类 A 的 setValue()
方法。

这里会将 A 的 value 值设为 22。

然后回到刚才的方法里,准备打印 value 值,打印后一共会回到 try 里的 return 语句。

这里虽然 value 的值是 22,但是刚才执行 finally 语句块之前就已经将 reutrn 的值确定了,也就是 11,详细参见 由一道 Java finally 执行顺序的题引发的思考 。

回到刚才的调用,由于上一步返回的值是 11,所以这里应该是调用的 setValue(11- 3);
,这里调用的是 B 的方法。

接下来又要执行 super.setValue()
了,也就是父类的 setValue()
方法。

这里会将 A 类的的 value 值从 22 改为 16.

然后 B 类的构造方法执行完了,回到 main 方法,准备调用 getValue()
方法,因为 B 类没有,所以会调用其父类 A 的。

这里将 A 的 value 值自增1,变为 17 ,然后最为 return 的最终返回结果后,执行 finally 语句中的内容。

这里又到了 setValue
的抉择,到底执行哪个类的呢?

还是调到了 B 类中,因为 main 方法里调用的是 new B().xxx
方法,所以这里的 this 代表的是 B 类。
接下来这里指定 super.setValue()
,调用父类的方法,将父类的 value * 2 ,然后回到 finally 块中。

然后调用输出语句,输出结果应为 34,输出后会返回到 try 里的 return 语句中。

那么这里的 return 值应该是刚才已经确定的值,也就是 17,然后回到 main 方法

这里返回的是 17,所以输出 17,程序结束。

总结
这道题很长,不过也只是靠两个知识点:
- 子类与父类之间的调用关系: 动态分派 在调用new B()时调用A的构造器时和super.getValue()时的setValue(int value)方法是根据隐式对象的实际类型来确定的。只有实际类型未重写该方法时,才按照继承层次由下往上查找。这个可以参阅《深入理解JVM》 的“分派”一节。
-
try-catch-finally
的执行顺序,详见: 由一道 Java finally 执行顺序的题引发的思考
理解了这两个知识点。这要脑子清醒,一步一步做,应该没什么问题。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
解决网页设计一定会遇到的210个问题
2006-4 / 42.00元
如何选择适合、简单、方便、快速的方法来解决您的网页设计问题?不会HTML、JavaScript、CSS也可轻易完成许多网页功能与特效。本书包含上百种HTML、JavaScript、CSS使用应用技巧与盲点解说,包含10个常用表单资料判断函数与特殊技巧,不必修改就可用于任何网页。本书现有的多数网页设计书籍相辅相成,让您事半功倍地完成工作。 许多计算机书籍都是从某个语言或者某个软件的......一起来看看 《解决网页设计一定会遇到的210个问题》 这本书的介绍吧!
RGB HSV 转换
RGB HSV 互转工具
HEX HSV 转换工具
HEX HSV 互换工具