1、定义
创建对象的过程就叫实例化。这个过程中会在堆中开辟内存,将一些非静态的方法,变量存放在里面。在程序执行的过程中,可以创建多个对象,既多次实例化。每次实例化都会开辟一块新的内存。
2、实例化的几种方法
- new 关键字创建
- 通过 new 关键字调用类构造器创建对象;
- 调用对象的 getXXXInstance 方法(单例模式/建造者模式);
- XXXBuilder/ XXXFactory。
- 反射:Class的 newInstance 方法或者 Constructor 的 newInstance 方法
- 使用类的 clone 方法
- 使用反序列化(deserialization)
- 第三方库 Objenesis
3、Java 类的生命周期
Java 类的生命周期是指一个 class 文件从加载到卸载的全过程,类的完整生命周期包括七个部分:加载——验证——准备——解析——初始化——使用——卸载。
PS:对象的生命周期请参考:Java对象的生命周期 - 简书
4、Java 类加载过程
- 加载:加载过程就是把class字节码文件载入到虚拟机中,至于从哪儿加载,虚拟机设计者并没有限定,你可以从文件、压缩包、网络、数据库等等地方加载class字节码。
- 通过类的全限定名来获取定义此类的二进制字节流
- 将此二进制字节流所代表的静态存储结构转化成方法区的运行时数据结构
- 在内存中生成代表此类的java.lang.Class对象,作为该类访问入口.
- 验证:验证的目的是确保class文件的字节流中信息符合虚拟机的要求,不会危害虚拟机安全,使得虚拟机免受恶意代码的攻击,这一步至关重要。
- 文件格式验证
- 源数据验证
- 字节码验证
- 符号引用验证
- 准备:准备阶段的工作就是为类的静态变量分配内存并设为jvm默认的初值,对于非静态的变量,则不会为它们分配内存。静态变量的初值为jvm默认的初值,而不是我们在程序中设定的初值。(仅包含类变量,不包含实例变量).
- 解析:虚拟机将常量池中的符号引用替换为直接引用,解析动作主要针对类或接口,字段,类方法,方法类型等等。
- 初始化:在该阶段,才真正意义上的开始执行类中定义的java程序代码,该阶段会执行类构造器,并且在Java虚拟机规范中有明确的规定,在下面5种情况下必须对类进行初始化:
- 遇到new、getstatic、putstatic、invokestatic这4条字节码指令时,如果类没有进行过初始化,则需要先触发其初始化。
- 使用java.long.reflect包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化。
- 当初始化一个类的时候,如果发现其父类没有进行过初始化,则需要先触发其父类的初始化。
- 当虚拟机启动时,需要制定一个执行的主类(即main方法的类),虚拟机必须先初始化这个类。
- 使用动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后解析结果是REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个方法句柄对应的类没有进行初始化,则需要先触发其初始化。
- 使用:使用该类所提供的功能,其中包括主动引用和被动引用。
- 主动引用:
- 通过new关键字实例化对象、读取或设置类的静态变量、调用类的静态方法。
- 通过反射方式执行以上三种行为。
- 初始化子类的时候,会触发父类的初始化。
- 作为程序入口直接运行时(也就是直接调用main方法)。
- 被动引用:
- 引用父类的静态字段,只会引起父类的初始化,而不会引起子类的初始化。
- 定义类数组,不会引起类的初始化。
- 引用类的常量,不会引起类的初始化。
- 主动引用:
- 卸载:从内存中释放,在我之前写的垃圾回收机制(GC)总结一文中有介绍到方法区内存回收中对类的回收条件,这里再贴出来一下:
- 该类所有的实例都已经被回收,也就是Java堆中不存在该类的任何实例;
- 加载该类的ClassLoader已经被回收;
- 该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
5、小结
Java 的对象基本上都是在 jvm 的堆区中创建,在创建对象之前,会触发类加载(加载、连接、初始化),当类初始化完成后,根据类信息在堆区中实例化类对象,初始化非静态变量、非静态代码以及默认构造方法,当对象使用完之后会在合适的时候被jvm垃圾收集器回收。
PS:对象的生命周期只是类的生命周期中使用阶段的主动引用的一种情况(即实例化类对象),而类的整个生命周期则要比对象的生命周期长的多。