一灭衷、克隆的作用
快速構(gòu)建一個和已有對象相同的副本,創(chuàng)建一個新對象旁涤,將已有對象的數(shù)據(jù)導入到新對象里面翔曲;
二、克隆基本簡介
我們說的克隆拭抬,都是基于超類 Object 來的部默,里面有個native方法,具體實現(xiàn)是它調(diào)用底層C語言的實現(xiàn)造虎,我們是看不到的
protected native Object clone() throws CloneNotSupportedException;
由此可知傅蹂,有幾個約束
- 使用時必須繼承Object類,我們所有的類都是Object派生的
- 接收對象必須強轉(zhuǎn)
- 必須實現(xiàn) Cloneable 接口標識 (表示重寫了clone() )
不實現(xiàn)這個接口會發(fā)生什么呢算凿?
public class User{
private String userName;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
@Override
protected Object clone() throws CloneNotSupportedException{
return super.clone();
}
public static void main(String[] args) throws CloneNotSupportedException {
User user=new User();
user.setUserName("zhangsan");
System.out.println(JSONObject.toJSONString(user));
User user1= (User) user.clone();
System.out.println(JSONObject.toJSONString(user1));
}
}
結(jié)果:
{"userName":"zhangsan"}
Exception in thread "main" java.lang.CloneNotSupportedException: com.demo.cs.study.clones.User
at java.lang.Object.clone(Native Method)
at com.demo.cs.study.clones.User.clone(User.java:19)
at com.demo.cs.study.clones.User.main(User.java:32)
添加實現(xiàn)后
public class User implements Cloneable{
private String userName;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
@Override
protected Object clone() throws CloneNotSupportedException{
return super.clone();
}
public static void main(String[] args) throws CloneNotSupportedException {
User user=new User();
user.setUserName("zhangsan");
System.out.println(JSONObject.toJSONString(user));
User user1= (User) user.clone();
System.out.println(JSONObject.toJSONString(user1));
}
}
結(jié)果:
{"userName":"zhangsan"}
{"userName":"zhangsan"}
1份蝴、淺克隆
概念:如果被復制的對象所有的變量與原來的變量值相同,且所持有對其它對象的引用任然指向原來的對象就叫淺克隆
User.java
public class User implements Cloneable{
private String userName;
private HavingDinner havingDinner;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public HavingDinner getHavingDinner() {
return havingDinner;
}
public void setHavingDinner(HavingDinner havingDinner) {
this.havingDinner = havingDinner;
}
@Override
protected Object clone() throws CloneNotSupportedException{
return super.clone();
}
}
HavingDinner.java
public class HavingDinner {
private String foodName;
public String getFoodName() {
return foodName;
}
public void setFoodName(String foodName) {
this.foodName = foodName;
}
public HavingDinner(String foodName) {
this.foodName = foodName;
}
}
Demo.java
public class Demo {
public static void main(String[] args) throws CloneNotSupportedException {
HavingDinner hd=new HavingDinner("青椒炒蘿卜");
User user=new User();
user.setUserName("哈比");
user.setHavingDinner(hd);
User user1= (User) user.clone();
user1.setUserName("哈比2");
User user2= (User) user.clone();
user2.setUserName("哈士奇");
System.out.println(user.getUserName()+"-->今天晚上要吃"+user.getHavingDinner().getFoodName()+" 引用地址:"+user.getHavingDinner());
System.out.println(user1.getUserName()+"-->今天晚上要吃"+user1.getHavingDinner().getFoodName()+" 引用地址:"+user1.getHavingDinner());
System.out.println(user2.getUserName()+"-->今天晚上要吃"+user2.getHavingDinner().getFoodName()+" 引用地址:"+user2.getHavingDinner());
}
}
結(jié)果:
哈比-->今天晚上要吃青椒炒蘿卜 引用地址:com.demo.cs.study.clones.HavingDinner@12a3a380
哈比2-->今天晚上要吃青椒炒蘿卜 引用地址:com.demo.cs.study.clones.HavingDinner@12a3a380
哈士奇-->今天晚上要吃青椒炒蘿卜 引用地址:com.demo.cs.study.clones.HavingDinner@12a3a380
看輸出結(jié)果氓轰,引用地址一樣婚夫。這就是淺克隆署鸡;那么問題來了案糙,哈比要吃土豆雞塊,我們改下
Demo2
public class Demo {
public static void main(String[] args) throws CloneNotSupportedException {
HavingDinner hd=new HavingDinner("土豆雞塊");
User user=new User();
user.setUserName("哈比");
user.setHavingDinner(hd);
User user1= (User) user.clone();
user1.setUserName("哈比2");
User user2= (User) user.clone();
user2.setUserName("哈士奇");
System.out.println(user.getUserName()+"-->今天晚上要吃"+user.getHavingDinner().getFoodName()+" 引用地址:"+user.getHavingDinner());
System.out.println(user1.getUserName()+"-->今天晚上要吃"+user1.getHavingDinner().getFoodName()+" 引用地址:"+user1.getHavingDinner());
System.out.println(user2.getUserName()+"-->今天晚上要吃"+user2.getHavingDinner().getFoodName()+" 引用地址:"+user2.getHavingDinner());
}
}
結(jié)果:
哈比-->今天晚上要吃土豆雞塊 引用地址:com.demo.cs.study.clones.HavingDinner@12a3a380
哈比2-->今天晚上要吃土豆雞塊 引用地址:com.demo.cs.study.clones.HavingDinner@12a3a380
哈士奇-->今天晚上要吃土豆雞塊 引用地址:com.demo.cs.study.clones.HavingDinner@12a3a380
會發(fā)現(xiàn)其它人都給吃土豆雞塊了靴庆,我們再改下哈比2的試試
Demo3:
public class Demo {
public static void main(String[] args) throws CloneNotSupportedException {
HavingDinner hd=new HavingDinner("土豆雞塊");
User user=new User();
user.setUserName("哈比");
user.setHavingDinner(hd);
User user1= (User) user.clone();
user1.setUserName("哈比2");
user1.getHavingDinner().setFoodName("冰淇淋");
User user2= (User) user.clone();
user2.setUserName("哈士奇");
System.out.println(user.getUserName()+"-->今天晚上要吃"+user.getHavingDinner().getFoodName()+" 引用地址:"+user.getHavingDinner());
System.out.println(user1.getUserName()+"-->今天晚上要吃"+user1.getHavingDinner().getFoodName()+" 引用地址:"+user1.getHavingDinner());
System.out.println(user2.getUserName()+"-->今天晚上要吃"+user2.getHavingDinner().getFoodName()+" 引用地址:"+user2.getHavingDinner());
}
}
結(jié)果:
哈比-->今天晚上要吃冰淇淋 引用地址:com.demo.cs.study.clones.HavingDinner@12a3a380
哈比2-->今天晚上要吃冰淇淋 引用地址:com.demo.cs.study.clones.HavingDinner@12a3a380
哈士奇-->今天晚上要吃冰淇淋 引用地址:com.demo.cs.study.clones.HavingDinner@12a3a380
結(jié)果還是 都吃冰淇淋时捌,這樣的話就有問題了,我想哈比吃土豆雞塊炉抒,哈比2吃冰淇淋奢讨;
這樣也證實了上面的概念;基本屬性的值是復制了焰薄,但是其它對象里的屬性根本就沒有復制拿诸,只是單純的復制了引用地址扒袖;
2、深克隆
User.java
public class User implements Cloneable{
private String userName;
private HavingDinner havingDinner;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public HavingDinner getHavingDinner() {
return havingDinner;
}
public void setHavingDinner(HavingDinner havingDinner) {
this.havingDinner = havingDinner;
}
@Override
protected Object clone() throws CloneNotSupportedException{
User user=(User) super.clone();
user.setHavingDinner((HavingDinner) getHavingDinner().clone());
return user;
}
}
HavingDinner.java
public class HavingDinner implements Cloneable{
private String foodName;
public String getFoodName() {
return foodName;
}
public void setFoodName(String foodName) {
this.foodName = foodName;
}
public HavingDinner(String foodName) {
this.foodName = foodName;
}
@Override
protected Object clone() throws CloneNotSupportedException{
return super.clone();
}
}
Demo.java
public class Demo {
public static void main(String[] args) throws CloneNotSupportedException {
HavingDinner hd=new HavingDinner("土豆雞塊");
User user=new User();
user.setUserName("哈比");
user.setHavingDinner(hd);
User user1= (User) user.clone();
user1.setUserName("哈比2");
user1.getHavingDinner().setFoodName("冰淇淋");
User user2= (User) user.clone();
user2.setUserName("哈士奇");
System.out.println(user.getUserName()+"-->今天晚上要吃"+user.getHavingDinner().getFoodName()+" 引用地址:"+user.getHavingDinner());
System.out.println(user1.getUserName()+"-->今天晚上要吃"+user1.getHavingDinner().getFoodName()+" 引用地址:"+user1.getHavingDinner());
System.out.println(user2.getUserName()+"-->今天晚上要吃"+user2.getHavingDinner().getFoodName()+" 引用地址:"+user2.getHavingDinner());
}
}
結(jié)果:
哈比-->今天晚上要吃土豆雞塊 引用地址:com.demo.cs.study.clones.HavingDinner@12a3a380
哈比2-->今天晚上要吃冰淇淋 引用地址:com.demo.cs.study.clones.HavingDinner@29453f44
哈士奇-->今天晚上要吃土豆雞塊 引用地址:com.demo.cs.study.clones.HavingDinner@5cad8086
總結(jié):達到了互不影響的結(jié)果亩码,對象里面包含的對象也復制了季率,這是第一種方式,有點復雜蟀伸;
2蚀同、序列化深克隆
原理:把對象序列化輸出到流里面,然后把流里面的數(shù)據(jù)序列化出來啊掏,得到一個新的對象
事例:
User.java
public class User implements Serializable {
private String userName;
private HavingDinner havingDinner;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public HavingDinner getHavingDinner() {
return havingDinner;
}
public void setHavingDinner(HavingDinner havingDinner) {
this.havingDinner = havingDinner;
}
protected User deepClone() throws IOException, ClassNotFoundException {
ByteArrayOutputStream byteArrayOutputStream=new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream=new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(this);
ByteArrayInputStream byteArrayInputStream=new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
ObjectInputStream objectInputStream=new ObjectInputStream(byteArrayInputStream);
return (User) objectInputStream.readObject();
}
}
HavingDinner.java
public class HavingDinner implements Serializable {
private String foodName;
public String getFoodName() {
return foodName;
}
public void setFoodName(String foodName) {
this.foodName = foodName;
}
public HavingDinner(String foodName) {
this.foodName = foodName;
}
}
測試類
public class Demo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
HavingDinner hd=new HavingDinner("土豆雞塊");
User user=new User();
user.setUserName("哈比");
user.setHavingDinner(hd);
User user1=user.deepClone();
user1.setUserName("哈比2");
user1.getHavingDinner().setFoodName("冰淇淋");
User user2=user.deepClone();
user2.setUserName("哈士奇");
System.out.println(user.getUserName()+"-->今天晚上要吃"+user.getHavingDinner().getFoodName()+" 引用地址:"+user.getHavingDinner());
System.out.println(user1.getUserName()+"-->今天晚上要吃"+user1.getHavingDinner().getFoodName()+" 引用地址:"+user1.getHavingDinner());
System.out.println(user2.getUserName()+"-->今天晚上要吃"+user2.getHavingDinner().getFoodName()+" 引用地址:"+user2.getHavingDinner());
}
}
結(jié)果:
哈比-->今天晚上要吃土豆雞塊 引用地址:com.demo.cs.study.clones.HavingDinner@5e2de80c
哈比2-->今天晚上要吃冰淇淋 引用地址:com.demo.cs.study.clones.HavingDinner@34a245ab
哈士奇-->今天晚上要吃土豆雞塊 引用地址:com.demo.cs.study.clones.HavingDinner@7cc355be