Test.main() 函数执行后的输出是()
- 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);
- }
- }
- }
首先,super()函数指的是调用父类的构造方法
①
new B()
执行B的构造函数,第一行是super(5);
此时执行的是A的构造函数,A的构造函数调用的是setValue()方法,由于B重写了A的这个方法,
所以!!!执行的是B的 setValue()方法。
即传入的参数是2*5=10
此时,因为super,所以调用的是父类的 setValue()方法,即value=10
第一行执行完毕。
第二行是 setValue(getValue()-3);
B没有getValue()方法,故执行父类的此方法,
try返回的是value=10+1=11,保存在临时栈中
finally中调用this的方法,这个this指的是B的对象,又重写,故就是B的 setValue()方法
value=2*11=22,第一个打印到屏幕上的数字
接下来参数 getValue()-3=11-3=8
传入B的 setValue()方法
此时value=2*8=16
至此,new B()执行结束
②
new B(). getValue()
B没有 getValue(),故执行A的 getValue()
try返回16+1=17,保存到临时栈中
finally调用B的 setValue()方法
value=17*2=34,第二个打印到屏幕上面的数字
最后主函数打印返回值,也就是try保存到临时栈的17
这题有两个考点:
- 动态分派 在调用new B()时调用A的构造器时和super.getValue()时的setValue(int value)方法是根据隐式对象的实际类型来确定的。只有实际类型未重写该方法时,才按照继承层次由下往上查找。这个可以参阅《深入理解JVM》的“分派”一节。
- finally块中的代码具体执行逻辑。这个可以通过javap查看字节码进行理解。详细可以参阅《深入理解JVM》和《JLS8》(JAVA虚拟机规范8)。
以字节码的角度看,最后返回的是前面已经保存在局部变量表中的value值。以代码的角度来看,finally块中的代码一定会执行,但前面return语句已经求得了value这个表达式的值,这个值不会因为finally块中value的改变而改变。当然,最好还是以字节码的角度去理解返回值为什么是前面求得的value值而不是在finally块中更新后的value值。Code: stack=3, locals=3, args_size=1 0: aload_0 //将this引用入栈 1: dup //复制栈顶元素 2: getfield #3 //消耗一个this引用,获取其value字段,入栈 5: iconst_1 //int常量1入栈 6: iadd //将栈顶两元素相加(将消耗掉)并入栈 7: putfield #3 //取出栈顶元素存入value字段中 10: aload_0 //将this引用入栈 11: getfield #3 //消耗一个this引用,获取其value字段,入栈 14: istore_1 //取出value值存入局部变量表中(位置0为this引用) 15: aload_0 //将this引用入栈 16: aload_0 //将this引用入栈 17: getfield #3 //消耗一个this引用,获取其value字段,入栈 20: invokevirtual #2 //调用实例方法setValue(消耗掉value和this引用) 23: getstatic #4 //获取System.out静态域,入栈 26: aload_0 //将this引用入栈 27: getfield #3 //消耗一个this引用,获取其value字段,入栈 30: invokevirtual #5 //调用实例方法println(消耗掉value和System.out引用) 33: iload_1 //将局部变量表中第14步存入的value值取出,入栈 34: ireturn //返回栈顶的数据 ...
主要一点就是从头到尾都是子类对象在调用所有方法,所以除非显示写super,不然使用的方法都应该为子类重写过后的。所以第一个就应该(5*2+1)*2等于22。且注意,除非finally块中再写return,否则之前的return变量的值会被缓存,再修改变量不会改变return的值。
其实关键问题在于try中的return值是在Try模块内的程序运行完之后就会保存下来,Finally模块中的程序不会影响到return的返回值,所以才会导致第一次输出的是22,但是return返回的却是11.
1、学习,关于函数调用谁的问题,正在执行的父类的构造函数,则在构造函数里面调用父类的函数
2、如果现在正在执行子类的构造函数,则构造函数里面调用的是子类的函数
3、this指正,最开始这个方法从哪个类发起,则this指正就是指向的最开始的那个对象
1.子类继承父类,调用方法时(如果没有显式声明super,则默认是this)先是调用子类中的方法,如果没有就调用父类中的方法。即以下几种情况:
super.a() ——调用父类的a方法
this.a() ——调用本类的a方法,如果本类没有该方法,应该会报错
a() ——相当于默认this,但是会先调用本类的方法,如果没有再去找父类中的方法来调用,如果父类找不到,则编译报错
***原题中的方法调用都是在B类中进行,所以B就是this,A就是super。
2.还有一点就是try{ }、catch{ }、finally{ }返回值的问题。
try{ }中返回了某一个值,如果finally也有return,则finally中的返回值会覆盖try的返回值;如果finally没有返回值,则return try{}中缓存的返回值。
这题需要注意
1、除非调用super.setValue()否则都是调用子类的setValue(),
2、调用getValue()返回的值是value++后的值(已经存入returnValue, 等待finally块执行完成后返回给上一级)
3、finally块会修改value的值,最终的成员变量value值是finally块运算结果。