身為 Java 開發者,我們每天都在建立大量物件,多半仰賴 Spring 等框架管理。但 Java 其實提供了五種根本的物件建立方式,理解它們有助於掌握 JVM 底層機制,也能在面試與框架原理分析中脫穎而出。
最常見、最直接的方式,可呼叫任意建構子。
Employee emp1 = new Employee();
位元碼指令:new + invokespecial(呼叫建構子)。
透過反射呼叫無參建構子。Java 9 後標記為過時,建議改用 Constructor.newInstance()。
Employee emp2 = Employee.class.newInstance();
// 或
Employee emp2 = (Employee) Class.forName("pkg.Employee").newInstance();
位元碼:invokevirtual 呼叫 Class.newInstance()。
反射 API 的核心,支援帶參建構子,也是 Spring、Hibernate 等框架的首選實例化方式。
Constructor<Employee> constructor = Employee.class.getConstructor();
Employee emp3 = constructor.newInstance();
位元碼:invokevirtual 呼叫 Constructor.newInstance()。
淺層複製現有物件,不呼叫任何建構子。類別需實作 Cloneable 介面並覆寫 clone()。
Employee emp4 = (Employee) emp3.clone();
位元碼:invokevirtual 呼叫 clone()。
從位元組流重建物件,同樣不經建構子。類別需實作 Serializable 介面。
ObjectInputStream in = new ObjectInputStream(new FileInputStream("data.obj"));
Employee emp5 = (Employee) in.readObject();
位元碼:invokevirtual 呼叫 ObjectInputStream.readObject()。
只有 new 指令會轉換為 new + invokespecial;其餘四種皆為 invokevirtual 動態派發。這也是為何框架偏好反射實例化的原因——統一了物件創建入口,便於攔截與增強(如 AOP 代理、依賴注入)。
class Employee implements Cloneable, Serializable {
private String name;
public Employee() { System.out.println("Employee Constructor Called..."); }
// getter/setter、equals、hashCode、toString、clone 略
}
public class ObjectCreation {
public static void main(String[] args) throws Exception {
// 1. new
Employee emp1 = new Employee(); emp1.setName("Naresh");
// 2. Class.newInstance()
Employee emp2 = Employee.class.newInstance(); emp2.setName("Rishi");
// 3. Constructor.newInstance()
Constructor<Employee> c = Employee.class.getConstructor();
Employee emp3 = c.newInstance(); emp3.setName("Yogesh");
// 4. clone
Employee emp4 = (Employee) emp3.clone(); emp4.setName("Atul");
// 5. 反序列化
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("data.obj"));
out.writeObject(emp4); out.close();
ObjectInputStream in = new ObjectInputStream(new FileInputStream("data.obj"));
Employee emp5 = (Employee) in.readObject(); in.close();
emp5.setName("Akash");
}
}
Employee Constructor Called... // new
Employee [name=Naresh], hashcode: -1968815046
Employee Constructor Called... // Class.newInstance()
Employee [name=Rishi], hashcode: 78970652
Employee Constructor Called... // Constructor.newInstance()
Employee [name=Yogesh], hashcode: -1641292792
Employee [name=Atul], hashcode: 2051657 // clone:未呼叫建構子
Employee [name=Akash], hashcode: 63313419 // 反序列化:未呼叫建構子
注意:clone 與反序列化不會觸發建構子,這在某些初始化邏輯(如 final 欄位賦值、註冊監聽器)中需特別留意。
| 方式 | 是否呼叫建構子 | 適用場景 |
|---|---|---|
| new | 是 | 日常開發、明確型別 |
| Class.newInstance() | 是(無參) | 舊版反射、簡單實例化 |
| Constructor.newInstance() | 是(可帶參) | 框架開發、動態載入 |
| clone() | 否 | 原型模式、效能敏感複製 |
| 反序列化 | 否 | 持久化、跨 JVM 傳遞 |