在之前讲到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. FreeBSD系统更新与软件安装方法

    一.系统更新 freebsd-update fetch freebsd-update install 二.软件源更新(类似yum update.apt-get update) 1.取回源 portsn ...

  2. Python获取web页面信息

    import sys, urllib2 # req = urllib2.Request(sys.argv[1]) req = urllib2.Request('http://www.sina.com. ...

  3. Factory Method 工厂方法模式

    Factory method工厂方法模式是一种实现了“工厂”概念的面向对象设计模式.就像其他创建型模式一样,它也是处理在不指定对象具体类型的情况下创建对象的问题.工厂方法模式的实质是“定义一个创建对象 ...

  4. 3DES加解密【示例】

    代码 /**  * 3DES加解密  */ public class DESedeUtils {     private static final String ALGORITHM_MD5 = &qu ...

  5. Eclipse项目 迁移到 Intellj IDEA

    自从用了Intellj IDEA,很多项目都想迁移到Intellj上面去开发  鉴于我们的大部分项目都是基于Maven构建的,所以就可以利用maven的命令来做这个事情.     1.选择一个ecli ...

  6. google code 上传源码

    在使用google code 的时候 做个备份, git clone https://wushuangzilong@code.google.com/p/maplebanana-proxy/ git c ...

  7. JS控制文字一个一个出现

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  8. zoj1276矩阵连乘dp

    很经典的入门dp /*******************************************************************************/ /* OS : 3 ...

  9. 对N个数组进行操作。先把这N个一维数组合并成一个2为数组;然后进行操作

    using System;using System.Collections.Generic;using System.Linq;using System.Collections;using Syste ...

  10. jquery ajax 后台响应成功,返回正确json但不执行success方法,执行error的问题

    昨天被这问题卡了好几个小时.查看http状态码:是200.而且返回了预想的json字符串.但执行的是error方法,不执行success方法.在网上查了一下,才发现是后台页面返回的json字符串格式不 ...