2026-04-11 10:02:11
JAVA对象实例化过程详解
一、对象的实例化过程概述
对象的实例化过程在Java中是一个复杂但有序的过程,主要分为两部分:类的加载初始化与对象的初始化。要创建类的对象实例,首先需要加载并初始化该类,包括main方法所在的类。类的初始化执行<clinit>方法,而对象的实例化则执行<init>方法。
二、类的加载过程
类的加载机制涉及将class文件加载到方法区的过程,包括以下几个阶段:
加载:载入class对象,不一定是从class文件获取,也可以是jar包或动态生成的class。
验证:校验class字节流是否符合当前JVM规范。
准备:为类变量分配内存并设置变量的初始值(默认值)。如果是final修饰的对象,则是赋值声明值。
解析:将常量池的符号引用替换为直接引用。
初始化:执行类构造器<clinit>,为类变量赋值,执行静态代码块。JVM会保证子类的<clinit>执行之前,父类的<clinit>先执行完毕。
其中,验证、准备、解析三个阶段统称为连接。<clinit>方法由静态变量赋值代码和静态代码块组成,先执行类静态变量显示赋值代码,再到静态代码块代码。
三、触发类加载的条件
类加载的触发条件包括:
第一次创建类的新对象时,会触发类的加载初始化和对象的初始化函数<init>执行。
JVM启动时会先加载初始化包含main方法的类。
调用类的静态方法。
对类或接口的静态字段执行读写操作(final修饰的静态字段除外)。
调用Java API中的反射方法时。
当初始化一个类时,其父类没有初始化,则需先触发父类的初始化(接口例外)。
四、对象的实例化过程
对象实例化过程执行类构造函数,对应在字节码文件中的<init>()方法(称之为实例构造器)。<init>()方法由非静态变量、非静态代码块以及对应的构造器组成。<init>()方法可以重载多个,类有几个构造器就有几个<init>()方法。
<init>()方法中的代码执行顺序为:父类变量初始化,父类代码块,父类构造器,子类变量初始化,子类代码块,子类构造器。
静态变量、静态代码块、普通变量、普通代码块、构造器的执行顺序如下:
静态变量和静态代码块(按在类中出现的顺序执行)
实例变量和普通代码块(按在类中出现的顺序执行,每次创建对象时都会执行)
构造器(每次创建对象时都会执行)
对于具有父类的子类,其实例化顺序如下:
父类静态变量和静态代码块(只执行一次)
子类静态变量和静态代码块(只执行一次)
父类实例变量和普通代码块(每次创建对象时执行)
父类构造器(每次创建对象时执行)
子类实例变量和普通代码块(每次创建对象时执行)
子类构造器(每次创建对象时执行)
五、类加载器和双亲委派规则
类加载器是通过一个类的全限定名来获取描述此类的二进制字节流的代码模块。任意一个类都需要其加载器和类本身来确定类在JVM的唯一性。每个类加载器都有自己的类名称空间,同一个类class由不同的加载器加载,则被JVM判断为不同的类。
双亲委派模型是Java类加载的一种实现方式,它要求除了顶层的启动类加载器外,其余的类加载器都应当有自己的父类加载器。当一个类加载器需要加载某个类时,它会先委托给父类加载器去加载,如果父类加载器无法加载(在自己负责的目录找不到对应的类),则交还下层类加载器去加载。
双亲委派模型很好地解决了各个类加载基础类的同一问题(越基础的类由越上层的加载器加载),但有时候基础类需要调用下层的代码,此时需要打破双亲委派模型。例如,JNDI服务需要调用由独立厂商部署在应用程序classpath下的JNDI的SPI代码,为了解决SPI代码加载问题,Java引入了线程上下文类加载器去加载SPI代码。线程上下文类加载器是线程创建时从父线程继承的,如果全局范围没有设置过,则默认设置为application Class Loader。





综上所述,Java对象的实例化过程是一个复杂但有序的过程,涉及类的加载初始化、对象的初始化以及类加载器和双亲委派规则等多个方面。理解这些概念和过程对于深入掌握Java语言至关重要。