public class Father {
    private String name;
    public Father() {
        System.out.println("调用了Father的构造器-------this指针的值为:" + this);// ③
        System.out.println("调用了Father的构造器-------this.getClass()的值为:" + this.getClass());// ④
         = "父亲";// ⑤
    }
  //省略get/set方法……
}

 

public class Children extends Father {
    private int age;
    public Children() {
        super();// ②
        System.out.println("调用了Children的构造器-------this指针的值为:" + this);// ⑥
        this.age = 1;//⑦
    }
//省略get/set方法……
}

 

public class Test {
    public static void main(String[] args) {
        // 创建Children的构造器创建实例
        Children children = new Children();// ①
System.out.println("name:"+children.getName());//⑧
⑨
    }
}

准备知识:

1、首先需要澄清一个概念:java对象是由构造器创建的吗?

很多书籍、资料中会说,是的。但是实际情况是:构造器只是负责对java对象实例变量执行初始化(也就是赋初始值),在执行构造器代码之前,该对象所占的内存已经被分配下来,这些内存里值都默认是空值——对于基本类型的变量,默认的空值就是0或false,对于引用类型的变量默认的空值就是null。

 

2、在处理java类中的成员变量时,并不是采用运行时绑定,而是一般意义上的静态绑定。必须明确,运行时(动态)绑定针对的范畴只是对象的方法。(方法的话只有static和final(所有private默认是final的)是静态绑定的.)

 

3、当创建任何java对象时,程序总会先一次调用每个类非静态初始化块、父类构造器(总是从Object开始)执行初始化,最后才调用本类的非静态初始化块、构造器执行初始化。

 

此时当程序执行到①时,系统会先为父类中的私有属性在堆内存开辟空间

注意:这里并不会实例化父类对象,仅仅是为父类中的属性在堆中开辟了一段内存空间。

然后在为子类Children在堆内存空间,此时调用父类的构造方法(不写super();的话会隐式的调用),此时通过准备知识1中我们已经知道构造方法仅仅只是负责对java对象实例变量执行初始化,而不会实例化父类。

该结论可以通过Father类的构造器中输出的this和this.getClass()结果得到佐证。

输出结果如下:

ather类构造器中的this指向的实际是子类对象的实例。这里说明子类实例化的确不会创建父类的对象,因为如果创建了父类对象的话,那么父类对象构造器中的this指向的地址肯定就不可能是子类对象的实例了。

这时我们有可能产生一个疑惑,那就是:既然不创建父类对象的实例,那么子类中是怎么拥有(这个说法可能不够严谨,但为了方便理解不做其他讨论)父类中的属性的?

准备知识2来说明了,在编译时java类的成员变量采用静态绑定,即成员变量被关联到了类上。此时虽然父类构造器中的this指向的是子类对象的实例,但是虚拟机在子类对象中找不到name属性的时候,就到包含该构造方法中的类去找静态绑定相应的name属性的,并为其赋值为“父亲”。所以⑧中输出的结果为“父亲”。

 

上面是我对子类对象创建过程中是否实例化父类对象的理解,如果有什么不对的地方欢迎斧正!