Java 对象相关高频面试题

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)执行时被回收。

详细拆解

  1. 回收的核心条件 :对象不再被任何可达引用 指向(GC Roots 到该对象不可达)。
    • 常见的 GC Roots:栈帧中的局部变量、方法区的静态变量、本地方法栈的变量等。
    • 当对象的所有引用都被置为 null、超出作用域、或被其他对象回收时,对象变为不可达。
  2. 回收的时机
    • GC 不是实时执行的,而是按需触发 (如堆内存不足、手动调用 System.gc()(仅建议,不保证立即执行))。
    • 对象被判定为垃圾后,会经历标记-清除复制标记-整理等GC算法,最终被回收内存。
  3. 特殊情况(对象复活)
    • 对象可以在 finalize() 方法中重新建立引用,暂时避免被回收,但 finalize() 只会执行一次,第二次GC会直接回收。
    • 注意:finalize() 已被 Java 9 标记为废弃,不推荐使用。
  4. 内存区域
    • 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,仅用于跟踪对象回收,必须配合引用队列使用。

五、面试答题技巧

  1. 创建对象:先讲5种主流方式,再补充原理和适用场景,面试官会觉得你基础扎实。
  2. 对象回收:重点讲"可达性分析"和GC触发时机,区分"对象变为垃圾"和"实际回收"的区别。
  3. 私有对象获取 :核心讲反射+setAccessible(true),结合代码示例,体现对反射的理解。
  4. 加分项 :提到反射的安全风险、单例破解、clone()的深浅克隆等延伸知识点,拉开和其他面试者的差距。

相关推荐
Scott9999HH3 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
腻害兔3 小时前
【若依项目-产品经理视角】深度拆解 RuoYi-Vue-Pro 商城模块:从商品管理到交易引擎,50 张表撑起一整套电商系统
java·大数据·vue.js·产品经理·ai编程
码智社4 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海4 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖5 小时前
JDK 26 新特性详解
java·开发语言
马优晨6 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区7 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大8 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai8 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
维天说8 小时前
CLI-Switch 2026年3月版历史设计:Hook、TTY 隔离与 JSON 状态
java·服务器·json