首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 創建對象的 5 種方式全解析:new、反射、克隆、反序列化與字節碼視角

     編輯:Java 相關知識

前言

Java 中創建對象不僅有 new 關鍵字,還有反射、克隆、反序列化等方式。本文通過代碼示例、字節碼分析、運行輸出對比,全方位解析 5 種創建方式的異同、底層機制及適用場景。

一、 五種方式總覽

方式核心 API是否調用構造器典型場景
1. new 關鍵字new Class()是(任意構造器)日常開發、依賴注入容器管理
2. Class.newInstance()Class.forName(...).newInstance()是(僅無參構造器)早期框架、簡單動態實例化
3. Constructor.newInstance()clazz.getConstructor().newInstance()是(任意構造器)Spring/Hibernate 等現代框架、動態代理
4. clone()obj.clone()否(直接內存拷貝)原型模式、拷貝大對象、性能敏感場景
5. 反序列化ObjectInputStream.readObject()否(直接恢復字段值)分布式傳輸、持久化恢復、深拷貝

二、 代碼實戰與字節碼證據

實體類定義

class Employee implements Cloneable, Serializable {
    private static final long serialVersionUID = 1L;
    private String name;

    public Employee() { System.out.println("Employee Constructor Called..."); }
    // getter/setter/hashCode/equals/toString 省略

    @Override
    protected Object clone() {
        try { return super.clone(); }
        catch (CloneNotSupportedException e) { throw new AssertionError(e); }
    }
}

五種方式調用

public class ObjectCreationDemo {
    public static void main(String[] args) throws Exception {
        // 1. new
        Employee e1 = new Employee();
        e1.setName("Naresh");
        System.out.println(e1 + ", hashcode: " + e1.hashCode());

        // 2. Class.newInstance()
        Employee e2 = (Employee) Class.forName("Employee").newInstance();
        // 或 Employee.class.newInstance();
        e2.setName("Rishi");
        System.out.println(e2 + ", hashcode: " + e2.hashCode());

        // 3. Constructor.newInstance()
        Constructor<Employee> ctor = Employee.class.getConstructor();
        Employee e3 = ctor.newInstance();
        e3.setName("Yogesh");
        System.out.println(e3 + ", hashcode: " + e3.hashCode());

        // 4. clone()
        Employee e4 = (Employee) e3.clone();
        e4.setName("Atul");
        System.out.println(e4 + ", hashcode: " + e4.hashCode());

        // 5. 反序列化
        try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("data.obj"))) {
            out.writeObject(e4);
        }
        try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("data.obj"))) {
            Employee e5 = (Employee) in.readObject();
            e5.setName("Akash");
            System.out.println(e5 + ", hashcode: " + e5.hashCode());
        }
    }
}

運行輸出

Employee Constructor Called...
Employee [name=Naresh], hashcode: -1968815046
Employee Constructor Called...
Employee [name=Rishi], hashcode: 78970652
Employee Constructor Called...
Employee [name=Yogesh], hashcode: -1641292792
Employee [name=Atul], hashcode: 2051657
Employee [name=Akash], hashcode: 63313419

關鍵觀察:前三種方式觸發構造器打印,後兩種無構造器調用,hashcode 完全不同,證明是獨立新對象。

字節碼層面差異

// 1. new 關鍵字
0: new #19 // class Employee
3: dup
4: invokespecial #21 // Method Employee."":()V

// 2. Class.newInstance()
51: invokevirtual #70 // Method Class.newInstance:()Ljava/lang/Object;

// 3. Constructor.newInstance()
111: invokevirtual #80 // Method Constructor.newInstance:([Ljava/lang/Object;)Ljava/lang/Object;

// 4. clone()
162: invokevirtual #87 // Method Employee.clone:()Ljava/lang/Object;

// 5. 反序列化
261: invokevirtual #118 // Method ObjectInputStream.readObject:()Ljava/lang/Object;

只有 new 生成 new + invokespecial(構造器調用)雙指令,其餘均為 invokevirtual 直接創建對象。

三、 深度機制剖析

1. Class.newInstance() vs Constructor.newInstance()

  • Class.newInstance() 內部調用 Constructor.newInstance(),但只能調用無參構造器,且拋出受檢異常(需聲明 throws)。
  • Constructor.newInstance() 支持帶參構造器、拋出未受檢異常(包裝在 InvocationTargetException),框架首選。
  • Java 9 起 Class.newInstance() 標註 @Deprecated,建議遷移至 Constructor.newInstance()。

2. clone():淺拷貝與深拷貝

  • 實現 Cloneable(標記接口),重寫 clone() 為 public。
  • 默認 super.clone() 為淺拷貝:基本類型複製值,引用類型複製引用(共享對象)。
  • 深拷貝需手動拷貝可變引用字段,或通過序列化實現通用深拷貝。
  • 不調用構造器,繞過構造器邏輯(如初始化、驗證),需謹慎使用。

3. 反序列化:繞過構造器的對象重建

  • 實現 Serializable,建議顯式聲明 serialVersionUID。
  • JVM 直接分配內存、按字段恢復值,不調用任何構造器(含父類構造器)。
  • 若需初始化邏輯,實現 private void readObject(ObjectInputStream in) 自定義反序列化。
  • 單例類若實現序列化,必須提供 readResolve() 返回唯一實例,否則破壞單例。

四、 選擇指南

需求推薦方式理由
常規業務對象new / 依賴注入可讀性最好、構造器驗證、IDE 支持
框架通用實例化Constructor.newInstance()支持帶參、異常處理友好、性能可接受
高性能拷貝大對象clone()(淺拷貝)避免構造器開銷、直接內存拷貝
通用深拷貝/跨進程傳輸序列化/反序列化自動處理對象圖、跨 JVM 边界
動態加載類Class.forName() + Constructor插件系統、熱加載

五、 總結

Java 提供多樣化的對象創建機制,new 是主流,反射支撐框架靈活性,clone 與序列化解決特定拷貝與持久化場景。理解是否調用構造器、字節碼指令差異、淺/深拷貝語義、序列化繞過構造器等本質區別,能避免初始化邏輯缺失、單例破壞、共享可變狀態等隱蔽 Bug。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved