Java 对象相关高频面试题
一、Java 创建对象有哪些方式?(除了 new 还有什么?)
核心回答(面试必背 5 种主流方式)
Java 中创建对象的方式不止 new,常见的有 5 种,每种的原理和适用场景不同:
1. 使用 new 关键字(最常用、最基础)
这是最直观的创建方式,会调用类的构造方法,在堆中分配内存并初始化对象。
java
// 调用无参构造
User user = new User();
// 调用有参构造
User user = new User("张三", 20);
原理:JVM 会检查类是否已加载、链接、初始化,若未加载则先执行类加载,然后在堆中分配内存、初始化对象头、执行构造方法,最后返回对象引用。
2. 使用 Class.newInstance()(反射方式,Java 9 已废弃)
通过反射调用类的无参构造方法创建对象,要求类必须有可访问的无参构造。
java
// 方式1:Class.forName()获取Class对象,再调用newInstance()
Class<?> clazz = Class.forName("com.example.User");
User user = (User) clazz.newInstance();
// 方式2:通过类名.class获取Class对象
User user = (User) User.class.newInstance();
原理 :反射机制,通过类的字节码对象,调用无参构造创建对象。
注意 :Java 9 开始废弃,推荐使用 Constructor.newInstance() 替代。
3. 使用 Constructor.newInstance()(反射方式,推荐)
通过 java.lang.reflect.Constructor 类,调用类的任意构造方法(有参/无参)创建对象,灵活性更高。
java
// 获取有参构造
Constructor<User> constructor = User.class.getConstructor(String.class, int.class);
// 调用构造创建对象
User user = constructor.newInstance("张三", 20);
原理 :反射的核心API,支持调用任意权限的构造(通过 setAccessible(true) 突破私有构造限制),是框架(Spring、MyBatis)创建对象的核心方式。
4. 使用 Object.clone()(克隆方式)
调用对象的 clone() 方法,创建一个新的、内容完全相同的对象 ,不会调用构造方法。
前提 :类必须实现 Cloneable 接口,并重写 clone() 方法(否则抛出 CloneNotSupportedException)。
java
public class User implements Cloneable {
private String name;
// 重写clone方法
@Override
protected User clone() throws CloneNotSupportedException {
return (User) super.clone();
}
}
// 使用clone创建对象
User user1 = new User("张三", 20);
User user2 = user1.clone(); // 新对象,和user1内容相同,地址不同
原理 :JVM 直接复制当前对象的内存数据,在堆中分配新内存,不执行构造方法,分为浅克隆 和深克隆。
- 浅克隆:只复制对象本身,引用类型成员仍指向原对象的引用
- 深克隆:完全复制对象及其所有引用类型成员,两个对象完全独立
5. 使用反序列化(I/O 流方式)
将序列化后的对象(字节流)通过 ObjectInputStream.readObject() 反序列化,创建一个新对象,不会调用构造方法 。
前提 :类必须实现 Serializable 接口。
java
// 序列化:将对象写入文件
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user.txt"));
oos.writeObject(new User("张三", 20));
oos.close();
// 反序列化:从文件读取对象,创建新对象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.txt"));
User user = (User) ois.readObject();
ois.close();
原理:JVM 读取字节流,在堆中分配内存、恢复对象状态,不执行构造方法,常用于对象持久化、网络传输。
补充:其他小众创建方式(面试加分项)
- 使用
Unsafe类 :通过sun.misc.Unsafe.allocateInstance()直接在堆中分配内存,完全不调用构造方法,是 JVM 底层操作,普通开发禁止使用,仅用于框架(如Hibernate)。 - 使用工厂方法/建造者模式 :本质是封装了
new操作,属于设计模式,不是独立的创建方式。 - 数组创建 :
new User[10]会创建一个数组对象,数组元素为null,不会创建 User 对象。
二、New 出的对象什么时候回收?
核心回答
new 出来的对象,当没有任何有效引用指向它时 ,会被判定为垃圾,在 JVM 垃圾回收(GC)执行时被回收。
详细拆解
- 回收的核心条件 :对象不再被任何可达引用 指向(GC Roots 到该对象不可达)。
- 常见的 GC Roots:栈帧中的局部变量、方法区的静态变量、本地方法栈的变量等。
- 当对象的所有引用都被置为
null、超出作用域、或被其他对象回收时,对象变为不可达。
- 回收的时机 :
- GC 不是实时执行的,而是按需触发 (如堆内存不足、手动调用
System.gc()(仅建议,不保证立即执行))。 - 对象被判定为垃圾后,会经历标记-清除 、复制 、标记-整理等GC算法,最终被回收内存。
- GC 不是实时执行的,而是按需触发 (如堆内存不足、手动调用
- 特殊情况(对象复活) :
- 对象可以在
finalize()方法中重新建立引用,暂时避免被回收,但finalize()只会执行一次,第二次GC会直接回收。 - 注意:
finalize()已被 Java 9 标记为废弃,不推荐使用。
- 对象可以在
- 内存区域 :
new出来的对象存储在堆内存中,GC 回收的就是堆内存中的对象。- 栈内存中的局部变量引用,会在方法执行结束后自动销毁,对应的堆对象若没有其他引用,会被回收。
代码示例
java
public void test() {
// 创建对象,user是栈中的局部变量,指向堆中的User对象
User user = new User();
// 方法执行结束,user局部变量销毁,堆中User对象无引用,变为垃圾,等待GC回收
}
三、如何获取私有对象?
核心回答
通过Java反射机制,可以突破访问权限限制,获取/操作类的私有对象(私有成员变量、私有方法、私有构造)。
详细拆解+代码示例
1. 获取私有成员变量(私有对象)
通过 Field 类,结合 setAccessible(true) 突破私有权限,获取/修改私有变量的值。
java
public class User {
// 私有成员变量(私有对象)
private String name = "张三";
private int age = 20;
}
public class ReflectTest {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
User user = new User();
// 1. 获取Class对象
Class<User> clazz = User.class;
// 2. 获取私有成员变量name
Field nameField = clazz.getDeclaredField("name");
// 3. 突破私有访问权限(核心步骤)
nameField.setAccessible(true);
// 4. 获取私有变量的值
String name = (String) nameField.get(user);
System.out.println("获取到私有变量name:" + name); // 输出:张三
// 5. 修改私有变量的值
nameField.set(user, "李四");
System.out.println("修改后name:" + user.getName()); // 输出:李四
}
}
2. 获取私有方法(私有对象的方法)
通过 Method 类,突破权限,调用私有方法。
java
public class User {
// 私有方法
private void sayHello(String msg) {
System.out.println("私有方法sayHello:" + msg);
}
}
public class ReflectTest {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
User user = new User();
Class<User> clazz = User.class;
// 获取私有方法sayHello
Method method = clazz.getDeclaredMethod("sayHello", String.class);
// 突破权限
method.setAccessible(true);
// 调用私有方法
method.invoke(user, "你好"); // 输出:私有方法sayHello:你好
}
}
3. 获取私有构造方法,创建私有对象
通过 Constructor 类,突破权限,调用私有构造创建对象(常用于单例模式的破解)。
java
public class Singleton {
// 私有构造,禁止外部new
private Singleton() {}
private static Singleton instance = new Singleton();
public static Singleton getInstance() {
return instance;
}
}
public class ReflectTest {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Class<Singleton> clazz = Singleton.class;
// 获取私有构造
Constructor<Singleton> constructor = clazz.getDeclaredConstructor();
// 突破权限
constructor.setAccessible(true);
// 调用私有构造,创建新的Singleton对象(破解单例)
Singleton newInstance = constructor.newInstance();
System.out.println(newInstance == Singleton.getInstance()); // 输出:false,两个不同对象
}
}
补充说明
- 核心原理 :
setAccessible(true)只是关闭了Java语言的访问权限检查,并没有修改JVM的权限,因此可以突破private限制,但会破坏封装性,存在安全风险。 - 安全限制 :在安全管理器(SecurityManager)开启的环境中,
setAccessible(true)可能会被禁止(如Applet、沙箱环境)。 - 应用场景:框架(Spring、MyBatis、Jackson)的底层核心,用于依赖注入、对象序列化、ORM映射等。
四、补充高频延伸面试题
1. clone() 和 new 创建对象的区别?
| 对比维度 | new |
clone() |
|---|---|---|
| 构造方法 | 会调用构造方法 | 不会调用构造方法 |
| 初始化 | 执行构造中的初始化逻辑 | 直接复制内存,不执行构造 |
| 性能 | 较慢(需执行构造、初始化) | 快(直接内存复制) |
| 前提 | 无,只要有构造即可 | 必须实现Cloneable接口 |
| 引用类型 | 新建引用,指向新对象 | 浅克隆共享原引用,深克隆新建引用 |
2. 如何避免单例被反射破解?
- 在私有构造中添加判断,若已创建实例则抛出异常:
java
private Singleton() {
if (instance != null) {
throw new RuntimeException("单例模式禁止反射创建");
}
}
- 使用枚举单例,枚举天然防止反射和反序列化破解,是最佳单例实现。
3. 对象的引用类型有哪些?对GC的影响?
- 强引用 :默认引用,
Object obj = new Object(),只要强引用存在,对象永远不会被GC回收。 - 软引用 :
SoftReference,内存不足时才会被回收,用于缓存。 - 弱引用 :
WeakReference,下次GC时一定会被回收,用于WeakHashMap。 - 虚引用 :
PhantomReference,仅用于跟踪对象回收,必须配合引用队列使用。
五、面试答题技巧
- 创建对象:先讲5种主流方式,再补充原理和适用场景,面试官会觉得你基础扎实。
- 对象回收:重点讲"可达性分析"和GC触发时机,区分"对象变为垃圾"和"实际回收"的区别。
- 私有对象获取 :核心讲反射+
setAccessible(true),结合代码示例,体现对反射的理解。 - 加分项 :提到反射的安全风险、单例破解、
clone()的深浅克隆等延伸知识点,拉开和其他面试者的差距。