創建 Java 對象有幾種寫法?
- 2020 年 4 月 8 日
- 筆記
創建 Java 對象有幾種寫法?

創建 Java 對象,跟"回"字一樣也有好幾種寫法。雖然簡單,但是也涉及到了幾個 Java 關鍵的基礎知識,比如反射、克隆、序列化與反序列化,所以面試也經常會遇到,然後不斷擴展的問。
第一種:通過 new 關鍵字創建
這一種沒啥好說的,從學 Java 第一天就不停的跟兩樣東西打交道,一個的是 new 關鍵字,一個是NullPointerException ?。程式碼如下
測試對象程式碼 Person.java
package cn.coder4j.blog.demo.code.crete.object.method; import org.apache.commons.lang3.builder.ReflectionToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; /** * 人員類 */ public class Person implements Cloneable { /** * 年齡 */ private Integer age; /** * 用戶名 */ private String name; public Person(Integer age, String name) { this.age = age; this.name = name; } public Person() { } /** * Getter method for property <tt>age</tt>. * * @return property value of age */ public Integer getAge() { return age; } /** * Setter method for property <tt>age</tt>. * * @param age value to be assigned to property age */ public void setAge(Integer age) { this.age = age; } /** * Getter method for property <tt>name</tt>. * * @return property value of name */ public String getName() { return name; } /** * Setter method for property <tt>name</tt>. * * @param name value to be assigned to property name */ public void setName(String name) { this.name = name; } @Override public String toString() { return ReflectionToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE); } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } }
創建對象程式碼
/** * 通過 new 關鍵字去創建對象 */ @Test public void testNew() { Person person = new Person(); person.setAge(18); person.setName("kiwi"); System.out.println(person); }
第二種:通過 class 的 newInstance_() 方法_
這裡就涉及到了 Java 基礎裡面的反射知識了,大多數框架也是通過這種方式創建的對象,比如說 spring。通過反射拿到 class 對象,再直接調用 newInstance() 方法就可以直接創建出一個對象。獲得 class 對象的方法也有好幾種,這裡直接通過類來獲得。程式碼如下:
創建對象程式碼
/** * 通過類反射 */ @Test public void testClassReflect() throws IllegalAccessException, InstantiationException { Person person = Person.class.newInstance(); person.setAge(18); person.setName("kiwi"); System.out.println(person); }
第三種:通過 constructor 的 newInstance() 方法
與第二種方法一樣,同樣是通過反射。也是拿到 class 對象,不過這裡,拿到對象後,又多了一步去拿構造函數。可能有人覺得疑問了,與第二種達到的結果是一樣的,但是還要多寫一些程式碼,為什麼還要用他。其實對於有無參構造函數的類來說,兩種方法都一樣,哪個都可以使用。但是是對於只有有參構造函數的類來說,只能使用第三種。因為第二種無法指定構建函數。所以因為大多數框架使用的都是第二種包括 spring,所以當你的 bean 沒有無參構造函數時,框架就會報錯,他是不會幫你用第三種的。程式碼如下:
創建對象程式碼
/** * 通過構建函數反射創建 * @throws NoSuchMethodException * @throws IllegalAccessException * @throws InvocationTargetException * @throws InstantiationException */ @Test public void testConstructReflect() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { Constructor<Person> constructor = Person.class.getConstructor(); Person person = constructor.newInstance(); person.setAge(18); person.setName("kiwi"); System.out.println(person); } /** * 通過有參構造函數反射創建 * @throws NoSuchMethodException * @throws IllegalAccessException * @throws InvocationTargetException * @throws InstantiationException */ @Test public void testConstructWithParamReflect() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { Constructor<Person> constructor = Person.class.getConstructor(Integer.class, String.class); Person person = constructor.newInstance(18, "kiwi"); System.out.println(person); }
第四種:通過 clone
可以看到 Person.java 實現了介面 Cloneable 並覆蓋了 clone 方法(其實只是調用了父類方法),然後調用對象的 clone 方法就可以創建一個一毛一樣的對象。需要注意的是這是是淺克隆,啥是淺克隆?淺克隆是指複製出來的對象的所有變數都含有與原來的對象相同的值,而所有的對其他對象的引用仍然指向原來的對象。也就是說修改克隆後的對象中的引用變數,也會導致原對象也產生變化。深克隆反之。程式碼如下:
創建對象程式碼
/** * 通過克隆 */ @Test public void testClone() throws CloneNotSupportedException { Person person = new Person(); person.setAge(18); person.setName("kiwi"); Person personClone = (Person)person.clone(); System.out.println(personClone); }
第五種:通過反序列化
序列化與反序列化有很多種,包括 json,xml 其實都是。這裡使用的是 java 原生的 Serializable 實現的序列化。很多 rpc 框架,比如 dubbo 使用的就是這種方式,這裡需要類實現 jdk 的 Serializable 介面,並且給他一個 serialVersionUID 屬性。程式碼如下:
測試對象程式碼 PersonDto.java
package cn.coder4j.blog.demo.code.crete.object.method; import org.apache.commons.lang3.builder.ReflectionToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import java.io.Serializable; /** * 人員類 */ public class PersonDto implements Serializable { private static final long serialVersionUID = 3911118195380172132L; /** * 年齡 */ private Integer age; /** * 用戶名 */ private String name; public PersonDto(Integer age, String name) { this.age = age; this.name = name; } public PersonDto() { } /** * Getter method for property <tt>age</tt>. * * @return property value of age */ public Integer getAge() { return age; } /** * Setter method for property <tt>age</tt>. * * @param age value to be assigned to property age */ public void setAge(Integer age) { this.age = age; } /** * Getter method for property <tt>name</tt>. * * @return property value of name */ public String getName() { return name; } /** * Setter method for property <tt>name</tt>. * * @param name value to be assigned to property name */ public void setName(String name) { this.name = name; } @Override public String toString() { return ReflectionToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE); } }
創建對象程式碼
/** * 通過反序列化 * @throws IOException * @throws ClassNotFoundException */ @Test public void testSerializable() throws IOException, ClassNotFoundException { PersonDto person = new PersonDto(); person.setAge(18); person.setName("kiwi"); // 把對象序列化 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person")); oos.writeObject(person); oos.close(); // 反序列化 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person")); PersonDto personSeri = (PersonDto) ois.readObject(); System.out.println(personSeri); }
這裡就說完了,比較常用的幾種 Java 對象創建方法了。由此可以看出單例模式是無法保證系統中只有一個對象的。