发现一道有趣的试题,可以加深对多态实例化子类调用方法的理解,如下:
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);
}
}
}
该题主要考的是对面向对象多态的理解,本题中最主要的是遵循一个原则:调用方法都是实例化后的子类的重写方法,除非明确指定super.xx或者子类没有该方法时,才会调用父类的同名方法。
在这道题中,首先构造了一个new B()的B类实例,进入B类中,构造器中super(5)调用父类带参构造器,执行setValue(v);,注意!这里的调用者为B类【遵循调用方法都是实例化后的子类的重写方法原则】,进入B类的setValue方法,B类该方法中明确指定调用父类的方法:super.setValue(2 * value) => 2*5=10传入进去,此时父类value值为10。
紧接着,B类继续执行setValue(getValue()- 3);,首先执行getValue()方法,在该方法中:
1.首先value++,即10+1=11,但因为try...finally是在方法体中,后面还有finally,要等finally执行完后,才会return返回该值(finally执行操作对value值的改变不会影响到return返回的value值)。
2.在finally中,调用了setValue方法,注意!这里的调用者仍然是B类【遵循调用方法都是实例化后的子类的重写方法原则,并且这里使用了this关键字,指定了调用当前对象的方法】。
3.子类的setValue方法中,同上,明确指定调用父类的方法 => 11*2=22,即父类的value值为22。
4.因此,finally这里输出22,然后return执行,返回value值11(给到方法,同时父类A的value值也为11)。
执行完getValue方法,回过头继续执行setValue(getValue()- 3),通过上面得知,getValue的返回值为11,即11-3=8,再调用B类的setValue方法 => 2*8=16传入父类的setValue方法中,此时父类的value值为16.
接下来执行new B().getValue()中的getValue()方法,由于B类没有getValue()方法,因此进入父类的该方法中:
- value值为16
- 进入getValue方法后,首先执行value++,即16+1=17,等待finally执行完后return返回该值
- 在finally中,调用setValue()方法,调用者依然为B类对象【遵循调用方法都是实例化后的子类的重写方法原则,并且这里使用了this关键字,指定了调用当前对象的方法】
- 进入B类的setValue()中,同上,明确指定调用父类的方法 => 17*2=34,即父类的value值为34,因此,finally这里再次输出34,return value值为17。
最后,System.out.println(new B().getValue());会将getValue()的返回值打印出来,即输出17.
最终结果输出:22 34 17