如果我们有一个对象a,我们想得到它的一个克隆,那么我们该怎么做呢?最直观、最笨的方法是我们先new一个a的同类对象b,然后挨个拷贝a的属性给b中的相应属性。那么,这里马上就得引出两个概念:浅克隆与深克隆。
如果用直白的、非严格定义的语言来解释这两个概念,那么可以这么说:
所谓浅克隆是指复制一个对象的实例,但是这个对象中包含的其它的对象还是共用的。
所谓深克隆是指复制一个对象的实例,而且这个对象中包含的其它的对象也要复制一份。
如果我们要深克隆一个对象,而这个对象又比较复杂,它还包含n多其它对象的引用,那么要用我们开始说的那种克隆方法,简直是要人命! 即便是我们只要一个浅克隆的对象,如果这个对象有几十上百个基本属性,我们挨个去复制也是不可接受的!
那么,我们先来看看java中浅克隆一个对象可以怎么做。
Object 类提供有一个clone方法,它的方法签名如下:
- protected native Object clone() throws CloneNotSupportedException;
可以看到:(1)它是一个native方法。native方法的效率一般来说都远高于非native方法。
(2)它是一个protected方法。
(3)它返回一个Object。
如果我们要在其它类中调用clone方法,那么我们就要重写这个方法,将它的方法属性改为public 。这个方法其实就提供了浅克隆的功能。
验证的代码如下:
Swallow.java:
- package com.myclone.test;
-
-
- public class Swallow implements Cloneable{
-
-
- private String name;
- private Wing leftWing;
- private Wing rightWing;
-
- public Swallow(String name, Wing leftWing, Wing rightWing){
- this.name = name;
- this.leftWing = leftWing;
- this.rightWing = rightWing;
- }
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- public Wing getLeftWing() {
- return leftWing;
- }
-
- public void setLeftWing(Wing leftWing) {
- this.leftWing = leftWing;
- }
-
- public Wing getRightWing() {
- return rightWing;
- }
-
- public void setRightWing(Wing rightWing) {
- this.rightWing = rightWing;
- }
-
- @Override
- protected Object clone() throws CloneNotSupportedException {
-
- return super.clone();
- }
- }
Wing.java:
- package com.myclone.test;
-
- public class Wing{
-
- private int width;
-
- public Wing(int width){
- this.width = width;
- }
-
- public int getWidth() {
- return width;
- }
-
- public void setWidth(int width) {
- this.width = width;
- }
- }
Main.java:
- package com.myclone.test;
-
- public class Main {
-
- public static void main(String[] args) throws CloneNotSupportedException {
-
-
- Swallow s1 = new Swallow("大雁a", new Wing(10), new Wing(10));
- Swallow s2 = (Swallow) s1.clone();
-
- System.out.println("(s1==s2)="+ (s1==s2));
- System.out.println("s1.name="+ s1.getName()+", s2.name="+s2.getName());
- System.out.println("(s1.leftWing==s2.leftWing) = "+(s1.getLeftWing()==s2.getLeftWing()));
- System.out.println("(s1.rightWing==s2.rightWing) = "+(s1.getRightWing()==s2.getRightWing()));
- }
- }
运行结果:
(s1==s2)=false s1.name=大雁a, s2.name=大雁a (s1.leftWing==s2.leftWing) = true (s1.rightWing==s2.rightWing) = true
从运行结果中我们可以看出,调用 s1.clone() 方法我们得到的是另一个对象,它浅克隆了s1,因为s1和s2中的对象属性 leftWing 指向的是同一个实例,rightWing指向的是同一个实例。注意:要调用clone方法,必须要实现 Cloneable接口,在我们这个例子中即Swallow实现了Cloneable接口。
如果我们需要深克隆的对象呢?首先我们想到的是,可以再次重写Swallow中的clone方法,将leftWing 和 rightWing 克隆一份。那么Wing 也要重写它的clone方法,改写后的Wing类:
- package com.myclone.test;
-
- public class Wing implements Cloneable{
-
- private int width;
-
- public Wing(int width){
- this.width = width;
- }
-
- public int getWidth() {
- return width;
- }
-
- public void setWidth(int width) {
- this.width = width;
- }
-
- @Override
- protected Object clone() throws CloneNotSupportedException {
-
- return super.clone();
- }
- }
改写后的Swallow类:
- package com.myclone.test;
-
-
- public class Swallow implements Cloneable{
-
-
- private String name;
- private Wing leftWing;
- private Wing rightWing;
-
- public Swallow(String name, Wing leftWing, Wing rightWing){
- this.name = name;
- this.leftWing = leftWing;
- this.rightWing = rightWing;
- }
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- public Wing getLeftWing() {
- return leftWing;
- }
-
- public void setLeftWing(Wing leftWing) {
- this.leftWing = leftWing;
- }
-
- public Wing getRightWing() {
- return rightWing;
- }
-
- public void setRightWing(Wing rightWing) {
- this.rightWing = rightWing;
- }
-
-
- @Override
- protected Object clone() throws CloneNotSupportedException {
-
- Swallow swallow = (Swallow) super.clone();
- Wing lefWing = (Wing) swallow.getLeftWing().clone();
- Wing rightWing = (Wing) swallow.getRightWing().clone();
- swallow.setLeftWing(lefWing);
- swallow.setRightWing(rightWing);
- return swallow;
- }
- }
再次运行Main,得到的结果是:
(s1==s2)=false s1.name=大雁a, s2.name=大雁a (s1.leftWing==s2.leftWing) = false (s1.rightWing==s2.rightWing) = false
可以看出s2已经是s1的深克隆了。这种方法有个缺点就是,如果Wing还含有对象属性的话,那么我们就必须依次去重写属性类的clone方法!
另一种方法是通过序列化和反序列化来实现深克隆。这种方法实际上就是“先使对象实现Serializable接口,然后把对象(实际上只是对象的一个拷贝)写到一个流中,再从流中读出来,便可以重建对象。” ——java.lang.Object.clone()分析
要注意的是所有相关的类都要实现Serializable接口,改写后的实例:
Wing.java
- package com.myclone.test;
-
- import java.io.Serializable;
-
- public class Wing implements Cloneable, Serializable{
-
-
- private static final long serialVersionUID = -6757262538695878009L;
- private int width;
-
- public Wing(int width){
- this.width = width;
- }
-
- public int getWidth() {
- return width;
- }
-
- public void setWidth(int width) {
- this.width = width;
- }
-
- @Override
- protected Object clone() throws CloneNotSupportedException {
-
- return super.clone();
- }
- }
Swallow.java
- package com.myclone.test;
-
- import java.io.ByteArrayInputStream;
- import java.io.ByteArrayOutputStream;
- import java.io.IOException;
- import java.io.ObjectInputStream;
- import java.io.ObjectOutputStream;
- import java.io.Serializable;
-
-
- public class Swallow implements Cloneable, Serializable{
-
-
-
- private static final long serialVersionUID = -2152309743441152834L;
- private String name;
- private Wing leftWing;
- private Wing rightWing;
-
- public Swallow(String name, Wing leftWing, Wing rightWing){
- this.name = name;
- this.leftWing = leftWing;
- this.rightWing = rightWing;
- }
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- public Wing getLeftWing() {
- return leftWing;
- }
-
- public void setLeftWing(Wing leftWing) {
- this.leftWing = leftWing;
- }
-
- public Wing getRightWing() {
- return rightWing;
- }
-
- public void setRightWing(Wing rightWing) {
- this.rightWing = rightWing;
- }
-
-
- @Override
- protected Object clone() throws CloneNotSupportedException {
-
- Swallow swallow = (Swallow) super.clone();
- Wing lefWing = (Wing) swallow.getLeftWing().clone();
- Wing rightWing = (Wing) swallow.getRightWing().clone();
- swallow.setLeftWing(lefWing);
- swallow.setRightWing(rightWing);
- return swallow;
- }
-
-
- public Object deepClone() throws IOException, ClassNotFoundException{
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- ObjectOutputStream oos = new ObjectOutputStream(baos);
- oos.writeObject(this);
-
- ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
- ObjectInputStream ois = new ObjectInputStream(bais);
- return ois.readObject();
- }
- }
Main.java:
- package com.myclone.test;
-
- import java.io.IOException;
-
- public class Main {
-
- public static void main(String[] args) throws CloneNotSupportedException, ClassNotFoundException, IOException {
-
-
- Swallow s1 = new Swallow("大雁a", new Wing(10), new Wing(10));
-
- Swallow s2 = (Swallow) s1.deepClone();
-
- System.out.println("(s1==s2)="+ (s1==s2));
- System.out.println("s1.name="+ s1.getName()+", s2.name="+s2.getName());
- System.out.println("(s1.leftWing==s2.leftWing) = "+(s1.getLeftWing()==s2.getLeftWing()));
- System.out.println("(s1.rightWing==s2.rightWing) = "+(s1.getRightWing()==s2.getRightWing()));
- }
- }
运行结果:
(s1==s2)=false s1.name=大雁a, s2.name=大雁a (s1.leftWing==s2.leftWing) = false (s1.rightWing==s2.rightWing) = false
由此可见,通过序列化和反序列化来实现深克隆是最优雅、最方便的。
2015.09.02 补充:
上午有个同事说深克隆还有一个方法,就是用json,这的确是一个好方法!那么,我们可以在Swallow 类中再加一个deepClone2方法,效果是一样的:
- public Swallow deepClone2(){
- Gson gson = new Gson();
- String json = gson.toJson(this);
- return gson.fromJson(json, this.getClass());
- }
似乎更简单一些~ 不过当时我就在想这样效率是否会低一些,测试了下10万次克隆,结果如下:
deepClone 方法耗时=1564 deepClone2 方法耗时=4029