今天不忙,遂复习了一下语言基础,顺便编了一道题,专门针对Java语言基础,相信如果能够顺利理解小编下面出的这道题就能够在面试和笔试中对这方面问题发挥自如了。
废话不多说,先上代码,再进行分析。
public class MainClass {
public static void main(String[] args) {
// TODO Auto-generated method stub
Dog dog = new Dog(2); //坑1
dog.say();
}
}
abstract class Animal {
protected int age = 1; //坑2
public Animal() {
age += 10; //坑3
subIt();
}
{
age += 100; //坑4
}
abstract public void subIt();
}
class Dog extends Animal {
private int age = 0; //坑5
public Dog(int i) { //坑6
}
{
age += 1000; //坑7
}
public void say() {
System.out.println("final age is " + age);
}
public void subIt() {
// TODO Auto-generated method stub
age += 7; //坑8
}
}
这段代码跑完后输出几?
由于代码中的数字小编布置的还算“巧妙”,所以如果不是真正理解运行机制的很难蒙出正确答案。
OK下面开始进行分析,一个坑一个坑地躲:
首先从main方法开始看。坑1处的代码new了一个Dog对象,给了一个int入参:2,这样就会自然而然地引导到坑6处,这里是Dog这个子类的单参构造方法,但是父类Animal并没有单参的构造方法,于是就会在坑3处疑惑,于是就会对坑8处的代码是否会执行产生疑惑,坑8处操作的是age变量,而父类的坑2处也存在一个protected的名字叫age的变量,于是就会有可能被误导:通过父类构造方法导致的子类subIt()方法的执行那么到底操作的是哪个变量呢,而且代码块中的代码会不会执行以及在什么时候执行还记得那么清楚吗?其实这段代码最大的坑在坑5处,后文详细论述。
坑1处的参数2其实是个幌子,就是为了在new Dog这个对象的时候调用Dog的单参构造方法一个目的,而在坑6处并没有调用单参的super方法,而且Animal这个父类也没有单参的构造方法。实际上这种情况下,父类的空参构造方法会被默认调用的,所以坑3后面的subIt();方法是肯定能够被执行的,这也就意味着坑8处的代码也是同样会被执行到的。至于操作的age变量,的确,因为Animal类中的age是protected的,所以Dog的对象本确实能够访问到,但是因为Dog类自己private int age = 0; 了,这就类似于方法的重写,Dog中的age变量就如同把Animal中的age变量屏蔽了。所以本来Animal和Dog都能访问Animal中的age,现在变成了各自访问各自的age变量了,父类与子类互不干扰。
小坑躲的差不多了,下面就来看看坑5怎么躲吧。这里涉及到关键的一个知识点就是Java在这种情况下的代码执行顺序。也就是说,代码的执行顺序决定了最终的执行结果。
首先说一下Java中的代码执行顺序:(优先级从上至下逐渐降低)
父类静态变量 / 父类静态块 ("/" 符号连接的两个属于并级,谁在前面就先执行谁)
子类静态变量 / 子类静态块
父类代码块 / 父类变量
父类构造方法
子类代码块 / 子类变量
子类构造方法
有了这个概念之后就可以推断上面代码的执行顺序:
在坑1处new Dog对象,然后执行到坑2,使Animal的age为1,然后到坑4使Animal的age为101,然后到坑3使Animal的age为111(这里如果将Animal的age变量打印出来的话就会输出111),然后到坑8处,注意这里是最最关键的一步:这里会拿到Dog的private变量age,并把这个Dog的age变量赋值为7。到这里位置父类该执行的都执行完了,然后开始子类的执行流程:先执行坑5处的赋值语句,将Dog的age变量赋值成0(注意在此之前这个age可是7啊),然后执行坑7的语句,将Dog的age变量赋值成1000,然后执行到坑6处什么实质性的也没做。
到目前为止new Dog的动作执行完了,所以最后输出的应该是1000。
所以上面的关键还是在坑5处的赋值语句。如果那里改成private int age;的话,最后的输出结果就不是1000而是1007了。
==================================================================================================
好了这次的语言基础主要是围绕代码执行顺序展开的,之后小编会再出一篇讲述一下关于Java在运行时内存状态的基础题。这两篇的基础题共同构成了目前Android和Java面试中最最看重的语言基础。如果都掌握了,那面试和笔试的时候语言基础就能轻松过关了。