本文最后更新于 2024年9月25日 下午
Java 深克隆与浅克隆
定义
浅克隆
在克隆对象时,只复制对象本身及其内部引用的基本数据类型字段的值,而不复制引用类型字段所指向的对象.这意味着克隆后的对象和原始对象会共享一部分引用对象
下面是一份Java代码样例.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| class Adventurer implements Cloneable { public String name; public Weapon wp; public Adventurer(){} public Adventurer(String name, int atk){ this.name = name; this.wp = new Weapon(atk); }
@Override public Adventurer clone() throws CloneNotSupportedException{ return (Adventurer) super.clone(); }
public void setName(String name){ this.name = name; }
public String getName(){ return name; } }
class Weapon{ private int atk; public Weapon(){} public Weapon(int atk){ this.atk = atk; } public Weapon(Weapon wp){ this.atk = wp.atk; } public void setAtk(int atk){ this.atk = atk; } public int getAtk(){ return atk; } }
public class TestClone { public static void main(String[] args) { Adventurer adv1 = new Adventurer("xiaohong",8); try { Adventurer adv2 = adv1.clone(); adv2.wp.setAtk(10); adv2.setName("xiaoming"); System.out.println(adv1.wp.getAtk() + " " + adv2.wp.getAtk()); System.out.println(adv1.getName() + " " + adv2.getName()); }catch(Exception e){ System.out.println(e); } }
}
|
可以看到,代码对Adventurer类进行了clone()
方法的重载,但并没有对其引用类Weapon
类进行克隆.这样的克隆方法只复制了Adventurer
类所持有的name
,而没有复制Weapon
.
深克隆
我们对重载clone()
方法做修改,使其可以复制引用类Weapon类
1 2 3 4 5 6
| @Override public Adventurer clone() throws CloneNotSupportedException{ Adventurer copy = (Adventurer) super.clone(); copy.wp = new Weapon(this.wp); return copy; }
|
这里我们调用Weapon
的创建方法,使其复制了一个新的Weapon并将其作为复制对象的引用类.代码重新运行的结果如下:
可以看到,对adv2(第二个Adventurer
类)的Weapon
进行修改,并不会改变adv1
的Weapon的atk
值,说明两个Adventurer
类所引用的Weapon
类已不是同一个.
深克隆方法
在 Java 中,常见的实现深克隆的方式有:
- 通过继承
Cloneable
接口,重写 clone()
方法实现深克隆;
- 通过序列化与反序列化的方式实现深克隆;
- 第三方工具类实现深克隆,克隆对象需继承
Serializable
接口。
Override clone()
前文已经介绍这种方法.
在Object
类中的clone()
方法的声明中包含了throws CloneNotSupportedException
,因此如果你的类实现了Cloneable
接口并重写了clone()
方法,而在该方法中调用了super.clone()
,那么你的方法也需要声明会抛出CloneNotSupportedException
异常。
1 2 3 4 5 6
| @Override public Adventurer clone() throws CloneNotSupportedException{ Adventurer copy = (Adventurer) super.clone(); copy.wp = new Weapon(this.wp); return copy; }
|
Serialize and Deserialize
要实现序列化,我们首先改变Adventurer
类与Weapon
类的接口,将其修改为Serializable
1 2
| class Adventurer implements Serializable class Weapon implements Serializable
|
通过新建DeepCopyCloneUtil
类搭建deepClone(T object)
方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| class DeepCopyCloneUtil { public static <T> T deepClone(T object) throws IOException, ClassNotFoundException { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(object); oos.close();
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); T clonedObject = (T) ois.readObject(); ois.close();
return clonedObject; } }
|
在main()
中运行以下语句:
1 2 3 4 5 6 7 8 9 10
| Adventurer adv3 = new Adventurer("liming", 23); try { Adventurer adv4 = DeepCopyCloneUtil.deepClone(adv3); adv4.setName("wangming"); adv4.wp.setAtk(999); System.out.println(adv3.getName() + " " + adv4.getName()); System.out.println(adv3.wp.getAtk() + " " + adv4.wp.getAtk()); } catch (Exception e) { System.out.println(e); }
|
得到结果为:
打开IDEA,文件->项目结构->库,点击"+",选择"来自Maven",输入com.google.code.gson进行搜索,下载对应安装包,对应导入语句为:
1
| import com.google.gson.Gson;
|
我们利用Gson这个第三方库实现深克隆
1 2 3 4 5 6 7 8
| Adventurer adv5 = new Adventurer("Bob", 19); Gson gson = new Gson(); Adventurer adv6 = gson.fromJson(gson.toJson(adv5), Adventurer.class);
adv6.setName("Alice"); adv6.wp.setAtk(100); System.out.println(adv5.getName() + " " + adv6.getName()); System.out.println(adv5.wp.getAtk() + " " + adv6.wp.getAtk());
|
运行结果:
附:整体代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
| import java.io.*; import com.google.gson.Gson;
class Adventurer implements Serializable, Cloneable{ public String name; public Weapon wp;
public Adventurer() { }
public Adventurer(String name, int atk) { this.name = name; this.wp = new Weapon(atk); }
@Override public Adventurer clone() throws CloneNotSupportedException { Adventurer copy = (Adventurer) super.clone(); copy.wp = new Weapon(this.wp); return copy; }
public void setName(String name) { this.name = name; }
public String getName() { return name; } }
class Weapon implements Serializable { private int atk;
public Weapon() { }
public Weapon(int atk) { this.atk = atk; }
public void setAtk(int atk) { this.atk = atk; }
public int getAtk() { return atk; }
public Weapon(Weapon wp) { this.atk = wp.atk; } }
class DeepCopyCloneUtil { public static <T> T deepClone(T object) throws IOException, ClassNotFoundException { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(object); oos.close();
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); T clonedObject = (T) ois.readObject(); ois.close();
return clonedObject; } }
public class TestClone { public static void main(String[] args) { Adventurer adv1 = new Adventurer("xiaohong", 8); try { Adventurer adv2 = adv1.clone(); adv2.wp.setAtk(10); adv2.setName("xiaoming"); System.out.println(adv1.wp.getAtk() + " " + adv2.wp.getAtk()); System.out.println(adv1.getName() + " " + adv2.getName()); } catch (Exception e) { System.out.println(e); }
Adventurer adv3 = new Adventurer("liming", 23); try { Adventurer adv4 = DeepCopyCloneUtil.deepClone(adv3); adv4.setName("wangming"); adv4.wp.setAtk(999); System.out.println(adv3.getName() + " " + adv4.getName()); System.out.println(adv3.wp.getAtk() + " " + adv4.wp.getAtk()); } catch (Exception e) { System.out.println(e); }
Adventurer adv5 = new Adventurer("Bob", 19); Gson gson = new Gson(); Adventurer adv6 = gson.fromJson(gson.toJson(adv5), Adventurer.class);
adv6.setName("Alice"); adv6.wp.setAtk(100); System.out.println(adv5.getName() + " " + adv6.getName()); System.out.println(adv5.wp.getAtk() + " " + adv6.wp.getAtk()); }
}
|