#一、克隆的作用
快速構建一個和已有對象相同的副本,創建一個新對象,將已有對象的數據導入到新對象裏面;
#二、克隆基本簡介
我們說的克隆,都是基於超類 Object 來的,裏面有個native方法,具體實現是它調用底層C語言的實現,我們是看不到的
protected native Object clone() throws CloneNotSupportedException;
由此可知,有幾個約束
- 使用時必須繼承Object類,我們所有的類都是Object派生的
- 接收對象必須強轉
- 必須實現 Cloneable 接口標識 (表示重寫了clone() )
不實現這個接口會發生什麼呢?
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));
}
}
結果:
{"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)
添加實現後
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));
}
}
結果:
{"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());
}
}
結果:
哈比-->今天晚上要喫青椒炒蘿蔔 引用地址:com.demo.cs.study.clones.HavingDinner@12a3a380
哈比2-->今天晚上要喫青椒炒蘿蔔 引用地址:com.demo.cs.study.clones.HavingDinner@12a3a380
哈士奇-->今天晚上要喫青椒炒蘿蔔 引用地址:com.demo.cs.study.clones.HavingDinner@12a3a380
看輸出結果,引用地址一樣。這就是淺克隆;那麼問題來了,哈比要喫土豆雞塊,我們改下
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());
}
}
結果:
哈比-->今天晚上要喫土豆雞塊 引用地址:com.demo.cs.study.clones.HavingDinner@12a3a380
哈比2-->今天晚上要喫土豆雞塊 引用地址:com.demo.cs.study.clones.HavingDinner@12a3a380
哈士奇-->今天晚上要喫土豆雞塊 引用地址:com.demo.cs.study.clones.HavingDinner@12a3a380
會發現其它人都給喫土豆雞塊了,我們再改下哈比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());
}
}
結果:
哈比-->今天晚上要喫冰淇淋 引用地址:com.demo.cs.study.clones.HavingDinner@12a3a380
哈比2-->今天晚上要喫冰淇淋 引用地址:com.demo.cs.study.clones.HavingDinner@12a3a380
哈士奇-->今天晚上要喫冰淇淋 引用地址:com.demo.cs.study.clones.HavingDinner@12a3a380
結果還是 都喫冰淇淋,這樣的話就有問題了,我想哈比喫土豆雞塊,哈比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());
}
}
結果:
哈比-->今天晚上要喫土豆雞塊 引用地址:com.demo.cs.study.clones.HavingDinner@12a3a380
哈比2-->今天晚上要喫冰淇淋 引用地址:com.demo.cs.study.clones.HavingDinner@29453f44
哈士奇-->今天晚上要喫土豆雞塊 引用地址:com.demo.cs.study.clones.HavingDinner@5cad8086
總結:達到了互不影響的結果,對象裏面包含的對象也複製了,這是第一種方式,有點複雜;
2、序列化深克隆
原理:把對象序列化輸出到流裏面,然後把流裏面的數據序列化出來,得到一個新的對象
事例:
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());
}
}
結果:
哈比-->今天晚上要喫土豆雞塊 引用地址:com.demo.cs.study.clones.HavingDinner@5e2de80c
哈比2-->今天晚上要喫冰淇淋 引用地址:com.demo.cs.study.clones.HavingDinner@34a245ab
哈士奇-->今天晚上要喫土豆雞塊 引用地址:com.demo.cs.study.clones.HavingDinner@7cc355be