在之前讲到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. Android - Binder驱动

      以下资料摘录整理自老罗的Android之旅博客,是对老罗的博客关于Android底层原理的一个抽象的知识概括总结(如有错误欢迎指出)(侵删): http://blog.csdn.net/luosh ...

  2. 各种vpn协议介绍(重点介绍sslvpn的实现方式openvpn)

    vpn介绍:   VIrtual Private Network 虚拟专用网络哪些用户会用vpn?    公司的远程用户(出差.家里),公司的分支机构.idc机房.企业间.FQ常见vpn协议有哪些?  ...

  3. SQL Server中建立外键的方法

    在SQL中建立外键约束,可以级联查询表中的数据,在C#代码生成器中,也能根据外键关系生成相应的外键表数据模型.外键也可防止删除有外键关系的记录,一定程度上保护了数据的安全性. 步骤: 1.要建立外键关 ...

  4. LCA问题

    基本概念 LCA:树上的最近公共祖先,对于有根树T的两个结点u.v,最近公共祖先LCA(T,u,v)表示一个结点x,满足x是u.v的祖先且x的深度尽可能大. RMQ:区间最小值查询问题.对于长度为n的 ...

  5. 在sql2008的实例 中 编写存储过程 读取 版本为sql2005 的实例 中的某个数据库里的数据

     --创建链接服务器 exec sp_addlinkedserver   'ITSV ', ' ', 'SQLOLEDB ', '远程服务器名或ip地址 ' exec sp_addlinkedsrvl ...

  6. struts 2读书笔记-----struts2的开发流程

    一.将struts 2的lib文件夹下地commons-fileupload.jar.commons-io.jar.freemarker.jar.javassist.jar.ognl.jar.stru ...

  7. Context是什么,怎么用

    一.Context是什么 开始学安卓的时候发现经常有context,但是都不知道为什么,什么时候需要它. 官方文档概述:关于应用程序环境的全局信息的接口.这是一个抽象类,它的实现是由安卓系统提供的.它 ...

  8. Windows环境下使用Cmake ndk编译fdk-aac

     一.废话 最近学习,第一步就是编译.我们需要编译FFmpag,x264,fdk_aac,下面是x264,网上说的很多都是几百年前的,我亲测完美可用 还是那句话 我能力有限,但是我希望我写的东西能够让 ...

  9. Sql产生自动增长的编号

    USE [DBName]GO/****** Object:  StoredProcedure [dbo].[sp_GetNo]    Script Date: 10/24/2013 19:26:44 ...

  10. Could not parse mapping document from resource cn/spt/model/Student.hbm.xml

    初始hibernate, 写第一个程序 helloworld的错误: Exception in thread "main" org.hibernate.InvalidMapping ...