基础篇:详解JAVA对象实例化过程

RabbitMQ信使
• 阅读 6908

1 对象的实例化过程

  • 对象的实例化过程是分成两部分:类的加载初始化,对象的初始化
  • 要创建类的对象实例需要先加载并初始化该类,main方法所在的类需要先加载和初始化
  • 类初始化就是执行<clinit>方法,对象实例化是执行<init>方法
  • 一个子类要初始化需要先初始化父类

2 类的加载过程

基础篇:详解JAVA对象实例化过程

  • 类的加载机制:如果没有相应类的class,则加载class到方法区。对应着加载->验证->准备->解析-->初始化阶段

    • 加载:载入class对象,不一定是从class文件获取,可以是jar包,或者动态生成的class
    • 验证:校验class字节流是否符合当前jvm规范
    • 准备:为类变量分配内存并设置变量的初始值(默认值)。如果是final修饰的对象则是赋值声明值
    • 解析:将常量池的符号引用替换为直接引用
    • 初始化:执行类构造器<client>(注意不是对象构造器),为类变量赋值,执行静态代码块。jvm会保证子类的<client>执行之前,父类的<client>先执行完毕
  • 其中验证、准备、解析3个部分称为 连接
  • <clinit>方法由静态变量赋值代码和静态代码块组成;先执行类静态变量显示赋值代码,再到静态代码块代码

3 触发类加载的条件

  • 第一次创建类的新对象时,会触发类的加载初始化和对象的初始化函数<init>执行,这个是实例初始化,其他6个都是类初始化
  • JVM启动时会先加载初始化包含main方法的类
  • 调用类的静态方法(如执行invokestatic指令)
  • 对类或接口的静态字段执行读写操作(即执行getstatic、putstatic指令);不过final修饰的静态字段的除外(已经赋值,String和基本类型,不包含包装类型),它被初始化为一个编译时常量表达式

    • 注意:操作静态字段时,只有直接定义这个字段的类才会被初始化;如通过其子类来操作父类中定义的静态字段,只会触发父类<clinit>的初始化而不是子类的初始化
  • 调用JavaAPI中的反射方法时(比调用java.lang.Class中的方法(Class.forName),或者java.lang.reflect包中其他类的方法)
  • 当初始化一个类时,其父类没有初始化,则需先触发父类的初始化(接口例外)

4 对象的实例化过程

  • 对象实例化过程 其实就是执行类构造函数 对应在字节码文件中的<init>()方法(称之为实例构造器);<init>()方法由非静态变量、非静态代码块以及对应的构造器组成

    • <init>()方法可以重载多个,类有几个构造器就有几个<init>()方法
    • <init>()方法中的代码执行顺序为:父类变量初始化,父类代码块,父类构造器,子类变量初始化,子类代码块,子类构造器。
  • 静态变量,静态代码块,普通变量,普通代码块,构造器的执行顺序

基础篇:详解JAVA对象实例化过程

  • 具有父类的子类的实例化顺序如下

基础篇:详解JAVA对象实例化过程

5 类加载器和双亲委派规则,如何打破双亲委派规则

  • 类加载器

    • 通过一个类的全限定名来获取描述此类的二进制字节流,实现这个动作的代码模块称为类加载器
    • 任意一个类都需要其加载器和类本身来确定类在JVM的唯一性;每个类加载器都有自己的类名称空间,同一个类class由不同的加载器加载,则被JVM判断为不同的类

基础篇:详解JAVA对象实例化过程

  • 双亲委派模型

    • 启动类加载器有C++代码实现,是虚拟机的一部分。负责加载lib下的类库
    • 其他的类加载器有java语言实现,独立于JVM,并且继承ClassLoader
    • extention ClassLoader负责加载libext目录下的类库
    • application ClassLoader 负责加载用户路径下(ClassPath)的代码
    • 不同的类加载器加载同一个class文件会导致出现两个类。而java给出解决方法是下层的加载器加委托上级的加载器去加载类,如果父类无法加载(在自己负责的目录找不到对应的类),而交还下层类加载器去加载。如下图

基础篇:详解JAVA对象实例化过程

  • 打破双亲委派模型

    • 双亲委派模型并不是一个强制的约束模型,而是java设计者推荐给开发者的类加载实现方式
    • 双亲委派模型很好的解决各个类加载基础类的同一问题(越基础的类由越上层的加载器加载),但是基础类总是作为用户代码调用的API,但是如果它的具体实现是下层的代码,此时基础类需要调用下层的代码,则需要打破双亲委派模型
    • 如JNDI服务,JNDI的代码有启动类去加载(rt.jar),它需要调用由独立厂商部署在应用程序classpath下的JNDI的SPI(Service Provider Interface)代码。为了解决SPI代码加载问题,java引入了线程上下文类加载器去加载SPI代码。也就是父类加载器请求子类去完成类的加载动作
    • 线程上下文类加载器,线程创建时会从父线程继承,如果全局范围没有设置过,则默认设置为application Class Loader

欢迎指正文中错误

关注公众号,一起交流

  • 基础篇:详解JAVA对象实例化过程

参考文章

点赞
收藏
评论区
推荐文章
Wesley13 Wesley13
3年前
java常用类(2)
三、时间处理相关类Date类:计算机世界把1970年1月1号定为基准时间,每个度量单位是毫秒(1秒的千分之一),用long类型的变量表示时间。Date分配Date对象并初始化对象,以表示自从标准基准时间(称为“历元”(epoch),即1970年1月1日08:00:00GMT)以来的指定毫秒数。示例:packagecn.tanjian
Wesley13 Wesley13
3年前
java实例化
初始化块实际上,初始化块是一个假象,使用javac命令编译java类后,初始化块会消失,初始化代码被还原到每个构造器中代码的最前面在实例化的过程中:前面的初始化块先执行,后面的后执行初始化块的修饰符只能是static普通初始化块负责对对象执行初始化,静态初始化块负责对类执行初始化初始化块只在创建
Wesley13 Wesley13
3年前
java成员变量的初始化
类变量(static变量,不需要实例化对象也可以引用)实例变量(非static变量,需要实例化对象)局部变量(类的成员函数中的变量)初始化方式:构造函数初始化变量声明时初始化代码块初始化java自动初始化(在构造函数执行之前执行) java保证所有变量被使用之前都是经过初始化的(声明并且定义过,被赋值
Stella981 Stella981
3年前
JVM 之 类的加载与初始化
JVM中的类或接口的加载,连接,初始化动作都是动态的。加载主要是根据指定的名称找到并读取类或接口的二进制表示形式,然后创建类和接口。连接则是通过验证,准备,解析等动作将相关联的类或接口合并为运行时形态以便可以被JVM执行。初始化的过程主要包括类或接口初始化方法<clinit的执行。JVM在启动时,首先会加载和创建"启动类",该类由具体JVM实现
Stella981 Stella981
3年前
Spring core 源码分析
    上节提到了在AbstractApplicationContext调用refresh方法里,初始化所有BeanDefinitions后,遍历所有BeanDefinitionNames后,循环调用BeanFactory的getBean(name)方法,实例化所有容器Bean对象(非lasyinit)。GetBean做了什么?循环引用如何处理
Wesley13 Wesley13
3年前
Java 构造方法
构造方法什么是构造方法:构造方法就是与类同名的那个方法且没有返回值。就是一个方法。有什么作用:就是初始化对象的成员变量,无参的构造方法,系统自动初始化。有参则根据你的要求初始化不同的类型,默认值如下:实例成员变量默认值:boolean:falsebyte:0short:0char:int:
Wesley13 Wesley13
3年前
TP3快速入门
一、查询  D方法实例化模型类的时候通常是实例化某个具体的模型类,如果你仅仅是对数据表进行基本的CURD操作的话,使用M方法实例化的话,由于不需要加载具体的模型类,所以性能会更高。$maparray();$map'u.store_id'array('eq',0);$map'u.reg_time'a
Stella981 Stella981
3年前
JVM02
文章目录前言对象创建1.类加载检查2.分配内存分配内存的方式内存分配的并发问题3.初始化零值4.设置对象头:5\.执行init方法;对象内存布局对象头实例数据对齐填充对象访问方式使用句柄
Wesley13 Wesley13
3年前
Java Calender 类详解
一、  如何创建Calendar对象Calendar是一个抽象类,无法通过直接实例化得到对象.因此, Calendar 提供了一个方法getInstance,来获得一个Calendar对象,得到的Calendar 由当前时间初始化.1CalendarcalCalendar.getInstance()
Easter79 Easter79
3年前
StringBuffer的用法总结
1.StringBuffer对象的初始化        StringBuffer对象初始化的方法通常一般用构造方法的进行初始化.例如:StringBuffersnewStringBuffer();这样初始化出的StringBuffer对象是一个空的对象.如果需要创建带有内容的StringBuffer对象,可以这样写:Str
美凌格栋栋酱 美凌格栋栋酱
5个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(