2009年11月27日星期五

[原创][精品]一道Java题

在网上看到一道题目,解答之后对类的重写又有了新一步认识,在此分享解答及经验。

代码如下:

/**
 * 父类
 * 
@author rongxinhua
 *
 
*/
public class Father{
    
private String name="FATHER";
    
public Father(){
        whoAmI();
        tellName(name);
    }
    
public void whoAmI(){
        System.out.println(
"Father says, I am " + name);
    }
    
public void tellName(String name){
        System.out.println(
"Father's name is " + name);
    }
}

/**
 * 子类
 * 
@author rongxinhua
 *
 
*/
public class Son extends Father{
    
private String name="SON";
    
public Son(){
        whoAmI();
        tellName(name);
    }
    
public void whoAmI(){
        System.out.println(
"Son says, I am " + name);
    }
    
public void tellName(String name){
        System.out.println(
"Son's name is " + name);
    }
}
问题:当执行 Father who = new Son(); 时,会输出什么?

结果为:

Son says, I am null
Son's name is FATHER
Son says, I am SON
Son's name is SON

 下面两个打印结果很容易想到。但是前面两个就有点摸不着头脑。我一开始也难以理解,主要是考虑的方向错误,误以为是与初始化顺序有关的题目。但其实是一道关于”重写“的题目。

解答:
Son的两个方法whoAmI和tellName重写了父类的方法,这一点是毋庸置疑的。这里有一点:重写是静态的,重写的过程在编译时就完成了,而重载则是动态的(延迟绑定)。所以当执行 Father who = new Son(); 时,实际的执行顺序如下

1. Son的whoAmI()方法:System.out.println("Father says, I am " + name);
2. Son的tellName(String name)方法:System.out.println("Son's name is " + name);
3. Son的whoAmI()方法:System.out.println("Father says, I am " + name);
4. Son的tellName(String name)方法:System.out.println("Son's name is " + name);
看起来有点奇怪,这不都一样吗?的确差不多。但是name不同!3与4的name是Son德name属性(这很容易理解)。
2的name是方法参数传过来的,而且是在父类的构造函数中执行的,那么name的值应该是父类的name属性。可能有点不理解,那么在父类的构造函数中加上一句
        System.out.println("Father says, I am " + name);
得到的结果是”Father says, I am FATHER“。所以name属性在父类的构造方法中的确为”Father“且传给了被重写的tellName方法。
1的name值又为什么为null呢?因为whoAmI方法中的name值直接来自类的实例变量。或许你认为Father的name值应该传给它,不就打印出Son's name is FATHER吗?其实不是。虽然变量名都是name,但是这两个变量是不同的。在编译成字节码的时候这两个变量就区分出来了。Java中方法可以重写,但类的属性不行。对此你可以把name的访问权限都改成public结果仍旧不变。还不理解,那就把Father的name属性名改为name_f,并且在子类的whoAmI方法中加入如下代码:

super.whoAmI();

得到的结果为:

Son says, I am null
Son's name is FATHER
Father says, I am FATHER
Son says, I am SON
Son's name is SON
可见父类的whoAmI方法仍旧可以正常打印结果。这样理解就方便多了!

结论:从这到题可以知道几点:

Java的重写是在编译成字节码时执行的。
Java的方法可以重写但是变量不行。
父类的变量会被保留,如果此变量的权限不为private,则可以通过super来访问。
被重写的方法中引用的实例变量是子类的变量,即使与父类变量名重复,也不会在父类的执行环境中(如构造函数)使用父类变量。
被重写的父类方法可以用super访问。

没有评论: