在
java老生常谈问题之RTTI
中,我曾经讨论过关于子类和父类之间成员函数的运行时识别的问题,今天要讨论的是成员属性的运行时识别问题。在我们的印象中java是具有覆盖特性的,但是要注意这个特性仅仅是对成员函数的,对于成员属性可不适用。下面就来小试身手。
package com.whyun.test.rtti;
public class ParentClass {
public String property = "parent";
public ParentClass() {
show();
}
public void show() {
System.out.println("property in parent:"+property);
}
}
代码片段1:父类代码
package com.whyun.test.rtti;
public class SubClass extends ParentClass {
public String property = "sub";
public SubClass() {
super();
show();
}
// public void show() {
// System.out.println("property in sub class:"+property);
// }
public static void main(String[] argc) {
new SubClass();
}
}
代码片段2:子类代码
运行一下SubClass这个类,将会输出一下内容:
property in parent:parent
property in parent:parent
也就是说父类方法中的show方法,只会使用父类中的property属性。
接下来,将SubClass中的show方法的注释打开。然后在看一下SubClass的构造函数,构造函数第二行调用show的时候,调用的肯定是他本身的show成员方法;构造函数第一行调用的父类的构造函数,虽然父类的构造函数也是调用的show函数,但是根据RTTI的规则,运行时肯定是调用SubClass中的show成员方法。最终打印的结果如下:
property in sub class:null
property in sub class:sub
由于调用的是SubClass中的show方法,所以打印的字符串的前缀肯定是property in sub class,但是为什么第一打印的时候SubClass的property属性。这个问题可以通过在eclipse中单步跟踪调试来解决。在new SubClass();这行添加断点,然后发现代码首先调用父类构造函数,在调用父类(ParentClass)的构造函数的时候,会先调用ParentClass的父类Object的构造函数,之后初始化ParentClass的成员属性property;然后调用父类构造函数中的show方法,但是这时SubClass的property属性还没有初始化,所以打印出来为null;经过以上操作后ParentClass的构造函数调用完成(也就是super();这句代码执行完成),接着是初始化SubClass的property属性,初始化完成后,最后调用SubClass构造函数中的show();这句代码,由于SubClass的property属性已经初始化,所以将会打印出它的值。
经过以上分析发现,想通过书写同名的成员属性,让子类在运行中覆盖父类的属性,是行不通的,看来需要一种变通的方法。接着看一下代码:
package com.whyun.test.rtti;
public class ParentClass {
private String property = "parent";
public ParentClass() {
show();
}
public ParentClass(String property) {
this.property = property;
show();
}
public void show() {
System.out.println("property in parent:"+property);
}
}
代码片段3:修正后的父类代码
package com.whyun.test.rtti;
public class SubClass extends ParentClass {
private static final String property = "sub";
public SubClass() {
super(property);
}
public SubClass(String myProperty) {
super(myProperty);
}
public static void main(String[] argc) {
new SubClass();
}
}
代码片段4:修正后的子类代码
最后运行SubClass类,输出:
property in parent:sub
我们复用了父类的代码,却输出了子类的属性,其实代码不难理解,就是通过调用父类带参数的构造函数,就其属性在构造时动态修改掉,这样使用property这个属性的时候,就是用的子类中的值了。