Java實現克隆的方式?淺拷貝如何理解?
阿新 • • 發佈:2019-01-07
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();
//這個就是將流中的東西讀出類,讀到一個物件流當中,這樣就可以返回這兩個物件的東西,實現深克隆
}
}