標籤:return read creat for cep image 方法 clone 物件導向
Java 是物件導向的語言,不可避免的,“對象”這個概念是 Java 語言的核心部分,這裡來簡單討論一下在 Java 中建立一般對象的方法。
總結下來有以下4種建立對象的方法:
- 使用 new 關鍵字調用對象的構造器;
- 使用 Java 反射的 newInstance() 方法;
- 使用 Object 類的 clone() 方法;
- 使用物件流程 ObjectInputStream 的 readObject() 方法讀取序列化對象;
1. 使用 new 關鍵字
最常見的 Java 對象的構造方法,通過調用類提供的構造器建立對象。
2. 使用 newInstance() 方法
Java 反射中有一個 newInstance() 方法,可以建立對象,步驟如下:
- 擷取要建立的類的 Class 對象。
- 如果只需要調用這個類的存取權限為 public 無參構造器,直接使用 Class 類的執行個體方法 newInstance()。
- 擷取 Class 對象的構造器對象,通過調用 Class 類的執行個體方法 getDeclaredConstractors() 來擷取構造器對象的數組。(擷取所有構造器,無視存取權限的限制,數組順序按照代碼中的順序決定)
- 如果調用的構造器是 private 的,需要調用 Constractor 類的父類 AccessibleObject 類的執行個體方法 setAccessible(true) 來打破訪問限制。
- 使用 Constractor 類的執行個體方法 newInstance()。
範例程式碼:
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
備忘:
- 擷取 Class 對象的方法有3個,此處不多贅述。
- 擷取 Constractor 對象的方法有4個,此處不多贅述。
3. 使用 clone() 方法
Object 類是所有類的直接或間接父類,Object 類中提供了 執行個體方法 native(),在給定對象的基礎上,建立一個完全相同的對象。步驟如下:
- 想要使用 clone() 方法建立對象的類,實現 Cloneable 介面。
- 在類的內部,重寫 Object 類的 clone() 方法。
範例程式碼:
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() 方法,存取權限是 protected,所以如果不重寫 clone() 方法,是沒有許可權調用的。
- Object 類的 clone() 方法,是 native 方法。
4. 使用還原序列化的 readObject() 方法
這個方法一共分兩步:
- 將對象序列化,儲存到一個檔案中。
- 從檔案中還原序列化,得到類對象。
序列化:
- 想要序列化對象的類,實現 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 代碼創造的對象,所以要慎用;第四種方法在序列化的時候,需要實現特定的介面,而在還原序列化時就不關心這一點了,它是將對象暫存於其他媒介中,在還原序列化的時候將對象存於堆中。
第一彈:Java 中建立對象的4種方式