將會列舉5種方法去創建 Java 對象,以及他們如何與構造函數交互,並且會有介紹如何去使用這些方法的示例。
作為一個 Java 開發人員,我們每天都會創建大量的 Java 對象,但是我們通常會使用依賴管理系統去創建這些對象,例如 Spring 。然而,我們可以有更多的方式去創建對象,讓我們一起在文章中去學習這些方法吧。
這里列舉在 Java 中創建對象的五種方式,下面將介紹它們的示例,以及創建對象的行的字節碼。
使用 new 關鍵字 | 構造函數會被調用 |
使用 Class 類的 newInstance() | 構造函數會被調用 |
使用 Constructor 類的 newInstance() | 構造函數會被調用 |
使用 clone() 方法 | 無構造函數調用 |
使用 deserialization | 無構造函數調用 |
如果最后執行給定的程序,您將看到方法1、2、3使用構造函數創建對象,而4、5不調用構造函數創建對象。
1.使用 new 關鍵字
這是創建一個對象最通用、常規的方法,同時也是最簡單的方式。通過使用此方法,我們可以調用任何要調用的構造函數(默認使用無參構造函數)。
1 | Employee |
1 2 3 | 0:new #19 // class org/programming/mitra/exercises/Employe 3:dup 4: invokespecial #21 // Method org/programming/mitra/exercises/Employee."": ()V |
2.使用 Class 類的 newInstance()
我們能夠使用 Class 類的 newInstance() 方法創建對象。這個 newInstance() 方法將調用無參構造方法去創建一個對象。
在下述代碼中,我們通過調用 newInstance() 去創建一個對象:
Employee emp2 = (Employee) Class.forName("org.programming.mitra.exercises.Employee").newInstance();
或者:
1 | Employee emp2 = Employee.class.newInstance(); |
1 | 51: invokevirtual #70 // Method java/lang/Class.newInstance:()Ljava/lang/Object; |
3.使用 Constructor 類的 newInstance()
與 Class 類中的 newInstance() 方法相似,在此我們將使用 java.lang.reflect.Constructor 類中的 newInstance() 方法創建對象。通過使用這個 newInstance() 方法我們能夠調用有參構造函數和私有構造函數。
1 2 | Constructor constructor = Employee.class.getConstructor(); Employee emp3 = constructor.newInstance(); |
1 | 111: invokevirtual #80 // Method java/lang/reflect/Constructor.newInstance:([Ljava/lang/Object;)Ljava/lang/Object; |
這兩個 newInstance() 方法都被稱為創建對象的反射方法。實際上, Class 類的 newInstance() 方法內部使用 Constructor 類的 newInstance() 方法。這就是為什么后者更受歡迎,並且也被 Spring、Hibernate、Structs 等不同的框架所使用的原因。更多關於上述兩個 newInstance() 方法的區別請閱讀 Creating objects through Reflection in Java with Example。
4.使用 Clone() 方法
每當我們對任何對象調用 clone() 時,jvm 都會為我們創建一個新對象,並將前一個對象的所有內容復制到其中。使用 clone 方法創建對象不會調用任何構造函數。
要在對象上使用 clone() 方法,我們需要實現 Cloneable 並在其中定義 clone() 方法。
1 | Employee emp4 = (Employee) emp3.clone(); |
1 | 162: invokevirtual #87 // Method org/programming/mitra/exercises/Employee.clone ()Ljava/lang/Object; |
Java 克隆是 Java 社區中最有爭議的話題,它確實有它的缺點,但仍然是創建任何對象副本的最流行和最簡單的方法,直到該對象完全填充 Java 克隆的強制條件。我在3篇長的[ Java Cloning Series ](https://programmingmitra.blogspot.in/search/label/Java Cloning)中詳細介紹了克隆,其中包括(Java Cloning And Types Of Cloning (Shallow And Deep) In Details With Example, Java Cloning - Copy Constructor Versus Cloning, Java Cloning - Even Copy Constructors Are Not Sufficient),如果你想了解更多關於克隆的知識,請繼續閱讀它們。
5.使用 deserialization
每當我們序列化和反序列化對象時,JVM會為我們創建了一個獨立的對象。在 deserialization 中,JVM 不使用任何構造函數來創建對象。
對於序列化對象,我們需要在類中實現 Serializable 接口。
1 2 | ObjectInputStream in = new ObjectInputStream(new FileInputStream("data.obj")); Employee emp5 = (Employee) in.readObject(); |
1 | 261: invokevirtual #118 // Method java/io/ObjectInputStream.readObject:()Ljava/lang/Object; |
正如我們在上面的字節碼片段中看到的,4個方法都被調用並轉換為 invokevirtual(對象創建由這些方法直接處理),但第一個方法被轉換為兩個調用:一個是 new,另一個是 invokespecial(調用構造函數)。
在我的文章中,曾經討論過序列化和反序列化的細節,如果你想了解更多相關知識,請繼續閱讀:Everything About Java Serialization Explained With Example。
示例
創建對象的 Employee 類:
class Employee implements Cloneable, Serializable
private static final long serialVersionUID = 1L;
private String name
public Employee() {
System.out.println("Employee Constructor Called...");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Employee other = (Employee) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
@Override
public String toString() {
return "Employee [name=" + name + "]";
}
@Override
public Object clone() {
Object obj = null;
try {
obj = super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return obj;
}
}
在下面的 Java 程序中,我們將以5種方式創建 Employee 對象。您也可以在GitHub上找到源代碼。
public class ObjectCreation {
public static void main(String... args) throws Exception {
// By using new keyword
Employee emp1 = new Employee();
emp1.setName("Naresh");
System.out.println(emp1 + ", hashcode : " + emp1.hashCode());
// By using Class class's newInstance() method
Employee emp2 = (Employee) Class.forName("org.programming.mitra.exercises.Employee")
.newInstance();
// Or we can simply do this
// Employee emp2 = Employee.class.newInstance();
emp2.setName("Rishi");
System.out.println(emp2 + ", hashcode : " + emp2.hashCode());
// By using Constructor class's newInstance() method
Constructor<Employee> constructor = Employee.class.getConstructor();
Employee emp3 = constructor.newInstance();
emp3.setName("Yogesh");
System.out.println(emp3 + ", hashcode : " + emp3.hashCode());
// By using clone() method
Employee emp4 = (Employee) emp3.clone();
emp4.setName("Atul");
System.out.println(emp4 + ", hashcode : " + emp4.hashCode());
// By using Deserialization
// Serialization
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("data.obj"));
out.writeObject(emp4);
out.close();
//Deserialization
ObjectInputStream in = new ObjectInputStream(new FileInputStream("data.obj"));
Employee emp5 = (Employee) in.readObject();
in.close();
emp5.setName("Akash");
System.out.println(emp5 + ", hashcode : " + emp5.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
原文:https://dzone.com/articles/5-different-ways-to-create-objects-in-java-with-ex
作者:Naresh Joshi
譯者:陳苓洪
9月福利,關注公眾號
后台回復:004,領取8月翻譯集錦!
往期福利回復:001,002, 003即可領取!