从很多java的书籍中我们都知道,一个类继承了另外一个类,要实例化子类,必须要先实例化父类。有了父亲,才可能有了儿子。但是,这个实例化大概是个怎样的一个过程呢?我们举几个例子。
(1)
public class Son extends Father{
private String s = "子成员变量";
public Son(){
}
@Override
public void message() {
System.out.println("儿子");
}
public static void main(String[] args) {
new Son();
}
}
class Father {
private String s = "父成员变量";
public Father(){
System.out.println(this.s);
this.message();
}
public void message() {
System.out.println("父亲");
}
}
也许有些初学者会觉得输出的是:
父成员变量
父亲的
包括我,以前不加思考,也觉得是这个答案。查看了一些资料,才知道,但实际上输出的是:
父成员变量
儿子
实际上,在Father类的构造函数中,this对象是Son而不是Father,我们用getClass().getName()方法就可以得知。但是,为什么同是this,既然this是指Son,那为什么this.s却输出的是"父成员变量"呢?而this.message()却调用的是Son的message()方法。这样子,给了我很大的不确定性啊。本来我父类想调用自己的方法,做一些初始化的事情,但子类一继承,我调的却不是自己的方法了,子类我也是未知的,我那里知道子类是谁,子类你覆盖了我的方法,做些我不可以接收的事那怎么办。多态,给我带来了不确定性。先不急,再看第二个例子。
(2)
public class Son extends Father{
private String s = "子成员变量";
public Son(){
}
@Override
public void message() {
System.out.println("儿子");
}
public static void main(String[] args) {
Son son = new Son();
Father father = son;
System.out.println(son.s);
System.out.println(father.s);
son.message();
father.message();
}
}
class Father {
private String s = "父成员变量";
public Father(){
}
public void message() {
System.out.println("父亲");
}
}
你没眼花,没错,输出的是:
子成员变量
父成员变量
儿子
儿子
而不是:
子成员变量
子成员变量
儿子
儿子
father和son不是指向都是同一个对象吗?对。既然指向的都是同一个对象,那怎么输出会不一样呢?我是这样理解,父类与子类的实例化是这样的,对于方法,如果子类没有覆盖父类的方法,那么子类就会有父类方法的一个拷贝。如果子类覆盖了父类的方法,就不用拷贝了。但对于成员变量,无论是子类有没有一样的成员变量,子类都会有父类的所有成员变量的一个拷贝。我们用debug就可以看到,当Son实例化时,是有两个s成员变量的。但是,对于成员变量的调用,是由引用类型决定的,就是当我个调用father.s时,实际调用的是从Father继承过来的s成员变量,当我们调用son.s的时候,实际调用的是Son自己定义的s成员变量。而对于方法却不一样,方法的调用,是由实际类型决定的,不管你引用类型是什么,是father,还是son,调用的都是实际对象中的方法,即new出来的对象方法。
由此,我们再回去看看(1)。在Father的构造方法中,this的实际对像是Son,所以当调用方法时,执行的是son方法。但是由于在Father的构造方法内,所以引用类型是Father,而调用成员变量时由引用类型决定,所以调用的是Father的成员变量。
对于我们的javabean,要调用成员变量,我们通常都提供get方法。所以,不管是引用类型是什么,调用得到的都是new出来的对象的成员变量,这样,就减弱了多态带来的不确定性。
这是我的一些理解,如果有不对的地方,大家指正。