题目概述
这是一道Java的笔试题,所考察的知识点有类的继承和try、finally、return的执行顺序。
题目源代码
package test;
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);
}
}
}
上述的代码结构较为简单,其目的是在主函数中实例化一个B的对象,并调用其的getValue()方法,同时将其返回值打印到console中。
运行结果
22
34
17
分析过程
本程序,主要有两个难点需要理解:第一,super(5)的具体如何执行;第二,在getValue()中的try return finally的执行顺序,由此影响的value值的变化和return的返回值为多少。
super(5)的执行:
显然,在B的构造器中,调用了父类A的构造器,继而调用了setValue()方法。此处的setValue()是省略了this关键字,而this表示的是当前对象,当子类调用父类的构造器,此处的对象为子类的对象,所以this代表的是一个B的对象,从而先在B中查找是否有重载的setValue()方法,若有则执行,无则向上查找。
当执行B的setValue()时,调用了父类的value成员变量,并利用super.setValue()给value进行第一次赋值。
getValue()的执行:
根据继承的方法调用的规则,不难理解getValue()调用了父类的方法,在getValue()中,try代码块执行的语句有value++,return value,finally执行的代码块为this.setValue(value),以及打印value的值。
首先,try执行到return时,会去查找有无finally代码块,若有则先执行finally,没有就继续执行return。当去执行finally的时候,return会把当前value的值暂存起来,等finally代码块的执行完毕,再去返回这个暂存(这是根据打印出的结果进行的一个猜测,在后面有个简单的证明) 的value的值。其等效于以下代码:
public int getValue() {
try {
value ++;
// return value;
int id1 = value;
return id1;
} finally {
this.setValue(value);
System.out.println(value);
}
}
所以,此处的所返回的value值是try代码块执行完毕的值。在finally中,调用this.setValue()方法,this指代当前对象,当前对象为B的一个对象,所以此处的setValue执行的是B中的方法,和A的构造器中的setValue()一样,此处也可以省略this,得到的结果是一样的。
代码中value值的变化过程
由上述的理解,可以得到下述的value值的变化
代码 | value |
---|---|
super(5) | 5*2=10 |
getValue():value++ | 10+1=11(return) |
getValue():this.setValue() | 11*2=22(first print) |
getValue() | 11 |
setValue(getValue()-3) | 2*(11-3)=16 |
new B().getValue():value++ | 16+1=17(return) |
new B().getValue():this.setValue() | 17*2=34(second print) |
new B().getValue() | 17(third print) |
try finally return对所操作的值的影响
正如上述所猜想的,return在返回的时候,会声明一个同类型的临时变量去接收return关键字后面表达式的值,并且等finally执行完毕后,再返回临时变量的值。
public int test(){
int number = 10;
try{
return ++number;
}
finally{
number+=2;
System.out.println("number:"+number);
}
}
此处console中会打印出13,但是test()的返回值为11。即下述代码等价。
//return ++number ;
int id1 = ++number;
return id1;
若将++number改为number++,即为如下代码:
public int test(){
int number = 10;
try{
return number++;
}
finally{
number+=2;
System.out.println("number:"+number);
}
}
此时,return暂存的是number的值,暂存完毕后,number再自增加。等效于下述代码:
//return number++ ;
int id1 = number++;
return id1;
此时,number会先赋值,再自增加。此处console中会打印出13,但是test()的返回值为10。
若return后表达式是Long,String等不可变类型,或者是引用类型,道理都是类似的。只不过,引用类型返回的是一个指针,若在finally中对于该指针指向地址空间的值有所改变,返回的引用所指向的地址中的值也会改变。