作爲一個Java開發者,一種面向對象的語言,我們每天都創建很多對象。但後續我們開發中,採用了spring的依賴管理系統,我們就很少自己去創建對象了,全部交給容器去託管,那麼本篇文章回源塑本,講述一下java中能夠創建一個對象的5中方法。
本文最大的特色是,我不僅給出案例,還給出對應的字節碼解釋,從底層來解釋現象
1.使用new關鍵字
這是最常見也是最簡單的創建對象的方式了。通過這種方式,我們可以調用任意的構造函數(無參的和帶參數的)。
Employee emp1 = new Employee();
字節碼:
0: new #19 // class com/fsx/demo/Employee
3: dup
4: invokespecial #21 // Method com/fsx/demo/Employee."":()V
2.使用Class類的newInstance方法
這個方法創建對象其實我們用得也比較多,but,這個newInstance方法調用無參的構造函數創建對象。所以類必須有public無參構造函數才行
Employee emp2 = (Employee) Class.forName("com.fsx.demo.mployee").newInstance();
或者
Employee emp2 = Employee.class.newInstance();
字節碼:
51: invokevirtual #70 // Method java/lang/Class.newInstance:()Ljava/lang/Object;
3.使用Constructor類的newInstance方法
和Class類的newInstance方法很像。但是它可以調用任意構造函數創建對象,包括私有的。(所以即使你私有了構造函數,spring還是可以給你創建對象)
事實上Class的newInstance方法內部調用Constructor的newInstance方法。這也是衆多框架,如Spring、Hibernate、Struts等直接使用後者的原因
4.使用clone方法
無論何時我們調用一個對象的clone方法,jvm就會創建一個新的對象,將前面對象的內容全部拷貝進去。用clone方法創建對象並不會調用任何構造函數。
備註:要使用clone方法,我們需要先實現Cloneable接口並實現其定義的clone方法。
Employee emp4 = (Employee) emp2.clone();
字節碼:
162: invokevirtual #87 // Method com/fsx/demo//Employee.clone ()Ljava/lang/Object;
5.使用反序列化
當我們序列化和反序列化一個對象,jvm會給我們創建一個單獨的對象。在反序列化時,jvm創建對象並不會調用任何構造函數。
備註:爲了反序列化一個對象,我們需要讓我們的類實現Serializable接口
ObjectInputStream in = new ObjectInputStream(new FileInputStream("data.obj"));
Employee emp5 = (Employee) in.readObject();
字節碼:
261: invokevirtual #118 // Method java/io/ObjectInputStream.readObject:()Ljava/lang/Object;
總結:
我們從上面的字節碼片段可以看到,除了第1個方法,其他4個方法全都轉變爲invokevirtual(創建對象的直接方法),第一個方法轉變爲兩個調用,new和invokespecial(構造函數調用)。
直接給出是否調用了構造函數的結論:
- 使用new關鍵字 } → 調用了構造函數
- 使用Class類的newInstance方法 } → 調用了構造函數
- 使用Constructor類的newInstance方法 } → 調用了構造函數
- 使用clone方法 } → 沒有調用構造函數
- 使用反序列化 } → 沒有調用構造函數