java程序员应该知道的对象实例化顺序

    可能学过java的人都应该知道类的继承,子类总是先执行父类的构造方法,然后再执行本类的构造方法来实例化对象,下面我们先来看一个例子来体现这一规则:

1class Test 2{ 3 public static void main(String[] args) 4 { 5 B b = new B(); 6 } 7} 8// 父类 9class A 10{ 11 public A() 12 { 13 System.out.println("父类的构造函数..."); 14 } 15} 16// 子类 17class B extends A 18{ 19 public B() 20 { 21 System.out.println("本类的构造函数..."); 22 } 23}

大家一定会想,这么简单的一个程序算什么呢,大家应该都能分析出来答案了

1父类的构造函数... 2本类的构造函数...

这就反映了先父类的构造->本类的构造的执行顺序;

那现在大家再用这个思路来分析这个看似简单,实际暗藏了些许陷阱的几十行代码:

1class Test 2{ 3    public static void main(String[] args) 4    { 5        A a = new B(); 6    } 7} 8 9class A 10{ 11    int age = 11; 12    { 13        System.out.print(age + " "); 14        age = 22; 15        System.out.print(1 + " "); 16    } 17 18    public A() 19    { 20        System.out.print(2 + " "); 21        printAge(); 22    } 23 24    public void printAge() 25    { 26        System.out.print(this.age + " "); 27    } 28} 29 30class B extends A 31{ 32    int age = 3;// {}初始化块 33    { 34        System.out.print(3 + " "); 35    } 36 37    public B() 38    { 39        System.out.print(4 + " "); 40        printAge(); 41    } 42 43    public void printAge() 44    { 45        System.out.print(this.age + " "); 46    } 47}

大家先来分析一下上面这个代码会输出什么?

首先我们先来看看正确的输出结果,再来分析其缘由;正确的结果如下:

11 1 2 0 3 4 3

怎么样,大家分析结果正确了吗?或者是否会对这个结果有怀疑呢,不用怀疑,将这个程序段复制运行一下你将会看到这个可爱的结果了;

那为什么结果是这样呢?现在我就带大家一起来分析一下这段代码,分析完之后你将会对 对象实例化顺序有一个新的认识;

分析之前我先大家说一下对象实例化的几个基本顺序:

  1. 给父类和子类的成员变量分配内存空间,并初始化值(这里涉及基本数据类型的默认值的知识,在这里就不细说了);
  2. 执行父类的成员变量声明和初始化块(顺序执行)
  3. 执行父类的构造函数(具体执行哪个构造函数是由子类的构造函数决定的)
  4. 执行子类的成员变量声明和初始化块(也是顺序执行)
  5. 最后才是子类的构造函数的执行

好了,现在我们顺着这个步骤来分析一下这段代码,你清楚答案为什么是这个了:

  1. 给父类 A 和子类 B 的 age 成员分配内存空间,由于 age 是 int 类型,所以默认值都是 0 ,内存变化图如下:

  2. 执行父类的成员变量声明和初始化块(顺序执行)

    1    int age = 11;// 内存空间中父类的age值变为 22 2    { 3        System.out.print(age + " "); // 这时输出 11 4        age = 22; // 内存空间中父类的age值变为 22 5        System.out.print(1 + " ");// 输出 1 6    }

    执行完上面的语句之后内存最后的情况如下:
             子类的age依然是 0
    这时输出的值是:
    11 1

  3. 执行父类 A 的构造函数(具体执行哪个构造函数是由子类的构造函数决定的)

    1    public A() 2    { 3        System.out.print(2 + " ");// 会输出 2 4        printAge();// 因为这个函数在子类中被重写了,所以实际执行的是子类的那个方法,输出的是子类的 age 值,而此时子类 age 的值从刚才的内存中可以看出是:0 5    }

    这时输出的值是:
    11 1 2 0

  4. 执行子类 B 的成员变量声明和初始化块(也是顺序执行)

    1    int age = 3;// 子类的 age 变为 3 2    { 3        System.out.print(3 + " "); // 输出 3 4    }

    内存变化如下:

    这时输出的值是:
    11 1 2 0 3

  5. 执行子类 B 的构造函数

    1    public B() 2    { 3        System.out.print(4 + " "); // 输出 4 4        printAge(); // 调用子类的方法,输出子类 B 的 age 值,这是 age 的值是 3,所以输出 3 5    }

    所以最后的结果是:
    11 1 2 0 3 4 3

好了,现在时间点是 22:22,是时候洗澡去了,今天的培训笔记就先总结到这里吧!

不知道大家看完我总结的这篇笔记如何,我自己在听这个知识点的时候也是一脸的迷茫,怎么会这样的结果呢,最后一听内存变化的分析结果之后,就一切都明了啦;

所以看来自己还是不是很清楚对象的时候化顺序的,希望这个分析对大家有所收获。

总结的不好或者不对的地方,还请各位指出,学习是一个交流互动的过程,每个人看到的角度有时候确实挺大的,所以有时候会发现不一样的东西。

更多的JAVA学习总结,求关注我的每日笔记总结。bye!

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

Java日期时间API系列31

  时间戳是指格林威治时间1970年01月01日00时00分00秒起至现在的总毫秒数,是所有时间的基础,其他时间可以通过时间戳转换得到。Java中本来已经有相关获取时间戳的方法,Java8后增加新的类Instant等专用于处理时间戳问题。 1获取时间戳的方法和性能对比1.1获取时间戳方法Java8以前

java成员变量的初始化

类变量(static变量,不需要实例化对象也可以引用)实例变量(非static变量,需要实例化对象)局部变量(类的成员函数中的变量)初始化方式:构造函数初始化变量声明时初始化代码块初始化java自动初始化(在构造函数执行之前执行) java保证所有变量被使用之前都是经过初始化的(声明并且定义过,被赋值

你不可不知的JS面试题(第二期)

1、什么是继承?子类可以使用父类的所有功能,并且对功能进行扩展。新增方法改用方法(1)、ES6使用extends子类继承父类的方法。// 父类    class A        constructor(name)            this.name name;                getNa

java程序员应该知道的对象实例化顺序 - HelloWorld