在学习 Java 时,我们最先接触的对象创建方式通常是:

Person person = new Person();

因此,很多初学者会认为 Java 创建对象只能使用 new

实际上,Java 中常见的对象创建方式不止一种。除了直接使用 new,还可以通过反射、克隆、反序列化、工厂方法等方式获得对象。

本文将介绍 Java 中常见的 6 种对象创建方式:

1. 使用 new 关键字
2. 使用反射调用构造方法
3. 使用 Class.newInstance()
4. 使用 clone() 克隆对象
5. 使用反序列化创建对象
6. 使用工厂方法获得对象

在理解这些方式之前,需要先明确:对象、引用变量和类不是同一个概念。

一、什么是创建对象?

先定义一个简单的 Person 类:

class Person {
    private String name;

    public Person() {
        System.out.println("Person 构造方法执行了");
    }

    public Person(String name) {
        this.name = name;
        System.out.println("有参构造方法执行了");
    }

    public void sayHello() {
        System.out.println("你好,我是" + name);
    }
}

创建对象时通常写:

Person person = new Person();

这句代码可以拆成三部分:

Person:变量的类型
person:引用变量
new Person():创建 Person 对象

真正创建出来的对象是:

new Person()

person 只是一个引用变量,它保存了对这个对象的引用。

可以类比为:

Person 类:房屋设计图
Person 对象:根据设计图建出的房子
person 变量:房子的门牌号

接下来看看 Java 创建对象的不同方式。


二、方式一:使用 new 关键字

使用 new 是最常见、最直接的对象创建方式。

Person person = new Person();

也可以调用有参构造方法:

Person person = new Person("张三");

执行 new Person("张三") 时,Java 大致会完成以下操作:

1. 在堆内存中为 Person 对象分配空间;
2. 为成员变量设置默认值;
3. 执行成员变量的显式初始化;
4. 执行构造代码块;
5. 调用对应的构造方法;
6. 返回新对象的引用。

使用:

public class Test {
    public static void main(String[] args) {
        Person person = new Person("张三");
        person.sayHello();
    }
}

输出:

有参构造方法执行了
你好,我是张三

特点

语法简单;
可读性强;
会调用构造方法;
是日常开发中最常用的方式。

大多数情况下,创建普通业务对象都应该优先使用 new


三、方式二:使用反射调用构造方法

反射允许程序在运行过程中动态获取类的信息,并创建对象。

推荐使用:

Constructor.newInstance()

示例:

import java.lang.reflect.Constructor;

public class Test {
    public static void main(String[] args) throws Exception {
        Class<Person> clazz = Person.class;

        Constructor<Person> constructor =
                clazz.getDeclaredConstructor(String.class);

        Person person = constructor.newInstance("李四");

        person.sayHello();
    }
}

输出:

有参构造方法执行了
你好,我是李四

代码解释如下。

首先获取 PersonClass 对象:

Class<Person> clazz = Person.class;

然后获取参数为 String 的构造方法:

Constructor<Person> constructor =
        clazz.getDeclaredConstructor(String.class);

最后通过构造方法创建对象:

Person person = constructor.newInstance("李四");

如果要调用无参构造方法,可以写:

Constructor<Person> constructor =
        Person.class.getDeclaredConstructor();

Person person = constructor.newInstance();

为什么需要反射创建对象?

因为有些场景在编写代码时,并不知道最终要创建哪个类。

例如,配置文件中写着:

com.example.service.UserService

程序运行时读取这个类名,再动态创建对象。

很多框架内部都大量使用了反射,例如:

Spring
Spring Boot
MyBatis
Hibernate
JUnit
Jackson

Spring 扫描到一个带有 @Service 注解的类后,可以通过反射创建它的对象,并交给 IoC 容器管理。

特点

可以动态决定创建哪个类;
可以调用无参或有参构造方法;
适合框架和底层工具开发;
代码比 new 复杂;
反射调用通常比直接 new 慢。

在现代 Java 中,通过反射创建对象时,通常推荐使用 Constructor.newInstance()


四、方式三:使用 Class.newInstance()

以前还可以通过 Class 对象的 newInstance() 方法创建对象:

Person person = Person.class.newInstance();

或者:

Class<?> clazz = Class.forName("Person");
Person person = (Person) clazz.newInstance();

这段代码会调用 Person 的无参构造方法。

但是这种方式存在明显限制:

只能调用无参构造方法;
异常处理不够清晰;
无法灵活选择构造方法;
该方法已经被废弃。

因此,现在不建议继续使用:

clazz.newInstance();

推荐改成:

Person person = Person.class
        .getDeclaredConstructor()
        .newInstance();

也就是说,Class.newInstance() 可以作为面试知识点了解,但新项目中应该使用 Constructor.newInstance()


五、方式四:使用 clone() 克隆对象

clone() 的作用不是从零开始创建一个全新状态的对象,而是根据已有对象复制出一个新对象。

首先让类实现 Cloneable 接口:

class Person implements Cloneable {
    private String name;

    public Person(String name) {
        this.name = name;
        System.out.println("构造方法执行了");
    }

    public String getName() {
        return name;
    }

    @Override
    public Person clone() throws CloneNotSupportedException {
        return (Person) super.clone();
    }
}

使用:

public class Test {
    public static void main(String[] args)
            throws CloneNotSupportedException {

        Person p1 = new Person("王五");
        Person p2 = p1.clone();

        System.out.println(p1 == p2);
        System.out.println(p2.getName());
    }
}

输出:

构造方法执行了
false
王五

注意,构造方法只执行了一次。

执行:

Person p2 = p1.clone();

时,并不会再次调用 Person 的构造方法。

同时:

p1 == p2

结果为 false,说明 p1p2 指向两个不同的对象。

clone() 默认是浅拷贝

如果 Person 中包含一个引用类型成员:

class Address {
    String city;
}

class Person implements Cloneable {
    String name;
    Address address;

    @Override
    public Person clone() throws CloneNotSupportedException {
        return (Person) super.clone();
    }
}

执行:

Person p2 = p1.clone();

会得到:

p1 和 p2 是不同的 Person 对象;
p1.address 和 p2.address 指向同一个 Address 对象。

也就是说,super.clone() 默认实现的是浅拷贝。

如果需要深拷贝,还需要继续复制内部对象。

特点

根据已有对象复制新对象;
通常不会调用构造方法;
默认是浅拷贝;
需要实现 Cloneable;
适合复制已有对象状态。

六、方式五:使用反序列化创建对象

序列化是把对象转换成字节数据,反序列化则是把字节数据恢复成对象。

首先让类实现 Serializable

import java.io.Serializable;

class Person implements Serializable {
    private static final long serialVersionUID = 1L;

    private String name;

    public Person(String name) {
        this.name = name;
        System.out.println("构造方法执行了");
    }

    public String getName() {
        return name;
    }
}

把对象序列化到文件:

import java.io.FileOutputStream;
import java.io.ObjectOutputStream;

Person person = new Person("赵六");

try (ObjectOutputStream output =
             new ObjectOutputStream(
                     new FileOutputStream("person.dat"))) {

    output.writeObject(person);
}

再通过反序列化恢复对象:

import java.io.FileInputStream;
import java.io.ObjectInputStream;

Person person;

try (ObjectInputStream input =
             new ObjectInputStream(
                     new FileInputStream("person.dat"))) {

    person = (Person) input.readObject();
}

System.out.println(person.getName());

反序列化会根据文件中的数据重新创建一个 Person 对象。

对于普通实现了 Serializable 的类,反序列化时通常不会调用该可序列化类的构造方法。

使用场景

反序列化常见于:

读取持久化对象;
网络传输;
缓存恢复;
消息通信;
对象深拷贝。

不过,Java 原生序列化存在性能、安全性和兼容性方面的问题。现代项目中,更常见的是使用:

JSON
Protocol Buffers
Kryo
Avro

等格式传输或保存数据。

特点

根据已有的字节数据恢复对象;
通常不会调用当前类的构造方法;
类需要实现 Serializable;
可能存在安全和版本兼容问题。

不要反序列化不可信来源的数据,否则可能带来安全风险。


七、方式六:使用工厂方法获得对象

工厂方法是实际开发中非常常见的一种对象获取方式。

例如:

class Person {
    private String name;

    private Person(String name) {
        this.name = name;
    }

    public static Person create(String name) {
        return new Person(name);
    }
}

使用:

Person person = Person.create("小明");

表面上没有直接使用 new,而是通过:

Person.create()

获得对象。

不过需要注意,工厂方法内部可能仍然使用了 new

return new Person(name);

因此,从 JVM 底层角度看,最终可能还是由 new 创建对象;但从调用者角度看,对象是通过工厂方法获得的。

工厂方法有什么好处?

1. 方法名更清晰
Person.createAdmin();
Person.createGuest();
Person.fromDatabase(data);

比多个含义不清晰的构造方法更容易理解。

2. 可以返回缓存对象

例如:

Integer a = Integer.valueOf(100);
Integer b = Integer.valueOf(100);

Integer.valueOf() 可能返回缓存中的已有对象,而不是每次都创建新对象。

3. 可以返回子类对象
Animal animal = AnimalFactory.create("dog");

工厂内部可以根据参数返回:

new Dog()

或者:

new Cat()

调用者不需要关心具体实现类。

4. 可以统一控制创建过程

工厂方法可以加入:

参数校验;
缓存;
对象池;
权限检查;
日志记录;
依赖注入。

常见例子

Java 中有很多工厂方法:

Integer.valueOf(10);
List.of("A", "B");
LocalDate.of(2026, 7, 12);
Optional.of("Java");
Executors.newFixedThreadPool(5);

特点

隐藏对象创建细节;
方法名可以表达业务含义;
可以返回缓存对象或子类对象;
有利于降低代码耦合;
内部不一定每次都创建新对象。

严格来说,“通过工厂方法获得对象”不一定意味着每次都创建了新对象,因为它可能返回已有对象。


八、使用 Unsafe 创建对象

Java 底层还存在一种特殊方式:通过 Unsafe 分配对象。

概念上可以写成:

Person person =
        (Person) unsafe.allocateInstance(Person.class);

它可以直接为对象分配内存,但不会正常调用构造方法。

Unsafe 属于底层内部 API,可能破坏 Java 的封装性和对象初始化规则,一般业务开发中不应该使用。

它更可能出现在:

JVM 底层工具;
高性能框架;
序列化框架;
代理框架;
特殊测试场景。

对于初学者,只需要知道这种方式存在即可,不建议实际使用。


九、这些方式是否都会调用构造方法?

不同创建方式对构造方法的调用情况不同。

创建方式 是否调用构造方法 常见用途
new 普通对象创建
Constructor.newInstance() 反射、框架
Class.newInstance() 会调用无参构造 旧式反射,不推荐
clone() 通常不会 复制已有对象
反序列化 当前可序列化类通常不会 恢复对象
工厂方法 取决于内部实现 封装创建逻辑
Unsafe.allocateInstance() 不会 底层框架

这是面试中经常考察的一个重点:

使用 new 和反射调用构造器会执行构造方法;使用 clone() 和普通反序列化恢复当前类对象时,通常不会调用该类构造方法。


十、创建对象和获得对象有什么区别?

严格来说,有些方式是“真正创建新对象”,有些方式只是“获得一个对象”。

例如:

Integer number = Integer.valueOf(100);

valueOf() 可能返回缓存中的已有对象,因此不保证一定创建新对象。

再例如 Spring:

UserService userService =
        applicationContext.getBean(UserService.class);

这里是从 Spring 容器获取对象,对象可能早已创建完成,并不是此时才创建。

因此,下面两种说法存在细微区别:

创建对象:明确产生一个新的对象实例;
获得对象:拿到一个对象引用,可能是新对象,也可能是已有对象。

不过在初级面试题中,工厂方法、Spring 容器等方式经常也会被归入“对象创建方式”进行讨论。


十一、实际开发应该选择哪种方式?

普通业务对象

优先使用:

new Person();

代码直观、简单。

框架动态创建对象

使用反射:

clazz.getDeclaredConstructor().newInstance();

复制已有对象

可以使用:

拷贝构造方法;
clone();
手动字段复制。

相比 clone(),很多项目更喜欢使用拷贝构造方法,因为逻辑更加明确:

Person copied = new Person(original);

从文件、缓存或网络恢复对象

可以使用反序列化,但实际项目中通常优先考虑 JSON、Protobuf 等方案。

隐藏复杂创建逻辑

使用工厂方法:

Person person = PersonFactory.create(type);

十二、常见面试问题

1. Java 创建对象有哪些方式?

可以回答:

Java 创建或获得对象的常见方式包括:使用 new 关键字、使用反射调用构造方法、通过 clone() 克隆对象、通过反序列化恢复对象,以及通过静态工厂方法或工厂模式获得对象。其中,Class.newInstance() 已经被废弃,反射创建对象推荐使用 Constructor.newInstance()。

2. 哪些方式不会调用构造方法?

可以回答:

使用 clone() 克隆对象通常不会调用构造方法,通过 Java 反序列化恢复当前可序列化类对象时通常也不会调用该类构造方法,Unsafe.allocateInstance() 同样不会调用构造方法。

3. newInstance() 推荐使用哪一个?

推荐:

clazz.getDeclaredConstructor().newInstance();

不推荐:

clazz.newInstance();

因为后者已经被废弃,并且只能调用无参构造方法。


十三、总结

Java 中常见的对象创建或获取方式包括:

1. new:最常用,会调用构造方法;
2. Constructor.newInstance():通过反射调用构造方法;
3. Class.newInstance():旧式反射方式,已经废弃;
4. clone():复制已有对象,通常不调用构造方法;
5. 反序列化:从字节数据恢复对象;
6. 工厂方法:隐藏创建细节,可能创建新对象,也可能返回已有对象;
7. Unsafe:底层特殊方式,不建议普通开发使用。

初学者最需要掌握的是:

new
反射
clone
反序列化
工厂方法

最后用一句话概括:

Java 创建对象不只有 new,还可以通过反射、克隆、反序列化和工厂方法等方式获得对象;它们最大的区别在于创建过程是否调用构造方法、是否依赖已有对象,以及是否真正生成新的对象实例。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐