1. 程式人生 > >Java實現克隆的方式?淺拷貝如何理解?

Java實現克隆的方式?淺拷貝如何理解?

Java實現克隆的方式?淺拷貝如何理解?

1、淺複製(淺克隆)

淺拷貝(淺克隆):對一個物件進行clone生成新的物件,新的物件要開闢一塊新的記憶體來儲存,新物件中的基本型別屬性和String型別屬性都會開闢新的空間儲存,但是如果是引用型別的屬性,那這個引用型別的屬性還是指向原物件的引用屬性記憶體,當對新的物件或原物件的引用屬性做出改變的時候,兩方的引用屬性型別的值同時做出改變。
1.先申明一個bean作為引用型別

public class Student {
private String name;
private String sex;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student [name=" +
name + ", sex=" + sex + ", age=" + age + "]";
}
public Student(){
}
public Student(String name, String sex, int
age) {
super();
this.name = name;
this.sex = sex;
this.age = age;
}
}

2.呼叫main方法進行驗證

import java.io.Serializable;
import java.util.HashMap;
import java.util.Hashtable;
public class MapCloneTest { 
 public static void main(String[] args)
{
  Student zhangsan = new Student("zhangsan","男",25);
  HashMap map = new HashMap();
  map.put(1,zhangsan);  
  HashMap cloneMap =(HashMap) map.clone();
  System.out.println("*************************不做改變**********************************");
  System.out.println("未改變之前,  map的值:"+map.toString());
  System.out.println("未改變之前,cloneMap的值:"+cloneMap.toString());
  System.out.println("map和cloneMap是否指向同一記憶體地址:"+(map==cloneMap));
  System.out.println("map和cloneMap的hashcode:"+(map.hashCode()==cloneMap.hashCode()));
  System.out.println("map和cloneMap中儲存的student是否指向同一記憶體地址:"+(map.get(1)==cloneMap.get(1)));
  System.out.println("map和cloneMap中儲存的student值是否相同:"+(map.get(1).equals(cloneMap.get(1))));
  //對cloneMap中的值進行改變,看是否能影響到map
  Student cloneLisi =(Student) cloneMap.get(1);
  cloneLisi.setSex("女");
  System.out.println("*************************對map中的值做出修改***************************");
  System.out.println("改變之後,cloneMap的值:"+cloneMap.toString());
  System.out.println("改變之後, map的值:"+map.toString());
  System.out.println("*************************對map新增*********************************");
  Student lisi = new Student("lisi","男",18);
  map.put(2, lisi);
  System.out.println("改變之後,cloneMap的值:"+cloneMap.toString());
  System.out.println("改變之後, map的值:"+map.toString());
 }
}

輸出結果:

*************************不做改變***********************************
未改變之前,     map的值:{1=Student [name=zhangsan, sex=男, age=25]}
未改變之前,cloneMap的值:{1=Student [name=zhangsan, sex=男, age=25]}
map和cloneMap是否指向同一記憶體地址:false
map和cloneMap的hashcode:true
map和cloneMap中儲存的student是否指向同一記憶體地址:true
map和cloneMap中儲存的student值是否相同:true
*************************對map中的值做出修改****************************
改變之後,cloneMap的值:{1=Student [name=zhangsan, sex=女, age=25]}
改變之後,     map的值:{1=Student [name=zhangsan, sex=女, age=25]}
*************************對map新增**********************************
改變之後,cloneMap的值:{1=Student [name=zhangsan, sex=女, age=25]}
改變之後,     map的值:{1=Student [name=zhangsan, sex=女, age=25], 2=Student [name=lisi, sex=男, age=18]}

從HashMap的克隆方法可以看出這樣的幾點
1.HashMap的clone方法生成新的物件,新的物件有自己獨立的儲存空間。
2.雖然HashMap的clone方法生成了新的物件,但新的HashMap和原來的HashMap所儲存的引用型別都是指向的同一儲存空間。
   淺複製(淺克隆)這種淺複製,其實也就是把被複制的這個物件的一些變數值拿過來了。

public class CloneTest1
{
 public static void main(String[] args) throws Exception
 {
  Student student = new Student();
  student.setAge(24);
  student.setName("niesong");
  Student student2 =(Student)student.clone();
  //這個是呼叫下面的那個方法,然後把這個這個物件Clone到student
  System.out.println("Age:"+ student2.getAge() + " " + "Name:" + student2.getName());
  System.out.println("---------------------");
  student2.setAge(23);
  //克隆後得到的是一個新的物件,所以重新寫的是student2這個物件的值
   System.out.println(student.getAge());
  System.out.println(student2.getAge());
 } 
}
//克隆的物件必須實現Cloneable這個介面,而且需要重寫clone方法
class Student implements Cloneable
{
 private int age;
 //定義為private說明這個成員變數只能被被當前類中訪問,如果外部需要獲得,那麼就只能通過getAge方法進行獲取
 private String name;
 public int getAge()
 {
  return age;
 }
 public void setAge(int age)
 {
  this.age = age;
 }
 public String getName()
 {
  return name;
 }
 public void setName(String name)
 {
  this.name = name;
 }
 @Override
 public Object clone() throws CloneNotSupportedException
 {
  Object object =super.clone();
  return object;
 }
}

2、深複製

情況1使用的是在克隆的時候手動進行深克隆

public class CloneTest2
{
 public static void main(String[] args) throws Exception
 {
  Teacher teacher = new Teacher();
  teacher.setAge(40);
  teacher.setName("teacher zhang");  
  Student2 student2 = new Student2();
  student2.setAge(14);
  student2.setName("lisi");
  student2.setTeacher(teacher);  
  Student2 student3 = (Student2)student2.clone();
  //這裡是深複製,所以這時候Student2中的teacher就是teacher這個物件的一個複製,就和student3是student2的一個複製
  //所以下面teacher.setName只是對他原來的這個物件更改,但是複製的那個並沒有更改
  System.out.println(student3.getAge());
  System.out.println(student3.getName());
  System.out.println(student3.getTeacher().getAge());
  teacher.setName("teacher niesong");//不會又任何影響
  System.out.println(student3.getTeacher().getName()); 
 } 
}
class Student2 implements Cloneable
{
 private int age;
 private String name;
 private Teacher teacher;
 public int getAge()
 {
  return age;
 }
 public void setAge(int age)
 {
  this.age = age;
 }
 public String getName()
 {
  return name;
 }
 public void setName(String name)
 {
  this.name = name;
 }
 public Teacher getTeacher()
 {
  return teacher;
 }
 public void setTeacher(Teacher teacher)
 {
  this.teacher = teacher;
 }
 @Override
 public Object clone() throws CloneNotSupportedException
 {
  //這一步返回的這個student2還只是一個淺克隆,
  Student2 student2 = (Student2)super.clone();
  //然後克隆的過程中獲得這個克隆的student2,然後呼叫這個getTeacher這個方方法得到這個Teacher物件。然後實現克隆。在設定到這個student2中的Teacher。
  //這樣實現了雙層克隆使得那個teacher物件也得到了複製。
  student2.setTeacher((Teacher)student2.getTeacher().clone());
  //雙層克隆使得那個teacher物件也得到了複製
  return student2;
 }
}
class Teacher implements Cloneable
{
 private int age;
 private String name;
 public int getAge()
 {
  return age;
 }
 public void setAge(int age)
 {
  this.age = age;
 }
 public String getName()
 {
  return name;
 }
 public void setName(String name)
 {
  this.name = name;
 }
 @Override
 public Object clone() throws CloneNotSupportedException
 {
  return super.clone();
 } 
}

3、利用serializable實現深複製

public class CloneTest3
{
 public static void main(String[] args) throws Exception
 {
  Teacher3 teacher3 = new Teacher3();
  teacher3.setAge(23);
  teacher3.setName("niesong");
  Student3 student3 = new Student3();
  student3.setAge(50);
  student3.setName("wutao");
  student3.setTeacher3(teacher3);
  
  Student3 ss = (Student3)student3.deepCopt();
  System.out.println(ss.getAge());
  System.out.println(ss.getName());
  
  System.out.println("---------------------");
  System.out.println(ss.getTeacher3().getAge());
  System.out.println(ss.getTeacher3().getName());
  
  System.out.println("-----------------------");  
  ss.getTeacher3().setAge(7777);
  ss.getTeacher3().setName("hhhhh");
  
  System.out.println(teacher3.getAge());
  System.out.println(teacher3.getName());
  //雖然上面的已經改了,但是改的是那個複製物件後的那個裡面的,然後那個原來的那個裡面的並沒有改,下面驗證:::
  
  System.out.println("-----------------");  
  System.out.println(ss.getTeacher3().getAge());
  System.out.println(ss.getTeacher3().getName());  
 }
 
}
class Teacher3 implements Serializable
{
//  上面的那個警告可以直接消除,除了使用在設定中不顯示這個警告,還可以使用下面的這兩條語句中的任何一條語句
// 這個serialVersionUID為了讓該類別Serializable向後相容
// private static final long serialVersionUID = 1L;
// private static final long serialVersionUID = 8940196742313994740L;
 private int age;
 private String name;
 public int getAge()
 {
  return age;
 }
 public void setAge(int age)
 {
  this.age = age;
 }
 public String getName()
 {
  return name;
 }
 public void setName(String name)
 {
  this.name = name;
 }
}
class Student3 implements Serializable
 {
  private static final long serialVersionUID = 1L;
  private int age;
  private String name;
  private Teacher3 teacher3;
  public int getAge()
  {
   return age;
  }
  public void setAge(int age)
  {
   this.age = age;
  }
  public String getName()
  {
   return name;
  }
  public void setName(String name)
  {
   this.name = name;
  }
  public Teacher3 getTeacher3()
  {
   return teacher3;
  }
  public void setTeacher3(Teacher3 teacher3)
  {
   this.teacher3 = teacher3;
  }
  //使得序列化student3的時候也會將teacher序列化
  public Object deepCopt() throws Exception
  {
   ByteArrayOutputStream bos = new ByteArrayOutputStream();
   ObjectOutputStream  oos = new ObjectOutputStream(bos);
   oos.writeObject(this);
   //將當前這個物件寫到一個輸出流當中,,因為這個物件的類實現了Serializable這個介面,所以在這個類中
   //有一個引用,這個引用如果實現了序列化,那麼這個也會寫到這個輸出流當中
   
   ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
   ObjectInputStream ois = new ObjectInputStream(bis);
   return ois.readObject();
   //這個就是將流中的東西讀出類,讀到一個物件流當中,這樣就可以返回這兩個物件的東西,實現深克隆
  }
 }