Java创建对象

178 阅读3分钟

Java创建对象的方式

Java中创建对象的方式包含以下五种:

  1. 使用 new 关键字
  2. 使用 Class 类的 newInstance() 方法(反射)
  3. 使用 Constructor 类的 newInstance() 方法(反射)
  4. 使用 Clone
  5. 使用 序列化机制

new 关键字

Java 代码在通过编译时,遇到 new 关键字会同时生成 new 和 invokespecial 两条字节码指令,在这种情况下一般 new 指令之后会接着执行 构造函数,也就是 Class文件中的 <init>() 方法创建对象。 那么通过 new 关键字创建对象的过程是怎么样的呢?通常情况下,HotSpot 虚拟机对于对象的创建是这样的(摘自《深入理解Java虚拟机》):

  1. 当 Java 虚拟机遇到一条字节码 new 指令时,首先将去检查这个指令的参数是否能在常量池中定位到一个 类的符号引用,并且检查这个符号引用代表的类是否已经被加载、解析和初始化过。如果没有,必须执行类加载。
  2. 类加载检查后,Java 虚拟机会为 新生对象分配内存。对象所需内存的大小在类加载完成后便确定,为对象分配内存就当于把一块确定大小的内存块从 Java 堆内存中取出来。分配内存的方式(指针碰撞、空闲列表)由 Java 堆是否规整决定,Java 堆是否规整由采用的垃圾收集器是否带有空间压缩整理所决定。
    内存分配在并发环境下并非线程安全的,通常通过以下两种方式进行解决:
    一是:采用 CAS 配上失败重试 的方式保证更新操作的原子性
    二是:把内存分配的动作按照线程划分在不同的空间之中进行,即每个线程在 Java 堆中预先分配一小块内存,称为 本地线程分配缓冲(TLAB),哪个线程要分配内存,就在哪个线程的本地缓冲区中分配,只有本地缓冲区用完了,分配新的缓存区时才需要同步锁定。
  3. 分配完内存后初始化零值。这步操作保证了对象的实例字段在 Java 代码中可以不赋零值就直接使用,使程序能访问到这些字段的数据类型所对应的零值。
  4. 设置对象头。包括这个对象是哪个类的实例、如何才能找到类的元数据信息、对象的 GC 分代年龄、锁设置等信息。
  5. 执行 Class 文件中的 <init>() 方法,即构造函数,完成对象的初始化,这样就完成了一个对象的创建。

Class 类

  1. Class 类的构造器是私有的,只能通过 JVM 虚拟机进行创建。构造对象时,传入一个类加载器进行。
  2. newInstance()方法,底层就是调用 无参构造对象 的 newInstance()。所以,本质上 Class 对象想要创建实例,其实都是通过构造器对象。如果没有空参构造器对象,就无法使用 clazz.newInsatnce(), 必须要获取其他有参的构造对象然后调用构造对象的 newInstance()。
  3. 反射创建实例,其实是调用 clazz.newInstance(),底层还是调用 Contructor 对象的 newInstance()。 image.png

Constructor 类

Constructor类的 newInstance() 方法相比 Class类的 newInstance() 方法更加强大些,可以通过这个 newInstance() 方法调用有参数的和私有的构造函数

Clone 和 序列化

实现 Cloneable 接口并重写 clone() 方法
实现 Serializable接口
clone() 方法创建对象和序列号机制创建对象都不会调用构造函数。

参考资料

1.第二个回答