public class Base
{
private String baseName = "base";
public Base()
{
callName();
}
public void callName()
{
System. out. println(baseName);
}
static class Sub extends Base
{
private String baseName = "sub";
public void callName()
{
System. out. println (baseName) ;
}
}
public static void main(String[] args)
{
Base b = new Sub();
}
}
答案输出:null
为什么?为什么会输出null?
我们先体会一下上边程序的执行流程:
①执行main函数中的:Base b = new Sub();
②进入Sub类的构造函数
③为Sub类的成员变量baseName分配内存,注意此时仅仅是分配内存,所以baseName的值 为Java中定义String类型的成员变量默认值null,(并不是我们所想的“sub”);
④Base构造函数被隐式调用
⑤进入Base类的构造函数
⑥为Base类的成员变量baseName分配内存,注意此时也是仅仅分配内存,这里Base类中的 baseName的值也为 null
⑦执行Base类的初始化语句:private String baseName = "base"; //此时Base类中的baseName的值为base;
⑧执行Base类中的构造函数中的具体语句
⑨调用callName()方法,注意:这里的涉及到“多态”的特性(父类可以创建指向子类的对象)。因为子类Sub中重写(Override)了父类中的callName()方法,所以说父类中的callName()方法被隐藏,因此这里的callName()方法其实就是this.callName();
⑩输出Sub类中那个只分配了内存空间而没有赋值的baseName ----->即 null
这里挖坑就是挖在第③步:我们的错误就在于我们把Java中的声明和初始化看成了一体。在C++的世界中,C++并不支持成员变量在声明的时候进行初始化,其需要你在构造函数中显式的初始化其成员变量的值,看起来很土,但其实C++用心良苦。
举个栗子:
public class A {
private String a = "aaa";
// public A() {
// super();
// // TODO Auto-generated constructor stub
// }
}
①进入A类中的构造器(如果在一个类中没有显式声明构造方法,则默认隐式调用无参的构造方法,即那个注释了的就是被隐式 调用的无参构造方法)。
②声明一个String类型的私有成员变量,名字叫做 a
③初始化 a = "aaa";
④继续执行构造方法剩余的语句,如果没有,则顺序执行类中的语句。