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

Java 創建對象的 4 種方式深度解析:new、反射、克隆、反序列化

     編輯:Java 綜合教學

Java 對象創建四大方式總覽

方式核心 API特點典型場景
1. new 關鍵字構造器最常用、直接、效能最好日常業務對象創建
2. 反射 newInstanceClass/Constructor.newInstance()動態類型、框架核心機制DI 容器、序列化框架、動態代理
3. clone()Object.clone()淺拷貝、需實現 Cloneable原型模式、效能敏感拷貝
4. 反序列化ObjectInputStream.readObject()跨 JVM、持久化恢復RPC、Session 複製、緩存恢復

方式一:new 關鍵字調用構造器

User user = new User("張三", 25); // 調用有參構造器
User user2 = new User(); // 調用無參構造器
  • 編譯期類型確定
  • JVM 指令:new + invokespecial (構造器)
  • 對象在堆中分配,引用在棧中

方式二:反射 newInstance()

Class.newInstance() (已淘汰 Java 9+)

// 僅能調用 public 無參構造器
Class<?> clazz = Class.forName("com.example.User");
User user = (User) clazz.newInstance(); // 拋 InstantiationException, IllegalAccessException

Constructor.newInstance() (推薦)

Class<?> clazz = Class.forName("com.example.User");

// 獲取所有構造器 (含 private)
Constructor<?>[] constructors = clazz.getDeclaredConstructors();

// 調用 public 無參
constructors[0].newInstance();

// 調用有參 public
constructors[1].newInstance("李四", 30);

// 調用 private 構造器 - 破壞封裝
constructors[2].setAccessible(true); // 關鍵:暴力反射
constructors[2].newInstance("王五", "內部構造");

反射創建對象流程圖

Class.forName() → Class 對象
    → getDeclaredConstructors() → Constructor[] 
    → setAccessible(true) (私有構造器)
    → newInstance(args...) → 對象實例

方式三:clone() 淺拷貝

實現步驟

  1. 實現 Cloneable 標記介面 (不實現拋 CloneNotSupportedException)
  2. 重寫 clone() 方法,改為 public、呼叫 super.clone()
public class PrototypeUser implements Cloneable {
    private String name;
    private int age;
    private Address address; // 引用類型
    
    @Override
    public PrototypeUser clone() {
        try {
            return (PrototypeUser) super.clone(); // 淺拷貝
        } catch (CloneNotSupportedException e) {
            throw new AssertionError(); // 不可能發生
        }
    }
}

淺拷貝 vs 深拷貝

PrototypeUser u1 = new PrototypeUser("張三", 25, new Address("北京"));
PrototypeUser u2 = u1.clone();

// 淺拷貝結果:
u1.name != u2.name        // true (String 不可變,安全)
u1.age != u2.age          // true (基本類型複製)
u1.address == u2.address  // true! 同一個 Address 物件 (引用複製)

// 深拷貝需手動實現:
@Override
public PrototypeUser clone() {
    PrototypeUser u = (PrototypeUser) super.clone();
    u.address = this.address.clone(); // 遞歸深拷貝
    return u;
}

clone() 細節

  • Object.clone() 為 native 方法 (C/C++ 實現)
  • 預設 protected,需重寫為 public
  • 不調用構造器,直接記憶體複製 (效能優於 new)
  • 單例模式需防禦性重寫 clone 拋異常

方式四:反序列化 readObject()

序列化寫入

public class SerializableUser implements Serializable {
    private static final long serialVersionUID = 1L; // 版本控制
    private String name;
    private transient String password; // 不序列化
    
    // 特殊方法:替換序列化物件
    private Object writeReplace() {
        return new SerializationProxy(this); // 代理模式
    }
}

反序列化讀取

// 寫入檔案
try (FileOutputStream fos = new FileOutputStream("user.ser");
     ObjectOutputStream oos = new ObjectOutputStream(fos)) {
    oos.writeObject(user);
}

// 讀取檔案
try (FileInputStream fis = new FileInputStream("user.ser");
     ObjectInputStream ois = new ObjectInputStream(fis)) {
    SerializableUser user = (SerializableUser) ois.readObject();
}

特殊方法:readResolve()

// 單例模式反序列化保護
private Object readResolve() {
    return Singleton.INSTANCE; // 返回現有單例,防止創建新實例
}

序列化流程圖

對象 → ObjectOutputStream.writeObject()
    → 判斷 writeReplace() → 序列化二進制流
    → 檔案/網路傳輸
    → ObjectInputStream.readObject()
    → 判斷 readResolve() → 還原對象

四種方式對比總結

維度new反射clone反序列化
效能最高較低 (存取檢查)高 (記憶體複製)最低 (IO + 解析)
構造器調用是是否否 (除非 readObject)
封裝性完全可破壞 (setAccessible)受控受控
跨進程否否否是
深拷貝支援手動手動手動實現自動 (含對象圖)

最佳實踐建議

  1. **90%+ 場景用 new** - 簡單、可讀、效能最優
  2. **框架開發用反射** - Spring Bean 實例化、MyBatis 結果映射
  3. **原型模式/大對象拷貝用 clone** - 配合深拷貝實現
  4. **分散式/持久化用反序列化** - 注意 serialVersionUID 版本管理
  5. **單例類** - 重寫 readResolve() 防反序列化破壞單例
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved