在之前讲到java面向对象继承的时候,我们只讲到了两个比较重要的知识点,一个是父子类当中有同名的成员变量,这个时候,我们引入了super这个关键字来区分这两个同名成员变量,除此之外,我们还讲到了父子同名函数的覆盖,这父子同名函数必须是参数类型、个数相同,返回值也相同才可以,同时子类要覆盖后的成员方法的访问权限要大于等于父类当中的同名函数权限。

那么还有一个地方我们没有讲到,那就是父子当中的构造函数是怎样的?

class Fu
{ Fu()
{ System.out.println("Fu chu shihua "); } } class Zi extends Fu
{ Zi()
{ System.out.println("Zi lei chushi hua "); } } class Extend1
{ public static void main(String[] args) { new Zi(); } }

此时我们编译,运行后结果是:

通过这个结果我们可以看出,即使我们初始化子类的时候,并没有显式的调用父类的构造函数,但是子类当中还是隐式的调用了父类的构造函数。那么把这个隐式调用转换成显式的是如何的呢?在我们子类的构造函数在JVM解析的时候默认加了一个super(),这个函数。这个函数是不是跟我们之前讲到构造函数的时候的this()调用本地构造方法很相似呢?

class Zi extends Fu
{ Zi()
{
super();//这里就隐式调用了父类的构造函数
System.out.println("Zi lei chushi hua "); } }

那么这里的隐式调用有一个不足,什么不足呢,如果我们此时父类的构造函数接受的参数不是空,这个时候在隐式调用的时候就会出错。因为隐式调用super()并没有接收参数,跟父类的构造函数不匹配,自然而然的会报错。

那么在这里还要讨论一下为什么要有super这个关键字,为什么子类的构造函数会隐式的调用父类当中的调用方法呢?

比如现在父类当中有一个成员变量,当我们要使用这个成员变量的时候,构造函数会对其进行初始化,如果此时我们如果调用未初始化的成员变量,那么此时变量就是在堆中默认的变量,这样显然是不合适的,调用一个未初始化的变量,毫无意义可言。

也就是说子类继承了父类中的内容(属性)在子类使用父类的属性之前,必须了解父类是如何对其属性进行初始化操作的。为完成这个初始化操作,子类必须在构造函数当中访问这个父类的构造函数super()。

class Fu
{ int num;
Fu( int x)
{
num = x;
System.out.println("Fu chu shihua "); } } class Zi extends Fu
{ Zi()
{
super(8);
System.out.println("Zi lei chushi hua "+num); } } class Extend1
{ public static void main(String[] args) { new Zi(); } }

比如这里如果我们不调用super()这个方法的时候,就会显得没有意义可言。

这里还有需要注意的一点就是,如果构造函数当中既有this() 还有 super()这两个构造函数的时候,我们应当如何处理。显然这两个方法不能存在一个构造函数体当中,因为这个两个构造方法,必须放到构造函数的第一个位置。既然调用this()就证明此时我们有多个构造函数对于一个类,这个时候我们可以把super()放到别的不用this()这个构造方法的类当中,代码如下:

class Fu
{ int num;
Fu( int x)
{
num = x;
System.out.println("Fu chu shihua "); } } class Zi extends Fu
{ Zi(int x)
{
super(9);
System.out.println("This is first"); } Zi()
{
this(9);
System.out.println("Zi lei chushi hua "+num); } } class Extend1
{ public static void main(String[] args) { new Zi(); } }

Java当中的所有类都默认继承字object类,并且这个类在java虚拟机一运行的时候,就存在着。

那么java在调用构造函数时的内存图是怎样的呢?我们来画一下:

class Fu
{ Fu()
{ show(); } void show()
{ System.out.println("Fu class construct"); } } class Zi extends Fu
{ int num = 10; Zi()
{ super(); } void show()
{ System.out.println("Zi lei construct"+num); } } class Extend1
{ public static void main(String[] args) { new Zi(); } }

我们编译运行之后,结果就是:

我们来修改一下代码如下:

class Fu
{ Fu()
{ show(); } void show()
{ System.out.println("Fu class construct"); } } class Zi extends Fu
{ int num = 10; Zi()
{ super(); } void show()
{ System.out.println("Zi lei construct"+num); } } class Extend1
{ public static void main(String[] args) { Zi z = new Zi();
z.show();
} }

这个时候输出的结果是:

那么综合以上两个结果还有代码不同外,我们会有下面两个问题:

1、我们在只new Zi()的时候,为什么num是0

2、在我们调用show()方法的时候,为什么父类反而调用了子类的成员方法?

对于第二个问题,我们上一节讲到方法覆盖的时候就已经讲到过了,当子类的方法和父类的方法相同时,调用的是子类的方法。

对于第一个问题,我们用一个内存图解一下:

以上就是父子类构造方法调用的内存示意图。

需要说明的有两点:

1、通过super()初始化父类的时候,子类的成员变量并未初始化。等父类初始化完毕,才进行子类的成员变量显示初始化。

一个对象的实例化过程:

一、JVM读取指定目录下的.class文件,并且加载进内存。并且先加载此类的父类(在有直接父类的情况下)

二、在堆内存当中开辟内存,分配地址。

三、并在对象的空间内,对空间属性进行默认初始化

四、调用对应的构造函数进行初始化

五、在构造函数当中,第一行会先调用父类的构造函数进行初始化。

六、父类初始化完毕后,再对子类的属性进行显式初始化

七、再对子类的构造函数进行特定初始化

八、初始化完毕后,把地址赋值给引用变量。

待续....

java学习面向对象之父子构造函数初始化的更多相关文章

  1. java学习面向对象构造函数

    在java当中目前我们学到的一个比较特殊的函数就是main函数,他是JVM执行的入口,所以书写的格式是固定的,现在我们来介绍java中另一个比较特殊的函数: 构造函数:构造对象的时候调用的函数,作用, ...

  2. Java学习---面向对象的远程方法调用[RMI]

    基础知识 分布式计算是一门计算机科学,它研究如何把一个需要非常巨大的计算能力才能解决的问题分成许多小的部分,然后把这些部分分配给许多计算机进行处理,最后把这些计算结果综合起来得到最终的结果. 常见的分 ...

  3. java学习面向对象之异常之一

    一.异常的概述: 什么是异常?在我们编写java程序的时候,会出现一些问题,比如内存溢出啊或者数组索引超出最大索引啊,这些编程当中出现的这些个问题就是异常.但是异常也分为可以处理的和不可以处理的.比如 ...

  4. Java学习个人备忘录之构造函数&this

    构造函数 概念:构建创造对象时调用的函数. 作用:可以给对象进行初始化,创建对象都必须要通过构造函数初始化. 一个类中如果没有定义过构造函数,那么该类中会有一个默认的空参数构造函数.如果在类中定义了指 ...

  5. Java学习 面向对象(下)——Java疯狂讲义th4

    面向对象(下) [TOC] 包装类 通过包装类可以把8个基本类型的值包装成对象使用. 自动拆箱.自动装箱 把字符串类型值转换成基本类型的值: 包装类的 parseXxx(String s)静态方法 包 ...

  6. java学习面向对象之多态

    如何理解多态,让我们举个例子来描述一下,因为单纯的说多态大家可能不理解: abstract class Animal { ; abstract void eat(); public void run( ...

  7. java学习面向对象之接口

    上一节当中我们说道抽象类,抽象类当中的方法可以是抽象的也可以是非抽象的,那么当抽象类中所有方法都是抽象的时候,我们就可以把它重新定义为接口.代码示例: abstract class Animal { ...

  8. java学习面向对象之this

    在我们讲构造函数的时候,我们知道,如果同时在java的堆内存当中,同时存在好几个刚进内存,但是又没来得及初始化的同一个类的对象.在这种情况下,那么如何去区分栈内存当中的构造函数是属于那个对象的呢,其实 ...

  9. java学习--面向对象

    对象及类的概念 对象是java程序的核心,在java程序中“万事万物皆对象” 对象可以看成是属性和方法的封装体 类是用来创建同一类型的对象的模板,在一个类中定义了该类对象所应具有的属性和方法 J2SD ...

随机推荐

  1. centos emacs安装

    wget http://mirrors.ustc.edu.cn/gnu/emacs/emacs-24.5.tar.gz tar zxvf emacs-24.5.tar.gz cd emacs-24.5 ...

  2. python socket 模拟tcp通讯

    对于tcp server 端的创建而言, 分为如下步骤:   1,创建socket对象(socket):其中俩个参数分别为     Address Family(如AF_INET为ipv4),AF_I ...

  3. 项目打包 weblogic部署

    工作流打包:   由于没有集成单点,配置文件要修改 将webnocas.xml内容复制覆盖 web.xml   到这里修改完毕 选择weblogic项目,右键maven install,生成.ear文 ...

  4. 程序员带你十天快速入门Python,玩转电脑软件开发(二)

    关注今日头条-做全栈攻城狮,学代码也要读书,爱全栈,更爱生活.提供程序员技术及生活指导干货. 如果你真想学习,请评论学过的每篇文章,记录学习的痕迹. 请把所有教程文章中所提及的代码,最少敲写三遍,达到 ...

  5. Unity3D 获得GameObject组件的方法

    Unity3D 获得GameObject组件的方法有几种,这里进行说明一下: 组件: 要获得这些组件,进行操作的话,绑定一个Movescipt 的C#组件,里面的获取方法为 void Update ( ...

  6. 时间格式nls_date_format的设置

    nls_date_format参数用于设置日期显示格式,设置的方式有多种,不同的方式也会带来不同的结果.参数的设置是有优先级的,日期格式的参数设置也不例外.优先级如下(低到高):初始化参数 < ...

  7. PC110302/UVA10010

    下周开始就省选了,ACM的日子在今年内应该就会结束了,大三了,最后一次机会了,小小感伤一下-- 今天广州下大雨,心情怪怪的,感觉码不出质量高的,又很久没做过PC了,就刷刷水题吧. 老实说Program ...

  8. sql知识

    SQL 基本知识 SQL Server 是Microsoft 公司推出的关系型数据库管理系统.具有使用方便可伸缩性好与相关软件集成程度高等优点,可跨越从运行Microsoft Windows 98 的 ...

  9. HTML5储存

    1.sessionStorage 特点:关闭浏览器(或标签页)后数据就不存在了.但刷新页面或使用“前进”.“后退按钮”后sessionStorage仍然存在: sessionStorage每个窗口的值 ...

  10. 使用PHP脚本来写Daemon程序

    什么是Daemon进程   这又是一个有趣的概念,daemon在英语中是"精灵"的意思,就像我们经常在迪斯尼动画里见到的那些,有些会飞,有些不会,经常围着动画片的主人公转来转去,啰 ...