Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >一文理解java对象初始化顺序

一文理解java对象初始化顺序

作者头像
SecondWorld
发布于 2019-11-01 07:10:19
发布于 2019-11-01 07:10:19
59700
代码可运行
举报
文章被收录于专栏:Java开发者杂谈Java开发者杂谈
运行总次数:0
代码可运行

例子

Talk is cheap, Show you the code!

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class ParentClass {
	static int parentStaticField = 1;
	
	final static int parentFinalStaticField = 2;
	
	int parentSubField = 3;
	
	static {
		System.out.println("parent static!");
		System.out.println("SubStatic field:" + SubClass.subStaticField);
		System.out.println("SubFinalStatic field:" + SubClass.subFinalStaticField);
		System.out.println("ParentFinalStatic field:" + parentStaticField);
		System.out.println("ParentFinalStatic field:" + parentFinalStaticField);
	}
	
	{
		System.out.println("parent not static!");
		System.out.println("SubStatic field:" + SubClass.subStaticField);
		System.out.println("SubFinalStatic field:" + SubClass.subFinalStaticField);
		System.out.println("ParentFinalStatic field:" + parentStaticField);
		System.out.println("ParentFinalStatic field:" + parentFinalStaticField);
		System.out.println("Parent field:" + parentSubField);
	}
	
	public ParentClass() {
		System.out.println("parent construct!");
	}
}


public class SubClass extends ParentClass {
	
	static int subStaticField = 1;
	
	final static int subFinalStaticField = 2;
	
	int subField = 3;
	
	static {
		System.out.println("Sub static!");
		System.out.println("SubStatic field:" + SubClass.subStaticField);
		System.out.println("SubFinalStatic field:" + SubClass.subFinalStaticField);
	}
	
	{
		System.out.println("Sub no static!");
		System.out.println("SubStatic field:" + SubClass.subStaticField);
		System.out.println("SubFinalStatic field:" + SubClass.subFinalStaticField);
	}
	
	public SubClass() {
		System.out.println("Sub Construct!");
	}
	
	public static void main(String[] args) {
		new SubClass();
	}
}

输出:
parent static!			--- 父类静态代码块
SubStatic field:0
SubFinalStatic field:2
ParentFinalStatic field:1		--- 父类静态代码块调用之前静态属性已经初始化
ParentFinalStatic field:2
Sub static!				--- 子类静态代码块
SubStatic field:1
SubFinalStatic field:2
parent not static!		--- 父类非静态代码块
SubStatic field:1
SubFinalStatic field:2
ParentFinalStatic field:1
ParentFinalStatic field:2
Parent field:3
parent construct!		--- 父类构造函数
Sub no static!			 --- 子类非静态代码块
SubStatic field:1
SubFinalStatic field:2
Sub Construct!			 --- 子类构造函数

从上面的例子总结类初始化的时候代码的执行顺序如下:

  • 父类和子类的final static属性初始化 ---》 父类的static属性初始化---》父类的static代码块---》 子类的static属性初始化---》子类的static代码块 ---》父类的非静态属性 ---》父类的非静态代码块---》父类的构造函数 ---》 子类的非静态属性---》 子类的非静态代码块---》子类的构造含
  • 不管父类还是子类,静态还是非静态,属性的初始化时间在代码块执行之前
  • static final修饰的属性,不管是父类还是子类,在类加载的准备阶段就已经初始化了,他要优先于任何代码块和属性的初始化

细说

类的加载阶段
1、加载

​ 由类加载器负责根据一个类的全限定名来读取此类的二进制字节流到JVM内部,并存储在运行时内存区的方法区,然后将其转换为一个与目标类型对应的java.lang.Class对象实例

2、验证

​ 格式验证:验证是否符合class文件规范

​ 语义验证:检查一个被标记为final的类型是否包含子类;检查一个类中的final方法是否被子类进行重写;

​ 确保父类和子类之间没有不兼容的一些方法声明(比如方法签名相同,但方法的返回值不同)

操作验证:在操作数栈中的数据必须进行正确的操作,对常量池中的各种符号引用执行验证(通常在解析阶段执行,检查是否可以通过符号引用中描述的全限定名定位到指定类型上,以及类成员信息的访问修饰符是否允许访问等)

3、准备

​ 为类中的所有静态变量分配内存空间,并为其设置一个初始值(由于还没有产生对象,实例变量不在此操作范围内)

​ 被final修饰的static变量(常量),会直接赋值;

4、解析

​ 将常量池中的符号引用转为直接引用(得到类或者字段、方法在内存中的指针或者偏移量,以便直接调用该方法),这个可以在初始化之后再执行。

​ 解析需要静态绑定的内容。 // 所有不会被重写的方法和域都会被静态绑定

​ 以上2、3、4三个阶段又合称为链接阶段,链接阶段要做的是将加载到JVM中的二进制字节流的类数据信息合并到JVM的运行时状态中。

5、初始化(先父后子)

  • 为静态变量赋值
  • 执行static代码块

​ 注意:static代码块只有jvm能够调用

​ 如果是多线程需要同时初始化一个类,仅仅只能允许其中一个线程对其执行初始化操作,其余线程必须等待,只有在活动线程执行完对类的初始化操作之后,才会通知正在等待的其他线程。

​ 因为子类存在对父类的依赖,所以类的加载顺序是先加载父类后加载子类,初始化也一样。不过,父类初始化时,子类静态变量的值也有有的,是默认值。

​ 最终,方法区会存储当前类类信息,包括类的静态变量、类初始化代码(定义静态变量时的赋值语句 和 静态初始化代码块)、实例变量定义、实例初始化代码(定义实例变量时的赋值语句实例代码块和构造方法)和实例方法,还有父类的类信息引用。

对象创建阶段
1、在堆区分配对象需要的内存

​ 分配的内存包括本类和父类的所有实例变量,但不包括任何静态变量

2、对所有实例变量赋默认值

​ 将方法区内对实例变量的定义拷贝一份到堆区,然后赋默认值

3、执行实例初始化代码

​ 初始化顺序是先初始化父类再初始化子类,初始化时先执行实例代码块然后是构造方法

4、如果有类似于Child c = new Child()形式的c引用的话,在栈区定义Child类型引用变量c,然后将堆区对象的地址赋值给它

​ 需要注意的是,每个子类对象持有父类对象的引用,可在内部通过super关键字来调用父类对象,但在外部不可访问

补充:

​ 通过实例引用调用实例方法的时候,先从方法区中对象的实际类型信息找,找不到的话再去父类类型信息中找。

​ 如果继承的层次比较深,要调用的方法位于比较上层的父类,则调用的效率是比较低的,因为每次调用都要经过很多次查找。这时候大多系统会采用一种称为虚方法表的方法来优化调用的效率。

​ 所谓虚方法表,就是在类加载的时候,为每个类创建一个表,这个表包括该类的对象所有动态绑定的方法及其地址,包括父类的方法,但一个方法只有一条记录,子类重写了父类方法后只会保留子类的。当通过对象动态绑定方法的时候,只需要查找这个表就可以了,而不需要挨个查找每个父类。


参考链接

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019-10-31 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
【java基础☞初始化顺序】java继承中的初始化顺序
1、初始化顺序:父类的静态变量-->父类的静态代码块-->子类的静态变量-->子类的静态代码快-->父类的非静态变量(父类的非静态代码块)-->父类的构造函数-->子类的非静态变量(子类的非静态代码块)-->子类的构造函数
用户5640963
2019/07/25
1.1K0
Java类初始化顺序 转
关于构造函数,以下几点要注意: 1.对象一建立,就会调用与之相应的构造函数,也就是说,不建立对象,构造函数时不会运行的。 2.构造函数的作用是用于给对象进行初始化。 3.一个对象建立,构造函数只运行一次,而一般方法可以被该对象调用多次。
wuweixiang
2018/08/14
6680
从 static 关键字深入理解 java对象初始化顺序
最近在阅读 ThreadLocal 源码的时候,发现一段很有意思的代码,代码片段如下:
猿芯
2020/12/03
4440
从 static 关键字深入理解 java对象初始化顺序
彻底搞懂java程序的初始化顺序
在java程序中,当实例化对象时,对象的所在类的所有成员变量首先要进行初始化,只有当所有类成员完成初始化后,才会调用对象所在类的构造函数创建对象。
全菜工程师小辉
2019/08/16
2.6K0
重学Java基础篇—Java类加载顺序深度解析
Remember_Ray
2025/03/18
1520
重学Java基础篇—Java类加载顺序深度解析
类、变量、块、构造器、继承初始化顺序,终极解答。
最近发现微信群里面有些群友在讨论类的初始化顺序,如类的静态变量、成员变量、静态代码块、非静态代码块、构造器,及继承父类时,它们的初始化顺序都是怎样的,下面我通过例子来说明这个情况,以免被人误导。 示例1:测试单类的初始化顺序 public class ClassInitOrderTest { public static String staticField = "static field"; static { System.out.println(staticFiel
Java技术栈
2018/03/30
7490
类、变量、块、构造器、继承初始化顺序,终极解答。
Java中变量的初始化顺序
在写一个通用的报警模块时,遇到一个有意思的问题,在调用静态方法时,发现静态方法内部对静态变量引用时,居然抛出了npe,仿佛是因为这个静态变量的初始化在静态方法被调用时,还没有触发,从而导致这个问题,因此今天专门来学习下静态成员的初始化顺序,以及上面这个问题导致的原因
一灰灰blog
2022/01/17
1.4K0
Java中变量的初始化顺序
java类初始化顺序
java代码块 静态代码块:用staitc声明,jvm加载类时执行,仅执行一次 构造代码块:类中直接用{}定义,每一次创建对象时执行。 执行顺序优先级:静态块,main(),构造块,构造方法。 构造函数 创建对象时调用 一般用于给对象初始化 一个对象建立,构造函数执行一次 构造代码块 用来给对象初始化 对象建立时运行构造代码块,优先于构造函数 构造代码块是给所有对象进行统一初始化,而构造函数是给对应的对象初始化。因为构造函数是可以多个的,运行哪个构造函数就会建立什么样的对象,但无论建立哪个对象,都会先执行相
leobhao
2022/06/28
9302
Java的,与类的初始化顺序
转载自 https://blog.csdn.net/sujz12345/article/details/52590095
allsmallpig
2021/02/25
3430
一道有意思的“初始化”面试题
今天向大家分享一道Java面试题目,这道题是我自己设计的题目。题目原型来自于《Thinking in Java》中的“初始化与清理”一章,本来是一道简单的考察“初始化”题目,我在上面添加了其他与初始化相关的东西,最后变成一道比较综合的考察“初始化”题目。在很多笔试题中都会考察初始化方面的知识,如果你能把下面这道题目理解了,应付初始化方面的笔试题相信是“易如反掌”。
Java架构师必看
2021/05/18
3630
Java基础系列5:Java代码的执行顺序
该系列博文会告诉你如何从入门到进阶,一步步地学习Java基础知识,并上手进行实战,接着了解每个Java知识点背后的实现原理,更完整地了解整个Java技术体系,形成自己的知识框架。
说故事的五公子
2019/11/14
8230
Java基础(十):关键字static、代码块、关键字final
Java微观世界
2025/01/21
1540
Java基础(十):关键字static、代码块、关键字final
深入理解Java对象的创建过程:类的初始化与实例化
在Java中,一个对象在可以被使用之前必须要被正确地初始化,这一点是Java规范规定的。在实例化一个对象时,JVM首先会检查相关类型是否已经加载并初始化,如果没有,则JVM立即进行加载并调用类构造器完成类的初始化。在类初始化过程中或初始化完毕后,根据具体情况才会去对类进行实例化。本文试图对JVM执行类初始化和实例化的过程做一个详细深入地介绍,以便从Java虚拟机的角度清晰解剖一个Java对象的创建过程。
用户7886150
2020/12/07
2.8K0
java类加载常见的几种问题理解类加载机制
类加载过程中的相关概念详见- http://m.myexception.cn/program/1602930.html http://www.codeceo.com/article/java-class-loader-learn.html http://blog.csdn.net/ns_code/article/details/17881581 理解类加载过程的5步。 Round1: 首先请理解如下代码:
全栈程序员站长
2022/08/27
4880
java之static关键字
static方法: 1、定义: 《java编程思想》中提到:static方法就是没有this的方法,在static方法内部不能调用非静态方法,反过来是可以的,而且可以在没有创建任何对象的情况下,仅仅通过类本身来调用static方法,这实际上正是static方法的用途 2:概念: static方法一般称作静态方法,由于静态方法不依赖于对象就可以访问,所以也就没有this,并且static修饰的方法不能调用非静态成员变量和非静态成员方法,但是非静态成员方法可以调用静态成员方法 static变量: 定
说故事的五公子
2019/09/11
4310
深入理解Java对象的创建过程:类的初始化与实例化
在Java中,一个对象在可以被使用之前必须要被正确地初始化,这一点是Java规范规定的。在实例化一个对象时,JVM首先会检查相关类型是否已经加载并初始化,如果没有,则JVM立即进行加载并调用类构造器完成类的初始化。在类初始化过程中或初始化完毕后,根据具体情况才会去对类进行实例化。本文试图对JVM执行类初始化和实例化的过程做一个详细深入地介绍,以便从Java虚拟机的角度清晰解剖一个Java对象的创建过程。
Java团长
2018/08/03
3.5K0
java学习笔记(基础篇)—关键字static和final
a)所有类实例共享 b)使用静态变量 类名.变量名 c)静态变量是属于这个类,非静态变量是属于对象 d)代码加载到内存静态变量就已经存在了 e)static变量和实例变量的区别: i)static变量对于每个类而言在内存中只有一个,能被类的所有实例所共享; 实例变量对于每个类的每个实例都有一份,它们之间互不影响; ii)Java虚拟机在加载类的过程中为static变量分配内存, 实例变量在加载完类后创建对象时分配内存; iii)static变量存在方法区,实例变量存在堆区; v)static变量可以直接通过类名访问, 实例变量通过引用类型变量访问;
chlinlearn
2019/07/24
3620
详解 Java 对象与内存控制(上)
不管是类变量还是实例变量,你都不能引用一个还没有定义的变量,或者在引用之前没有定义的变量,如下图所示:
CoderJed
2018/09/13
4770
详解 Java 对象与内存控制(上)
干货——详解Java中的关键字
在平时编码中,我们可能只注意了这些static,final,volatile等关键字的使用,忽略了他们的细节,更深层次的意义。
Janti
2018/08/01
4410
干货——详解Java中的关键字
深入理解JVM虚拟机---类的加载、连接、初始化
除了以上7种情况,其他使用Java类的方式均被看作类的被动使用,都不会导致类的初始化
俺也想起舞
2019/12/31
4600
相关推荐
【java基础☞初始化顺序】java继承中的初始化顺序
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验