Java对象的实例化过程是一个相对复杂但非常核心的概念,它涉及到Java的内存管理、类加载、对象的创建和初始化等多个方面。以下是Java对象实例化过程的一个大致步骤:
1.类加载
1.当一个类首次被主动使用时(例如,创建类的实例、访问类的静态变量或静态方法、调用类的反射方法等),JVM的类加载器会加载这个类。
2.类加载器会读取类的字节码文件(通常是.class
文件),并将其加载到JVM中,生成对应的Class
对象。
3.类加载包括加载、链接(验证、准备、解析)和初始化三个阶段。
加载:把类的.class文件中的数据读入到内存中,通常是创建一个字节数组读入.class文件,然后产生与所加载类对应的Class对象。
链接:
验证:对文件的格式、字节码等进行验证。
准备:对类中的静态变量赋值默认值(即被static修饰的变量)。
解析:将类、方法、属性等符号引用解析为直接引用。
初始化:为类的静态变量赋予正确的初始值,并执行静态代码块中的代码。
2.内存分配
1.在堆内存中为对象分配内存空间。这个内存空间的大小取决于对象的实际大小(包括对象的实例变量以及从超类继承的变量)。
2.如果内存分配失败(例如,堆内存不足),则会抛出OutOfMemoryError
异常。
3.初始化零值
在分配的内存空间中,JVM会将所有字段(包括实例字段和从超类继承的字段)初始化为默认值(例如,数值类型默认为0,引用类型默认为null)。
4.设置对象头
1.对象头包含了对象的元数据信息,如哈希码、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等。
2.此外,对象头还包含一个指向类元数据的指针(即对象所属的类的Class
对象的引用)。
5.执行构造函数
1.在所有字段被初始化为零值之后,JVM会调用对象的构造函数(如果有的话)来完成对象的初始化。
2.构造函数可以执行各种初始化操作,如设置字段的初始值、执行其他方法等。
3.如果构造函数抛出异常,并且没有被捕获,那么对象实例化过程将失败,并且已经分配的内存空间可能会被垃圾回收器回收。
6.返回对象引用
一旦对象被成功创建并初始化,JVM就会返回一个指向该对象的引用。这个引用存放在栈中,可以用于后续的操作,如访问对象的字段、调用对象的方法等。
需要注意的是,以上步骤是一个简化的描述,实际的Java对象实例化过程可能涉及更多的细节和复杂性。比如经典单例的双重检查锁中为什么加volatile。
同时对照一下:
父类静态变量和静态代码块(按在类中声明的顺序)
子类静态变量和静态代码块(按在类中声明的顺序)
父类成员变量和代码块(按在类中声明的顺序)
父类构造方法
子类成员变量和代码块(按在类中声明的顺序)
子类构造方法