對象的複製(clone、序列化)

那就先看是clone方法複製對象  摘自 java.lang.Object.clone()分析html

首先,看一下源碼:
public class Object  {
    protected native Object clone() throws CloneNotSupportedException;
}
  由源代碼咱們會發現:
  第一:Object類的clone()方法是一個native方法,native方法的效率通常來講都是遠高於Java中的java

非native方法。這也解釋了爲何要用Object中clone()方法而不是先new一個類,而後把原始對象中的數據庫

信息複製到新對象中,雖然這也實現了clone功能。(JNI是Java Native Interface的 縮寫。從Java安全

1.1開始,Java Native Interface (JNI)標準成爲java平臺的一部分,它容許Java代碼和其餘語言寫的服務器

代碼進行交互。JNI一開始是爲了本地已編譯語言,尤爲是C和C++而設計的,可是它並不妨礙你使用其餘網絡

語言,只要調用約定受支持就能夠了。使用java與本地已編譯的代碼交互,一般會喪失平臺可移植性。併發

可是,有些狀況下這樣作是能夠接受的,甚至是必須的,好比,使用一些舊的庫,與硬件、操做系統進app

行交互,或者爲了提升程序的性能。JNI標準至少保證本地代碼能工做在任何Java 虛擬機實現下。)
  第二:Object類中的 clone()方法被protected修飾符修飾。這也意味着若是要應用 clone()方 法ide

,必須繼承Object類,在 Java中全部的類是缺省繼承 Object類的,也就不用關心這點了。而後重載函數

clone()方法。還有一點要考慮的是爲了讓其它類能調用這個 clone類的 clone()方法,重載以後要把

clone()方法的屬性設置爲 public。
  第三:Object.clone()方法返回一個Object對象。咱們必須進行強制類型轉換才能獲得咱們須要的

類型。
  淺層複製與深層複製概念:
  淺層複製: 被複制的對象的全部成員屬性都有與原來的對象相同的值,而全部的對其餘對象的引用

仍然指向原來的對象。換言之,淺層複製僅僅複製所考慮的對象(對象屬性、數據),而不復制它所引用

的對象(對象地址)。
  深層複製:被複制對象的全部變量都含有與原來的對象相同的值,除去那些引用其餘對象的變量。

那些引用其餘對象的變量將指向被複制過的新對象,而不是原有的那些被引用的對象。換言之,深層復

制要複製的對象引用的對象(對象屬性、數據)都複製一遍。
  Java中對象的克隆
  1)在派生類中實現Cloneable藉口。
  2)爲了獲取對象的一份拷貝,咱們能夠利用Object類的clone方法。
  3)在派生類中覆蓋積累的clone方法,聲明爲public。
  4)在派生類的clone方法中,調用super.clone()。
實現Cloneable接口
  首先,看一下源碼:  
1 public interface Cloneable {
2 }
  咱們奇怪的發現Cloneable居然是空的,那麼咱們爲何要實現Cloneable接口呢?其實Cloneable接口僅僅是一個標誌,並且這個標誌也僅僅是針對 Object類中 clone()方法的,若是 clone 類沒有實現 Cloneable 接口,並調用了 Object 的 clone() 方法(也就是調用了 super.Clone() 方法),那麼Object 的 clone() 方法就會拋出 CloneNotSupportedException 異常。

public class Person {
  private String name;
  private int age;
  public Person(){}
  public Person(String name,int age){
   this.name=name;
   this.age=age;
  }
  public Object clone(){
   Object o=null;
   try {
    o=super.clone();
   } catch (CloneNotSupportedException e) {
    e.printStackTrace();
   }
   return o;
  }
  public String getName() {
   return name;
  }
  public void setName(String name) {
   this.name = name;
  }
  public int getAge() {
   return age;
  }
  public void setAge(int age) {
   this.age = age;
  }
  public static void main(String[] args) {
   Person p1=new Person("zhangsan",18);
   Person p2=(Person)p1.clone();
   p2.setName("lis");
   p2.setAge(20);
   System.out.println("name="+p1.getName()+",age="+p1.getAge());
   System.out.println("name="+p2.getName()+",age="+p2.getAge());
   //修改p2後,沒有對p1產生影響。上面輸出爲:
   //name=zhangsan,age=18
   //name=lis,age=20
  }
 }

上面的代碼就能夠看出來這個簡單的clone是僅僅複製了對象的屬性及數據,不是存儲對象的索引。因此clone以後的對象與以前的沒有聯繫了,不屬於同一個對象。

說明:
  1)爲何咱們在派生類中覆蓋Object的clone()方法時,必定要調用super.clone()呢?在運行時刻,Object中的clone()識別你要複製的是哪個對象,而後爲此對象分配空間,並進行對象的複製,將原始對象的內容一一複製到新對象的存儲空間中。
 可能會有一個狀況,你的這個類沒有實現Cloneable接口,卻也能夠寫這個方法。那是覺得Object是全部對象的父類(包括新建的),因此能夠覆蓋它的clone方法,可是因爲少了Cloneable這個標識,它就會報錯,進入CloneNotSupportedException 異常裏面
  2)繼承自java.lang.Object.clone()方法是淺層複製。如下代碼能夠證實之:

public class Student implements Cloneable {
 private String name;
 private int age;
 private Professor pro;
 public Student(){}
 public Student(String name,int age,Professor pro){
  this.name=name;
  this.age=age;
  this.pro=pro;
 }
 public Object clone(){
  Object o=null;
  try {
   //Object中的clone()識別出你要複製的是哪個對象。
   o=super.clone();
  } catch (CloneNotSupportedException e) {
   System.out.println(e.toString());
  }
   return o;
 }
 public String getName() {
  return name;
 }
 public void setName(String name) {
  this.name = name;
 }
 public int getAge() {
  return age;
 }
 public void setAge(int age) {
  this.age = age;
 }
 public Professor getPro() {
  return pro;
 }
 public void setPro(Professor pro) {
  this.pro = pro;
 }
 public static void main(String[] args) {
     Professor p=new Professor("wangwu",50);
     Student s1=new Student("zhangsan",18,p);
     Student s2=(Student)s1.clone();
     s2.getPro().setName("maer");
     s2.getPro().setAge(40);
     System.out.println("name="+s1.getPro().getName()+",age="+s1.getPro().getAge());
     //name=maer,age=40
     System.out.println("name="+s2.getPro().getName()+",age="+s2.getPro().getAge());
     //name=maer,age=40
 }
}
class Professor{
 private String name;
 private int age;
 public Professor(){}
 public Professor(String name,int age){
  this.name=name;
  this.age=age;
 }
 public String getName() {
  return name;
 }
 public void setName(String name) {
  this.name = name;
 }
 public int getAge() {
  return age;
 }
 public void setAge(int age) {
  this.age = age;
 }
}

咱們會發現,在外層的對象是發生了複製,可是裏層的沒有。這時就要進行深層clone啦

public class Student implements Cloneable {
private String name;
private int age;
private Professor pro;
public Student(){}
public Student(String name,int age,Professor pro){
  this.name=name;
  this.age=age;
  this.pro=pro;
}
public Object clone(){
  Object o=null;
  try {
   //Object中的clone()識別出你要複製的是哪個對象。
   o=super.clone();
  } catch (CloneNotSupportedException e) {
   System.out.println(e.toString());
  }
   o.pro=(Professor)pro.clone();
   return o;
}
public String getName() {
  return name;
}
public void setName(String name) {
  this.name = name;
}
public int getAge() {
  return age;
}
public void setAge(int age) {
  this.age = age;
}
public Professor getPro() {
  return pro;
}
public void setPro(Professor pro) {
  this.pro = pro;
}
public static void main(String[] args) {
     Professor p=new Professor("wangwu",50);
     Student s1=new Student("zhangsan",18,p);
     Student s2=(Student)s1.clone();
     s2.getPro().setName("maer");
     s2.getPro().setAge(40);
     System.out.println("name="+s1.getPro().getName()+",age="+s1.getPro().getAge());
     //name=wang,age=50
    System.out.println("name="+s2.getPro().getName()+",age="+s2.getPro().getAge());
     //name=maer,age=40
}
}
class Professor implements Cloneable{
private String name;
private int age;
public Professor(){}
public Professor(String name,int age){
  this.name=name;
  this.age=age;
}
public Object clone(){
  Object o=null;
  try {
    o=super.clone();
  } catch (CloneNotSupportedException e) {
    e.printStackTrace();
  }
  return o;
}
public String getName() {
  return name;
}
public void setName(String name) {
  this.name = name;
}
public int getAge() {
  return age;
}
public void setAge(int age) {
  this.age = age;
}
}

這樣就達到效果了,不過能夠看仔細哦。1:做爲對象屬性的對象(上面的Professor)須要實現Cloneable接口,而且實現Clone方法;2:含有對象屬性的對象(上面的Student)的clone方法裏面在返回以前記得對對象屬性進行clone賦值給該對象屬性(o.pro=(Professor)pro.clone();)

而且由此能夠推出一個結論:一個對象裏面含有對象屬性的話,對象屬性也須要實現Cloneable接口及clone方法,而且在對象裏面賦值與對象的對象屬性。當對象屬性也含有對象屬性2的時候,對象屬性2也須要實現Cloneable接口及clone方法,而且在對象屬性(第一個)裏面賦值對象屬性(第一個)的對象屬性2(第二個)--這個推論已經驗證過,這兒時間緊迫就不寫了

下面開始序列化複製對象

先講講什麼是序列化吧(這個以前不太清楚,因此就看看,明白了自動略過,看下面)--轉自 什麼是java序列化,如何實現java序列化?  要對序列化更加深刻能夠看 Java序列化的機制和原理 Java序列化與反序列化 深刻理解Java對象序列化

Java 串行化技術可使你將一個對象的狀態寫入一個Byte 流裏,而且能夠從其它地方把該Byte 流裏的數據讀出來,從新構造一個相同的對象。這種機制容許你將對象經過網絡進行傳播,並能夠隨時把對象持久化到數據庫、文件等系統裏。Java的串行化機制是RMI、EJB等技術的技術基礎。用途:利用對象的串行化實現保存應用程序的當前工做狀態,下次再啓動的時候將自動地恢復到上次執行的狀態。
序列化就是一種用來處理對象流的機制,所謂對象流也就是將對象的內容進行流化。能夠對流化後的對象進行讀寫操做,也可將流化後的對象傳輸於網絡之間。序列化是爲了解決在對對象流進行讀寫操做時所引起的問題。
序列化的實現:將須要被序列化的類實現Serializable接口,而後使用一個輸出流(如:FileOutputStream)來構造一個ObjectOutputStream(對象流)對象,接着,使用ObjectOutputStream對象的writeObject(Object obj)方法就能夠將參數爲obj的對象寫出(即保存其狀態),要恢復的話則用輸入流。
二、串行化的特色:
    (1)若是某個類可以被串行化,其子類也能夠被串行化。若是該類有父類,則分兩種狀況來考慮,若是該父類已經實現了可串行化接口。則其父類的相應字段及屬性的處理和該類相同;若是該類的父類沒有實現可串行化接口,則該類的父類全部的字段屬性將不會串行化。
  (2)聲明爲static和transient類型的成員數據不能被串行化。由於static表明類的狀態, transient表明對象的臨時數據;
  (3)相關的類和接口:在java.io包中提供的涉及對象的串行化的類與接口有ObjectOutput接口、ObjectOutputStream類、ObjectInput接口、ObjectInputStream類。
    (1)ObjectOutput接口:它繼承DataOutput接口而且支持對象的串行化,其內的writeObject()方法實現存儲一個對象。ObjectInput接口:它繼承DataInput接口而且支持對象的串行化,其內的readObject()方法實現讀取一個對象。
    (2)ObjectOutputStream類:它繼承OutputStream類而且實現ObjectOutput接口。利用該類來實現將對象存儲(調用ObjectOutput接口中的writeObject()方法)。ObjectInputStream類:它繼承InputStream類而且實現ObjectInput接口。利用該類來實現讀取一個對象(調用ObjectInput接口中的readObject()方法)。
  對於父類的處理,若是父類沒有實現串行化接口,則其必須有默認的構造函數(即沒有參數的構造函數)。不然編譯的時候就會報錯。在反串行化的時候,默認構造函數會被調用。可是若把父類標記爲能夠串行化,則在反串行化的時候,其默認構造函數不會被調用。這是爲何呢?這是由於Java 對串行化的對象進行反串行化的時候,直接從流裏獲取其對象數據來生成一個對象實例,而不是經過其構造函數來完成。
import java.io.*;
public class Cat implements Serializable {
        private String name;
        public Cat () {
                this.name = "new cat";
        }
        public String getName() {
                return this.name;
        }
        public void setName(String name) {
                this.name = name;
        }
        public static void main(String[] args) {        
                Cat cat = new Cat();
                try {
                        FileOutputStream fos = new FileOutputStream("catDemo.out");
                        ObjectOutputStream oos = new ObjectOutputStream(fos);
                        System.out.println(" 1> " + cat.getName());
                        cat.setName("My Cat");                      
                        oos.writeObject(cat);
                        oos.close();                      
                } catch (Exception ex) {  ex.printStackTrace();   }
                try {
                        FileInputStream fis = new FileInputStream("catDemo.out");
                        ObjectInputStream ois = new ObjectInputStream(fis);
                        cat = (Cat) ois.readObject();
                        System.out.println(" 2> " + cat.getName());
                        ois.close();
                } catch (Exception ex) {
                        ex.printStackTrace();
                }
        }
}//writeObject和readObject自己就是線程安全的,傳輸過程當中是不容許被併發訪問的。因此對象能一個一個接連不斷的傳過來

 ================開始複製相關序列化========================

這個地方的序列化須要用用到 ByteArrayOutputStream、ObjectOutputStream 、ByteArrayInputStream、ObjectInputStream

class TestCC implements Serializable{ 
 private int id; 
 private String name; 
 public int getId() {  return id; } 
 public void setId(int id) {  this.id = id; } 
 public String getName() {  return name; } 
 public void setName(String name) {  
  this.name = name; 
 }
 public TestCC deepClone() throws IOException{
  ByteArrayOutputStream bos=new ByteArrayOutputStream();
  ObjectOutputStream oot=new ObjectOutputStream(bos);
  oot.writeObject(this);
  oot.flush();
  oot.close();
  ByteArrayInputStream bas=new ByteArrayInputStream(bos.toByteArray());
  ObjectInputStream ois=new ObjectInputStream(bas);
  TestCC readObject = null;
  try {
   readObject = (TestCC)ois.readObject();
  } catch (ClassNotFoundException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  return readObject;
 }
 @Override 
 public String toString() {  
  return "TestCC [id=" + id + ", name=" + name + "]"; 
 } 
 public TestCC(){}; 
 public TestCC(int id,String name){  this.id=id;  this.name=name; }; 
}

字數超出最大容許值,服務器可能拒絕保存---這個字數超標--方法就是這樣,經測試正確

相關文章
相關標籤/搜索