Java 大厂面试高频题:别再只会 new 了!Java 创建对象竟然有这么多种方式
在学习 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();
}
}
输出:
有参构造方法执行了
你好,我是李四
代码解释如下。
首先获取 Person 的 Class 对象:
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,说明 p1 和 p2 指向两个不同的对象。
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,还可以通过反射、克隆、反序列化和工厂方法等方式获得对象;它们最大的区别在于创建过程是否调用构造方法、是否依赖已有对象,以及是否真正生成新的对象实例。
更多推荐




所有评论(0)