第001彈:Java 中建立對象的4種方式

  Java 是面向對象的語言,不可避免的,「對象」這個概念是 Java 語言的核心部分,這裏來簡單討論一下在 Java 中建立通常對象的方法。數組

  總結下來有如下4種建立對象的方法:ide

  • 使用 new 關鍵字調用對象的構造器;
  • 使用 Java 反射的 newInstance() 方法;
  • 使用 Object 類的 clone() 方法;
  • 使用對象流 ObjectInputStream 的 readObject() 方法讀取序列化對象;

 

1.      使用 new 關鍵字

 

  最多見的 Java 對象的構造方法,經過調用類提供的構造器建立對象。spa

 

 

2.      使用 newInstance() 方法

 

  Java 反射中有一個 newInstance() 方法,能夠建立對象,步驟以下:3d

  • 獲取要建立的類的 Class 對象。
  • 若是隻須要調用這個類的訪問權限爲 public 無參構造器,直接使用 Class 類的實例方法 newInstance()。
  • 獲取 Class 對象的構造器對象,經過調用 Class 類的實例方法 getDeclaredConstractors() 來獲取構造器對象的數組。(獲取全部構造器,無視訪問權限的限制,數組順序按照代碼中的順序決定)
  • 若是調用的構造器是 private 的,須要調用 Constractor 類的父類 AccessibleObject 類的實例方法 setAccessible(true) 來打破訪問限制。
  • 使用 Constractor 類的實例方法 newInstance()。

 

  示例代碼:code

 1 public class MethodNewInstance {
 2 
 3     public static void main(String[] args) throws Exception {
 4 
 5         // 獲得類對象
 6         Class<?> clazz = Class.forName("com.gerrard.create.method_newInstance.ObjectToCreate");
 7         // 類對象的 newInstance() 方法,只能調用公有的無參構造器
 8         clazz.newInstance();
 9 
10         // 獲得構造器對象數組(無論是私有仍是公有的構造器)
11         Constructor<?>[] cons = clazz.getDeclaredConstructors();
12         cons[1].newInstance();
13         cons[2].newInstance("Gerrard");
14         // 先打破私有構造器不可訪問的限制
15         cons[0].setAccessible(true);
16         cons[0].newInstance("Gerrard", "Info");
17     }
18 }
MethodNewInstance

 

  備註:orm

  • 獲取 Class 對象的方法有3個,此處很少贅述。
  • 獲取 Constractor 對象的方法有4個,此處很少贅述。

 

 

3.      使用 clone() 方法

 

  Object 類是全部類的直接或間接父類,Object 類中提供了 實例方法 clone(),在給定對象的基礎上,建立一個徹底相同的對象。步驟以下:對象

  • 想要使用 clone() 方法建立對象的類,實現 Cloneable 接口。
  • 在類的內部,重寫 Object 類的 clone() 方法。

 

  示例代碼:blog

 1 public class ObjectToCreate implements Cloneable {
 2 
 3     // 重寫 Object 類的 clone() 方法(native 方法)
 4     public ObjectToCreate clone() {
 5         ObjectToCreate obj = null;
 6         try {
 7             obj = (ObjectToCreate) super.clone();
 8         } catch (CloneNotSupportedException e) {
 9             // 沒有實現 Cloneable 接口就會拋出這個異常
10             e.printStackTrace();
11         }
12         return obj;
13     }
14 }
ObjectToCreate

 

  備註:接口

  • 沒有實現 Cloneable 接口,會拋出 CloneNotSupportedException 異常。
  • Object 類提供的 clone() 方法,是淺複製。
  • Object 類的 clone() 方法,是 native 方法。

 

 

4.      使用反序列化的 readObject() 方法

 

  這個方法一共分兩步:ci

  • 將對象序列化,存儲到一個文件中。
  • 從文件中反序列化,獲得類對象。

 

  序列化:

  • 想要序列化對象的類,實現 Serializable 接口。
  • 使用文件輸出流 FileOutputStream 建立存儲序列化以後對象的文件。
  • 使用對象輸出流 ObjectOutputStream 的實例方法 writeObject(obj)
  • 判斷類中是否存在,名爲writeReplace(),返回類型爲 Object 的方法,如有,寫入這個方法的返回值;不然,寫入 obj 對象。

 

  反序列化:

  • 使用文件輸入流 FileInputStream 找到存儲序列化對象的文件。
  • 使用對象輸入流 ObjectInputStream 的實例方法 readObject()
  • 判斷類中是否存在,名爲readResolve(),返回類型爲 Object 的方法,如有讀取這個對象;不然,反序列化文件中的對象流。

 

 

  示例代碼:

 1 public class ObjectToCreate implements Serializable {
 2 
 3     private static final long serialVersionUID = 1L;
 4     
 5     private Object writeReplace(){
 6         return new Integer(1);
 7     }
 8     
 9     private Object readResolve(){
10         return new Double(2);
11     }
12 }
ObjectToCreate
 1 public class MethodSerialable {
 2 
 3     public static void main(String[] args) {
 4 
 5         // 默認路徑是項目的根路徑
 6         final String fileName = "./file/serialable.txt";
 7 
 8         ObjectToCreate o1 = new ObjectToCreate();
 9 
10         // 序列化
11         try (FileOutputStream fos = new FileOutputStream(fileName);
12                 ObjectOutputStream oos = new ObjectOutputStream(fos);) {
13             oos.writeObject(o1);
14         } catch (FileNotFoundException e) {
15             e.printStackTrace();
16         } catch (IOException e) {
17             e.printStackTrace();
18         }
19     }
20 }
MethodSerialable
 1 public class MethodAntiSerialable {
 2 
 3     public static void main(String[] args) {
 4         // 默認路徑是項目的根路徑
 5         final String fileName = "./file/serialable.txt";
 6         Object o2 = null;
 7         // 反序列化
 8         try (FileInputStream fio = new FileInputStream(fileName); ObjectInputStream ois = new ObjectInputStream(fio);) {
 9             o2 = ois.readObject();
10         } catch (FileNotFoundException e) {
11             e.printStackTrace();
12         } catch (IOException e) {
13             e.printStackTrace();
14         } catch (ClassNotFoundException e) {
15             e.printStackTrace();
16         }
17         System.out.println(o2);
18     }
19 }
MethodAntiSerialable

 

  備註:

  • 在類中,writeReplace() readResoleve() 是兩個很是特殊的方法,其特徵簽名須要嚴格限制:方法名限定,參數個數限定爲0,返回類型必須是 Object,不能爲 Object 的子類,可是能夠拋出不一樣的異常。訪問修飾符沒有限制,但通常推薦爲 private,防止誤操做。其特殊的地方還在於將其設爲 private 方法,沒有其餘方法調用的狀況下,編譯器不會發出警告。

 

 

5.      總結

 

  Java 建立對象的4種方法:第一種是最經常使用的;第二種方法深刻至源碼會指向 sun.reflect.ConstructorAccessor 類,JDK 中彷佛沒有提供繼續深刻下去的源碼,可是既然是調用構造器的方法,那麼與第一種方法同樣,建立的對象是存儲在堆(Heap)中的;第三種方法是要實現特定的接口才可使用,並且是經過調用 native 方法,也就是非 Java 代碼(很大多是 C)實現的,也就是說,這個方法產生的對象,可能不會被 GC 回收(我的的想法),由於 GC 是用來回收 Java 代碼創造的對象,因此要慎用;第四種方法在序列化的時候,須要實現特定的接口,而在反序列化時就不關心這一點了,它是將對象暫存於其餘媒介中,在反序列化的時候將對象存於堆中。

相關文章
相關標籤/搜索