当前位置: 首页 > news >正文

Java 大厂面试高频题:别再只会 new 了!Java 创建对象竟然有这么多种方式

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

Personperson=newPerson();

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

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

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

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

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

一、什么是创建对象?

先定义一个简单的Person类:

classPerson{privateStringname;publicPerson(){System.out.println("Person 构造方法执行了");}publicPerson(Stringname){this.name=name;System.out.println("有参构造方法执行了");}publicvoidsayHello(){System.out.println("你好,我是"+name);}}

创建对象时通常写:

Personperson=newPerson();

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

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

真正创建出来的对象是:

newPerson()

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

可以类比为:

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

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


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

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

Personperson=newPerson();

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

Personperson=newPerson("张三");

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

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

使用:

publicclassTest{publicstaticvoidmain(String[]args){Personperson=newPerson("张三");person.sayHello();}}

输出:

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

特点

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

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


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

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

推荐使用:

Constructor.newInstance()

示例:

importjava.lang.reflect.Constructor;publicclassTest{publicstaticvoidmain(String[]args)throwsException{Class<Person>clazz=Person.class;Constructor<Person>constructor=clazz.getDeclaredConstructor(String.class);Personperson=constructor.newInstance("李四");person.sayHello();}}

输出:

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

代码解释如下。

首先获取PersonClass对象:

Class<Person>clazz=Person.class;

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

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

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

Personperson=constructor.newInstance("李四");

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

Constructor<Person>constructor=Person.class.getDeclaredConstructor();Personperson=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()方法创建对象:

Personperson=Person.class.newInstance();

或者:

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

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

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

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

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

clazz.newInstance();

推荐改成:

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

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


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

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

首先让类实现Cloneable接口:

classPersonimplementsCloneable{privateStringname;publicPerson(Stringname){this.name=name;System.out.println("构造方法执行了");}publicStringgetName(){returnname;}@OverridepublicPersonclone()throwsCloneNotSupportedException{return(Person)super.clone();}}

使用:

publicclassTest{publicstaticvoidmain(String[]args)throwsCloneNotSupportedException{Personp1=newPerson("王五");Personp2=p1.clone();System.out.println(p1==p2);System.out.println(p2.getName());}}

输出:

构造方法执行了 false 王五

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

执行:

Personp2=p1.clone();

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

同时:

p1==p2

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

clone() 默认是浅拷贝

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

classAddress{Stringcity;}classPersonimplementsCloneable{Stringname;Addressaddress;@OverridepublicPersonclone()throwsCloneNotSupportedException{return(Person)super.clone();}}

执行:

Personp2=p1.clone();

会得到:

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

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

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

特点

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

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

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

首先让类实现Serializable

importjava.io.Serializable;classPersonimplementsSerializable{privatestaticfinallongserialVersionUID=1L;privateStringname;publicPerson(Stringname){this.name=name;System.out.println("构造方法执行了");}publicStringgetName(){returnname;}}

把对象序列化到文件:

importjava.io.FileOutputStream;importjava.io.ObjectOutputStream;Personperson=newPerson("赵六");try(ObjectOutputStreamoutput=newObjectOutputStream(newFileOutputStream("person.dat"))){output.writeObject(person);}

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

importjava.io.FileInputStream;importjava.io.ObjectInputStream;Personperson;try(ObjectInputStreaminput=newObjectInputStream(newFileInputStream("person.dat"))){person=(Person)input.readObject();}System.out.println(person.getName());

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

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

使用场景

反序列化常见于:

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

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

JSON Protocol Buffers Kryo Avro

等格式传输或保存数据。

特点

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

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


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

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

例如:

classPerson{privateStringname;privatePerson(Stringname){this.name=name;}publicstaticPersoncreate(Stringname){returnnewPerson(name);}}

使用:

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

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

Person.create()

获得对象。

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

returnnewPerson(name);

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

工厂方法有什么好处?

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

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

2. 可以返回缓存对象

例如:

Integera=Integer.valueOf(100);Integerb=Integer.valueOf(100);

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

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

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

newDog()

或者:

newCat()

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

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

工厂方法可以加入:

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

常见例子

Java 中有很多工厂方法:

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

特点

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

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


八、使用 Unsafe 创建对象

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

概念上可以写成:

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

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

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

它更可能出现在:

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

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


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

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

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

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

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


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

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

例如:

Integernumber=Integer.valueOf(100);

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

再例如 Spring:

UserServiceuserService=applicationContext.getBean(UserService.class);

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

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

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

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


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

普通业务对象

优先使用:

newPerson();

代码直观、简单。

框架动态创建对象

使用反射:

clazz.getDeclaredConstructor().newInstance();

复制已有对象

可以使用:

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

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

Personcopied=newPerson(original);

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

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

隐藏复杂创建逻辑

使用工厂方法:

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

http://www.cnnetsun.cn/news/3327120.html

相关文章:

  • 团队协作中的技术栈选择:平衡效率与可维护性
  • 62-LangGraph有状态AI工作流-状态图-条件边-审批Agent实现
  • Windows系统文件blb_ps.dll丢失找不到问题解决
  • HarmonyKit | 鸿蒙开发:@kit.UIDesignKit 组件体系完整解读
  • Palworld存档编辑完全掌握:从零开始实现游戏数据可视化修改
  • RapidOCR:突破性多引擎架构实现微秒级实时文字识别
  • 如何在3小时内构建零样本图像分类系统?CLIP ViT-Base-Patch16实战指南
  • 什么是 AGENTS.md?如何为 Claude Code、Codex、Cursor 及其他 AI 编程代理创建它
  • Vendure插件开发实战:5个高级场景深度解析
  • 企业获客软件获客效率技术对比:获客工具与CRM的操作链路与数据能力差异分析
  • 2026餐饮店怎么选GEO服务商:6类核心能力对比分析,全程0代码
  • Open Design 中文保姆级下载安装教程及入门教程(2026最新版)
  • Linux游戏性能翻倍秘籍:lsfg-vk无损帧生成技术深度解析
  • A/B 测试 3 大统计陷阱:辛普森悖论如何扭曲实验结论
  • LiveAutoRecord:跨平台直播自动录制的终极解决方案
  • 从0到1部署WebWalker:LLM网页遍历智能体的本地化与规模化实践指南
  • Glorytun安全配置详解:保护你的UDP隧道通信的完整策略
  • QEMU 8.2.2 Windows 部署 UOS ARM 虚拟机:3步脚本配置与 SSH 端口 2222 映射
  • 3个秘籍让旧版Python在Windows上重获新生
  • 告别 Demo 陷阱:用 LangGraph 构建可观测的生产级 Agent 工作流
  • Inventory Kamera未来路线图:计划中的新功能和改进方向 [特殊字符]
  • 2026年AI论文生成工具哪家强?5款热门工具实测对比
  • 终极指南:1500+ AI代理技能库如何彻底改变你的开发工作流
  • 如何快速上手Laguna-XS-2.1-bf16:5分钟完成MLX模型安装与推理
  • 探索CountryPicker核心功能:国旗显示、本地化名称与ISO标准支持
  • AI 写达梦/金仓代码疯狂翻车?我手撸了一套“信创专属 AI 生成引擎”,把大模型的方言幻觉按在地上摩擦!
  • 关于算法优化的量化评估与测试框架设计7
  • BSC信道下重复码纠错性能仿真 P124302178 李云徽
  • 如何在macOS上构建Windows应用运行环境的3种终极方案
  • Ream插件开发指南:打造属于你的自定义功能扩展