Skip to content

对象实例化方式

创建对象的方式有哪些

很多人以为 new 是创建对象的唯一方式。实际上,Java 中创建对象有多种方式,new 只是最常见的一种。

常见创建方式

1. new 关键字(最常用)

java
public class NewObject {
    public static void main(String[] args) {
        // 最常见的方式
        Student s = new Student();
    }
}

2. 反射:Class.newInstance()(已淘汰)

java
public class NewInstanceDemo {
    public static void main(String[] args) throws Exception {
        // JDK 9 之前推荐的方式
        Student s = Student.class.newInstance();
        // 等价于 new Student()
    }
}

newInstance() 实际上调用的是无参构造器,而且会抛出已检查异常,已被 Constructor.newInstance() 替代。

3. 反射:Constructor.newInstance()(推荐)

java
public class ConstructorNewInstance {
    public static void main(String[] args) throws Exception {
        // 获取构造器
        Constructor<Student> constructor = Student.class.getConstructor(String.class, int.class);
        // 调用有参构造器
        Student s = constructor.newInstance("Alice", 20);
    }
}

Constructor.newInstance() 是目前反射创建对象的推荐方式:

  • 支持调用有参构造器
  • 支持调用私有构造器(setAccessible(true)
  • 对已检查异常更友好

4. clone() 方法

java
public class CloneDemo {
    public static void main(String[] args) throws CloneNotSupportedException {
        Student original = new Student("Bob", 22);

        // clone() 需要类实现 Cloneable 接口
        // 并且重写 Object.clone()
        Student copy = (Student) original.clone();

        // 关键:clone() 创建的是原对象的副本
        // 不会调用任何构造器
        // 两个对象是独立的存在
        System.out.println(copy == original);  // false
    }
}

class Student implements Cloneable {
    private String name;
    private int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();  // 浅拷贝
    }
}

clone() 不会调用任何构造器,复制的是对象的字节流——这是一种浅拷贝

5. 反序列化

java
public class SerializationDemo {
    public static void main(String[] args) throws Exception {
        Student original = new Student("Carol", 25);

        // 序列化到字节数组
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(original);
        oos.close();

        // 反序列化创建新对象
        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bais);
        Student copy = (Student) ois.readObject();

        // 反序列化不会调用构造器
        // 创建的是堆中全新的对象
        System.out.println(copy == original);  // false
    }
}

6. Unsafe.allocateInstance()(底层方式)

java
public class UnsafeAllocDemo {
    public static void main(String[] args) throws Exception {
        // 直接在堆中分配内存,不调用构造器
        Unsafe unsafe = getUnsafe();

        // 在堆中分配 Student 大小的内存,但不调用任何构造器
        Student s = (Student) unsafe.allocateInstance(Student.class);

        // 此时 s 的字段全是默认值(name=null, age=0)
        System.out.println(s.getName());  // null
        System.out.println(s.getAge());   // 0
    }
}

Unsafe.allocateInstance() 是最底层的对象创建方式,完全跳过构造器,对象字段全是默认值。

典型使用场景:高性能对象池(如 Disruptor)、延迟构造。

创建方式的对比

方式是否调用构造器调用链典型场景
new✅ 是构造器日常开发
Constructor.newInstance()✅ 是构造器反射
Class.newInstance()✅ 是无参构造器JDK 8 及之前
clone()❌ 否不调用对象复制
反序列化❌ 否不调用对象持久化
Unsafe.allocateInstance()❌ 否不调用高性能/对象池

底层创建过程(new 关键字)

无论用哪种方式创建对象,new 字节码指令的核心步骤是相同的:

new 关键字


检查类是否已加载(未加载 → 类加载)


在堆中分配内存

      ├── TLAB:线程本地分配缓冲
      └── 指针碰撞:Eden 区有空闲指针


零值初始化(所有字段设为默认值)


设置对象头(Mark Word + 类型指针)


执行 `<init>` 方法(构造器)


返回对象引用

对象创建的完整字节码过程

javap -c 看看 new 的字节码:

java
public class Student {
    private String name;
    private int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

编译后:

java
public static void main(java.lang.String[]);
    Code:
       0: new           #3          // new Student,分配内存
       3: dup                        // 复制引用(供构造器使用)
       4: ldc           #4          // 加载字符串常量 "Alice"
       6: bipush        20         // 加载整数常量 20
       8: invokespecial  #5          // 调用构造器 Student()
      11: astore_1                 // 存到局部变量
      12: return

本节小结

Java 中有 6 种对象创建方式:

  • new:最常用
  • 反射(Constructor):最灵活
  • clone():对象复制
  • 反序列化:持久化/网络传输
  • Unsafe.allocateInstance():跳过构造器,高性能场景

理解这些创建方式,有助于理解框架底层(如 Spring 如何通过反射创建 Bean、MyBatis 如何通过构造器创建对象实例)。

下一节,我们来看 字节码视角:对象创建过程,深入理解 new 字节码指令的完整执行过程。

基于 VitePress 构建