一文理解java对象初始化顺序
例子
Talk is cheap, Show you the code!
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("ParentStatic 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("ParentStatic 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
ParentStatic field:1 --- 父类静态代码块调用之前静态属性已经初始化
ParentFinalStatic field:2
Sub static! --- 子类静态代码块
SubStatic field:1
SubFinalStatic field:2
parent not static! --- 父类非静态代码块
SubStatic field:1
SubFinalStatic field:2
ParentStatic 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关键字来调用父类对象,但在外部不可访问
补充:
通过实例引用调用实例方法的时候,先从方法区中对象的实际类型信息找,找不到的话再去父类类型信息中找。
如果继承的层次比较深,要调用的方法位于比较上层的父类,则调用的效率是比较低的,因为每次调用都要经过很多次查找。这时候大多系统会采用一种称为虚方法表的方法来优化调用的效率。
所谓虚方法表,就是在类加载的时候,为每个类创建一个表,这个表包括该类的对象所有动态绑定的方法及其地址,包括父类的方法,但一个方法只有一条记录,子类重写了父类方法后只会保留子类的。当通过对象动态绑定方法的时候,只需要查找这个表就可以了,而不需要挨个查找每个父类。
参考链接
一文理解java对象初始化顺序的更多相关文章
- Java变量、Java对象初始化顺序
局部变量与成员变量: 局部变量分为: 行参:在方法签名中定义的局部变量,随方法的结束而凋亡. 方法内的局部变量:必须在方法内对其显示初始化,从初始化后开始生效,随方法的结束而凋亡. 代码块内的局部变量 ...
- Java对象初始化顺序
最近我发现了一个有趣的问题,这个问题的答案乍一看下骗过了我的眼睛.看一下这三个类: package com.ds.test; public class Upper { String upperSt ...
- java对象初始化顺序的简单验证
以下这段小程序对调用对象构造函数时,父类构造函数.成员变量初始化函数,以及非静态初始化块调用顺序进行验证,不考虑静态成员及静态初始化块. public class Derive extends Bas ...
- 深入理解Java对象的创建过程:类的初始化与实例化
摘要: 在Java中,一个对象在可以被使用之前必须要被正确地初始化,这一点是Java规范规定的.在实例化一个对象时,JVM首先会检查相关类型是否已经加载并初始化,如果没有,则JVM立即进行加载并调用类 ...
- (转)深入理解Java对象的创建过程
参考来源:http://blog.csdn.net/justloveyou_/article/details/72466416 摘要: 在Java中,一个对象在可以被使用之前必须要被正确地初始化,这一 ...
- Java提高篇——静态代码块、构造代码块、构造函数以及Java类初始化顺序
静态代码块:用staitc声明,jvm加载类时执行,仅执行一次构造代码块:类中直接用{}定义,每一次创建对象时执行.执行顺序优先级:静态块,main(),构造块,构造方法. 构造函数 public H ...
- java 继承 初始化顺序
面向对象三大特性: 封装,继承,多态 java 继承初始化顺序 先初始化父类对象, 在父类对象中先初始化父类属性,再初始化父类的构造方法,然后初始化子类对象,初始化子类对象的属性,初始化子类的构造方法 ...
- java继承 初始化顺序
java继承 初始化顺序 标签: 初始化顺序 2013-08-01 10:13 879人阅读 评论(0) 收藏 举报 分类: java(8) 版权声明:本文为博主原创文章,未经博主允许不得转载. ...
- 深入理解Java对象
深入理解Java对象(理清关系) 1.对象的创建过程: 所有创建过程如下所示: new 类名 根据new的参数在常量池中定位一个类的符号引用. 如果没有找到这个符号引用,说明类还没有被加载,则进行类的 ...
随机推荐
- 写论文与PPT汇报时matlab图片的背景透明处理
不少同学在使用Word写论文时,将matlab生成的图保存为jpg格式,然后粘贴到文档中.word背景为纯白色,jpg图的缺点没有显示,实际上会存在很大白边,以及放大后不清晰的问题,很影响PPT展示和 ...
- 基于python的scrapy环境搭建
0.1安装python软件 32位机的电脑安装python-3.6.3.exe 64位机的电脑安装python-3.6.3-amd64.exe 0.1.1 python环境搭建 执行安装程序 选择Ad ...
- java-newInstance()和new()
public <T> T genericMethod(Class<T> tClass)throws InstantiationException , IllegalAccess ...
- Spring 梳理-接收请求的输入(原)
Spring MVC 允许一下方式将客户端的数据传送到控制器的处理方法中 查询参数(Query Parameter) 表单参数(Form Parameter) 路径变量(Path Variable ...
- Java 基本数据类型极限值
public static void main(String[] args){ System.out.println("Integer.MIN_VALUE = " + Intege ...
- 创建FTP访问的YUM源
创建FTP访问的YUM源 一.安装vsftpd(步骤详见“在linux中搭建vsftpd.docx”) 在主机A上安装FTP,安装后的ftp信息如下:ftp://192.168.43.300 账号密 ...
- Actor 模型中的通信模式
在 Actor 模型中所有的 Actor 之间有且只有一种通信模式,那就是 tell 的方式,也就是 fire and forget 的方式.但是在实际的开发过程中工程师们逐渐总结出了一些常用的通信模 ...
- Go语言操作MySQL
MySQL是常用的关系型数据库,本文介绍了Go语言如何操作MySQL数据库. Go操作MySQL 连接 Go语言中的database/sql包提供了保证SQL或类SQL数据库的泛用接口,并不提供具体的 ...
- Java 学习笔记之 Synchronized锁对象
Synchronized锁对象: Synchronized取得的锁都是对象锁,而不是把一段代码或方法当作锁,哪个线程执行带synchronized关键字的方法,哪个线程就持有该方法所属对象的锁,那么其 ...
- 快学Scala 第七课 (类构造函数)
类 主构造器: class Person (var name: String){ } 主构造参数可以不带val或者var,如果没有被其他方法使用,则不保存为字段. 如果被其他方法使用,则被升格为字段, ...