Java 對象創建四大方式總覽
| 方式 | 核心 API | 特點 | 典型場景 |
| 1. new 關鍵字 | 構造器 | 最常用、直接、效能最好 | 日常業務對象創建 |
| 2. 反射 newInstance | Class/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() 淺拷貝
實現步驟
- 實現
Cloneable 標記介面 (不實現拋 CloneNotSupportedException)
- 重寫
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) | 受控 | 受控 |
| 跨進程 | 否 | 否 | 否 | 是 |
| 深拷貝支援 | 手動 | 手動 | 手動實現 | 自動 (含對象圖) |
最佳實踐建議
- **90%+ 場景用 new** - 簡單、可讀、效能最優
- **框架開發用反射** - Spring Bean 實例化、MyBatis 結果映射
- **原型模式/大對象拷貝用 clone** - 配合深拷貝實現
- **分散式/持久化用反序列化** - 注意 serialVersionUID 版本管理
- **單例類** - 重寫 readResolve() 防反序列化破壞單例