php子类实例化加载顺序,实例化一个对象(类加载)的执行顺序详解

这篇博客将以类加载执行顺序为探索的起点,串讲涉及到的Java相关知识,主要是这个流程中JVN内部运行机制的解析。

结论

Created with Raphaël 2.1.0

开始

父类静态代码

子类静态代码

父类非静态代码

父类构造函数

子类非静态代码

子类构造函数

结束

注解:

默认该类及其父类JVM未曾加载过

先父后子,先静后常再构造

同等级内代码逐条按顺序执行

* 当静态代码和非静态代码中成员变量包含对象,也会先执行该对象类的静态代码和构造函数

先修知识

JVN 运行时数据区

JVN内存可简单分为三个区:堆(heap)、栈(stack)和方法区(method):

堆区

存放对象本身(包括非static成员变量),所有线程共享

栈区

存放基础数据类型、对象的引用,每个线程独立空间,不可互相访问

栈分为3个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)

方法区(静态区,包含常量池)

存永远唯一元素(类-class、类变量-static变量、方法),所有线程共享

实例化一个对象的执行顺序分析

因为涉及到的情况可以很复杂,最复杂的情况可能是有多个父类,父类及子类中有多个成员变量对象,这种情况下调用栈会比较深,所以我们先重简单一个单独的类开始分析,而且只要明白了基础的过程,那么再复杂的情况也只是在这个基础上叠加调用。

一个单独的类的情况

Created with Raphaël 2.1.0

开始

该类是否已加载到方法区中

执行非静态代码1——在堆中为对象分配空间

(对象信息:类引用、类变量引用、方法引用、成员变量等)

执行非静态代码2——运行构造函数

结束

执行静态代码——加载该类的静态信息(类、

static代码、方法)——在方法区分配空间

yes

no

有父类存在的情况

有父才有子,所以当父类存在时(其实Object类是所有类的父类),只是在这两个阶段前(执行静态、执行非静态)前,先运行父类的相关代码

默认该类及其父类JVM未曾加载过

Created with Raphaël 2.1.0

开始

父类静态代码

子类静态代码

父类非静态代码

父类构造函数

子类非静态代码

子类构造函数

结束

此处为什么要执行父类的构造方法呢?(此处存疑,待查证,欢迎指正)

因为子类的构造方法默认调用super();

创建子类实例的时,如果没有super调用父类带参数的构造方法,则默认会调用父类的无参构造方法,默认调用super().

当类中包含属性类时

因为同等级内代码逐条按顺序执行,所以当存在属性类时,例如:

public class A {

static {

System.out.println("A static");

}

B b = new B();

{

System.out.println("A not static");

}

A(){

System.out.println("A constructor");

}

public static void main(String[] args) {

System.out.println("Hello World!");

A m = new A();

}

}

class B{

static {

System.out.println("B static");

}

{

System.out.println("B not static");

}

B(){

System.out.println("B constructor");

}

}

结果输出如下:

A static

Hello World!

B static

B not static

B constructor A not static A constructor

在实例化A类时,在顺序执行到非静态代码中的“B b = new B()”时,将先执行加载完毕B的所有代码;

如果我将代码改成“非静态代码块”在“实例化B代码”前,如下:

...

{

System.out.println("A not static");

}

B b = new B();

...

将会得到如下输出

A static

Hello World!

A not static//这一行也在前面了

B static

B not static

B constructor A constructor

得以验证:同等级内代码逐条按顺序执行,遇到属性类则加载完属性类后再执行下一步代码

注意:当属性类不立刻进行实例化时,JVM只会将堆中的成员属性类指向一个null,而不进行加载该属性类的操作

例:

...

B b;

...

代码如上时,将不会有任何与类B相关的输出

关联知识

类变量-static变量、类方法-static方法

这两者的特性:

- 全局唯一,一改都改,节省资源

- 可直接通过类名调用

- 仅能访问static数据、static方法

- 不能引用this和super

下面我们通过实例化对象执行步骤来推导出以上四条特性:

Created with Raphaël 2.1.0

开始

该类是否已加载到方法区中

执行非静态代码1——在堆中为对象分配空间

(对象信息:类引用、类变量引用、方法引用、成员变量等)

执行静态代码——加载该类的静态信息(类、

static代码、方法)——在方法区分配空间

yes

no

由流程图可知,

1. static代码只在类初始化时加载一次,加载后存在方法区,而每一个对象在实例化时,只是在堆中保存指向方法区的引用,所以全局唯一,一改都改,节省资源

2. 因为static代码在对象被实例化之前和类初始化一起执行,所以除了可以通过对象应用外,也可以直接通过类名引用

3. 因为先执行静态代码,再执行非静态代码,所以static代码仅能访问static数据、static方法

4. this、super属于非静态代码(??),所以不能引用this和super

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值