java克隆

克隆,大家都听过,JAVA克隆实现的大致意思就是实现类的克隆,为什么JAVA要实现克隆了,那就要说到JAVA里面的指针,引用

 

我先贴一段测试代码,再慢慢解释

 

package com.linpyi.clone;

public class TestClone {

	public static void main(String[] args){
		TestBean bean1= new TestBean();
		//初始化给bean赋值
		bean1.setId(1);
		bean1.setName("name1");
		//打印出赋完值的bean
		System.out.println("bean1.id=="+bean1.getId()+",bean1.name=="+bean1.getName());
		System.out.println("******modify******");
		//实例化bean2
		TestBean bean2 = new TestBean();
		//把bean1赋给bean2,实际上这赋的是地址
		bean2=bean1;
		System.out.println("modify before bean1.id=="+bean1.getId()+",bean1.name=="+bean1.getName());
		System.out.println("modify before bean2.id=="+bean2.getId()+",bean2.name=="+bean2.getName());
		//修改bean1的值
		bean1.setId(2);
		bean1.setName("name2");
		//打印出bean1和bean2的值,发现bean2的值随bean1改变而改变,验证上面赋的是地址
		System.out.println("modify after bean1.id=="+bean1.getId()+",bean1.name=="+bean1.getName());
		System.out.println("modify after bean2.id=="+bean2.getId()+",bean2.name=="+bean2.getName());
		System.out.println("\r\n***************clone1****************\r\n");
		//实例化bean3
		TestBean bean3 = new TestBean();
		//使用克隆方法给bean3赋值
		bean3=(TestBean)bean1.clone();
		//打印未修改bean1前bean1,bean2,bean3的值
		System.out.println("clone before bean1.id=="+bean1.getId()+",bean1.name=="+bean1.getName());
		System.out.println("clone after bean2.id=="+bean2.getId()+",bean2.name=="+bean2.getName());
		System.out.println("clone before bean3.id=="+bean3.getId()+",bean3.name=="+bean3.getName());
		//修改bean1的值
		bean1.setId(3);
		bean1.setName("name3");
		//打印修改后bean的值,发现只有bean3使用克隆后的bean未改变值
		System.out.println("clone after bean1.id=="+bean1.getId()+",bean1.name=="+bean1.getName());
		System.out.println("clone after bean2.id=="+bean2.getId()+",bean2.name=="+bean2.getName());
		System.out.println("clone after bean3.id=="+bean3.getId()+",bean3.name=="+bean3.getName());
		
		System.out.println("\r\n***************clone2**************\r\n");
		
		//实现影子克隆和深度克隆,以下代码实现了深度克聋
		TestBean bean4 = new TestBean();
		bean4.setId(4);
		bean4.setName("name4");
		System.out.println("clone  bean4.id=="+bean4.getId()+",bean4.name=="+bean4.getName()+",bean4.bean.value=="+bean4.bean.i);
		TestBean bean5 = new TestBean();
		bean5=bean4;
		System.out.println("clone  bean5.id=="+bean5.getId()+",bean5.name=="+bean5.getName()+",bean5.bean.value=="+bean5.bean.i);
		TestBean bean6 = new TestBean();
		bean6=(TestBean)bean4.clone();
		System.out.println("clone before bean6.id=="+bean6.getId()+",bean5.name=="+bean6.getName()+",bean6.bean.value=="+bean6.bean.i);
		bean6.setId(6);
		bean6.setName("name6");
		bean6.bean.setValue();
		System.out.println("clone after bean4.id=="+bean4.getId()+",bean4.name=="+bean4.getName()+",bean4.bean.value=="+bean4.bean.i);
		System.out.println("clone after bean5.id=="+bean5.getId()+",bean5.name=="+bean5.getName()+",bean5.bean.value=="+bean5.bean.i);
		System.out.println("clone after bean6.id=="+bean6.getId()+",bean5.name=="+bean6.getName()+",bean6.bean.value=="+bean6.bean.i);
	
		System.out.println("\r\n***************clone3**************\r\n");
		//以下是对克隆String 和 StringBuffer的测试代码
		TestBean bean7 = new TestBean();
		bean7.setName("nam7");
		bean7.pwd="hello world.";
		bean7.sb = new StringBuffer("hello world.");
		bean7.sum=10;
		System.out.println("clone after bean7.pwd=="+bean7.pwd+",     bean7.sb=="+bean7.sb+"    bean7.sum=="+bean7.sum+",     bean7.name=="+bean7.getName());
		TestBean bean8 = new TestBean();
		bean8=(TestBean)bean7.clone();
		System.out.println("clone after bean8.pwd=="+bean8.pwd+",     bean8.sb=="+bean8.sb+"    bean8.sum=="+bean8.sum+",     bean7.name=="+bean8.getName());
		//这里实现了对String的克隆,如果改为bean8.pwd=bean8.pwd.substring(0,5)才会有效果,这又是一个赋值,达到假效果;单纯的bean8.pwd.substring(0,5)不会出现任何效果,所以对String和StringBuffer的克隆比较特别
		bean8.pwd.substring(0,5);
		//修改后,bean7的值也改变了
		bean8.sb.append("this is world.");
		//而int型的确没有改变
		bean8.sum-=20;
		//用set方法会就等于重新给他赋值,也可以实现clone的效果
		bean8.setName("name8");
		System.out.println("clone before bean7.pwd=="+bean7.pwd+",    bean7.sb=="+bean7.sb+"    bean7.sum=="+bean7.sum+",     bean7.name=="+bean7.getName());
		System.out.println("clone before bean8.pwd=="+bean8.pwd+",    bean8.sb=="+bean8.sb+"    bean8.sum=="+bean8.sum+",     bean8.name=="+bean8.getName());
	}
}

  

 

javaBean.java

 

package com.linpyi.clone;

/**
 * 测试JAVABEAN
 * @author work
 *
 */
public class TestBean implements Cloneable{
	private int id;
	private String name;
	
	public StringBuffer sb;//公有的变量,测试stringbuffer
	
	public String pwd;//公有变量,测试string
	
	public int sum;//公有变量,测试int
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	
	public TestBean1 bean = new TestBean1();
	
	public Object clone(){
		TestBean o = null;
        try{
            o = (TestBean)super.clone();
        }catch(CloneNotSupportedException e){
            e.printStackTrace();
        }
        o.bean = (TestBean1)bean.clone();
        return o;
    }

}

  

TestBean1

 

package com.linpyi.clone;

/**
 * bean1主要测试深度克隆
 * @author work
 *
 */
public class TestBean1 implements Cloneable{
	public int i=1;
	public TestBean1(){
	}
	public void setValue(){
		i=2;
	}
	public Object clone(){
		TestBean1 o = null;
        try{
            o = (TestBean1)super.clone();
        }catch(CloneNotSupportedException e){
            e.printStackTrace();
        }
        return o;
    }
}

 

 

运行结果

 

 

bean1.id==1,bean1.name==name1
******modify******
modify before bean1.id==1,bean1.name==name1
modify before bean2.id==1,bean2.name==name1
modify after bean1.id==2,bean1.name==name2
modify after bean2.id==2,bean2.name==name2

***************clone1****************

clone before bean1.id==2,bean1.name==name2
clone after bean2.id==2,bean2.name==name2
clone before bean3.id==2,bean3.name==name2
clone after bean1.id==3,bean1.name==name3
clone after bean2.id==3,bean2.name==name3
clone after bean3.id==2,bean3.name==name2

***************clone2**************

clone  bean4.id==4,bean4.name==name4,bean4.bean.value==1
clone  bean5.id==4,bean5.name==name4,bean5.bean.value==1
clone before bean6.id==4,bean5.name==name4,bean6.bean.value==1
clone after bean4.id==4,bean4.name==name4,bean4.bean.value==1
clone after bean5.id==4,bean5.name==name4,bean5.bean.value==1
clone after bean6.id==6,bean5.name==name6,bean6.bean.value==2

***************clone3**************

clone after bean7.pwd==hello world.,     bean7.sb==hello world.    bean7.sum==10,     bean7.name==nam7
clone after bean8.pwd==hello world.,     bean8.sb==hello world.    bean8.sum==10,     bean7.name==nam7
clone before bean7.pwd==hello world.,    bean7.sb==hello world.this is world.    bean7.sum==10,     bean7.name==nam7
clone before bean8.pwd==hello world.,    bean8.sb==hello world.this is world.    bean8.sum==-10,     bean8.name==name8

 

  1.首先我们先测试为什么要使用克隆,作为测试用的克隆,不需要使用TestBean1这个类,TestBean也做相应修改,注释掉

   

     克隆方法里面的 o.bean = (TestBean1)bean.clone();,测试克隆我们只用到

 

     private int id;
     private String name;

 

     和他们的set方法

 

    具体代码如下

 

    

package com.linpyi.clone;

/**
 * 测试JAVABEAN
 * @author work
 *
 */
public class TestBean implements Cloneable{
	private int id;
	private String name;
		
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	
	
	public Object clone(){
		TestBean o = null;
        try{
            o = (TestBean)super.clone();
        }catch(CloneNotSupportedException e){
            e.printStackTrace();
        }
//        o.bean = (TestBean1)bean.clone();
        return o;
    }

}

 

 

  我们实例化一个TestBean(下面称bean)bean1 然后给他赋初始值,再实例化一个bean2,把bean2=bean1,

 

  打印2个bean的值,发现值是一样的,

 

 

  接着修改bean1的值,发现2个bean都修改值了,在bean2=bean1的时候实际上指向同一个地址

 

  有很多情况下,我们需要=,但是又需要修改其中的值,因为2个bean我们都需要,当然你可以重新把bean的值打出来再赋给

 

  新的bean,但这样麻烦,所以就产生克隆的概念

 

  说说怎么克隆:

 

  比较经典的方法,其中TestBean可以改为你要克隆的类名

 

	public Object clone(){
		TestBean o = null;
        try{
            o = (TestBean)super.clone();
        }catch(CloneNotSupportedException e){
            e.printStackTrace();
        }
        o.bean = (TestBean1)bean.clone();
        return o;
    }

  

  在要实现的类上实现implements Cloneable接口

 

   implements Cloneable接口是不包含任何方法的!其实这个接口仅仅是一个标志,而且这个标志也仅仅是针对Object

 

   类中clone()方法的,如果clone类没有实现Cloneable接口,并调用了Object的clone()方法(也就是调用

 

   super.Clone()方法),那么Object的clone()方法就会抛出CloneNotSupportedException异常。

 

   这个接口属于java.lang包,java.lang包已经被缺省的导入类中,所以不需要写成java.lang.Cloneable。另一个值得

 

   请注意的是重载了clone()方法。最后在clone()方法中调用了super.clone(),这也意味着无论clone类的继承结构是什

 

   么样的,super.clone()直接或间接调用了java.lang.Object类的clone()方法。

 

  有了以上的介绍,我们开始测试

 

  首先实现bean3,使用bean3=(TestBean)bean1.clone();方法给bean赋值

 

  其次接着像上面那样修改bean1的值,结果可以发现,随着bean1的修改,bean2的值也改变了,bean3的值确没有改变

 

  说明克隆成功

 

clone after bean1.id==3,bean1.name==name3
clone after bean2.id==3,bean2.name==name3
clone after bean3.id==2,bean3.name==name2

 

2.我们来说说影子克隆

 

   实现影子克隆不能使用上面的代码,因为我已经实现了深度克隆,我再介绍下代码

 

   首先我们在上面实现了简单的克隆测试,也了解了JAVA克隆,现在我们的bean中嵌套bean

 

   也就是使用了TestBean1这个类,实现影子克隆的TestBean1的代码如下,和上面有点不一样,

 

  

package com.linpyi.clone;

/**
 * bean1主要测试深度克隆
 * @author work
 *
 */
public class TestBean1{
	public int i=1;
	public TestBean1(){
	}
	public void setValue(){
		i=2;
	}
}

 

  TestBean的代码如下

 

 

package com.linpyi.clone;

/**
 * 测试JAVABEAN
 * @author work
 *
 */
public class TestBean implements Cloneable{
	private int id;
	private String name;
	
	public StringBuffer sb;//公有的变量,测试stringbuffer
	
	public String pwd;//公有变量,测试string
	
	public int sum;//公有变量,测试int
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	
	public TestBean1 bean = new TestBean1();//其实就是多了一个bean
	
	public Object clone(){
		TestBean o = null;
        try{
            o = (TestBean)super.clone();
        }catch(CloneNotSupportedException e){
            e.printStackTrace();
        }
        return o;
    }

}

   然后我们开始测试,使用上面的clone2的代码测试

  

		System.out.println("\r\n***************clone2**************\r\n");
		
		//实现影子克隆和深度克隆
		TestBean bean4 = new TestBean();
		bean4.setId(4);
		bean4.setName("name4");
		System.out.println("clone  bean4.id=="+bean4.getId()+",bean4.name=="+bean4.getName()+",bean4.bean.value=="+bean4.bean.i);
		TestBean bean5 = new TestBean();
		bean5=bean4;
		System.out.println("clone  bean5.id=="+bean5.getId()+",bean5.name=="+bean5.getName()+",bean5.bean.value=="+bean5.bean.i);
		TestBean bean6 = new TestBean();
		bean6=(TestBean)bean4.clone();
		System.out.println("clone before bean6.id=="+bean6.getId()+",bean5.name=="+bean6.getName()+",bean6.bean.value=="+bean6.bean.i);
		bean6.setId(6);
		bean6.setName("name6");
		bean6.bean.setValue();//对TestBean1进行赋值
		System.out.println("clone after bean4.id=="+bean4.getId()+",bean4.name=="+bean4.getName()+",bean4.bean.value=="+bean4.bean.i);
		System.out.println("clone after bean5.id=="+bean5.getId()+",bean5.name=="+bean5.getName()+",bean5.bean.value=="+bean5.bean.i);
		System.out.println("clone after bean6.id=="+bean6.getId()+",bean5.name=="+bean6.getName()+",bean6.bean.value=="+bean6.bean.i);

 

其实原理和上面的克垄一样,只是多了一个对TestBean1中i的赋值

 

运行结果

 

***************clone2**************

clone  bean4.id==4,bean4.name==name4,bean4.bean.value==1
clone  bean5.id==4,bean5.name==name4,bean5.bean.value==1
clone before bean6.id==4,bean5.name==name4,bean6.bean.value==1
clone after bean4.id==4,bean4.name==name4,bean4.bean.value==2
clone after bean5.id==4,bean5.name==name4,bean5.bean.value==2
clone after bean6.id==6,bean5.name==name6,bean6.bean.value==2

 

我们实现了一个bean4和bean5,对bean4实行赋值,把bean5=bean4,打印出来,很明显2个值是一样的

 

接着我们实现bean6,对bean6实行对bean4的克聋bean6=(TestBean)bean4.clone();

 

打印出bean6的值

 

clone before bean6.id==4,bean5.name==name4,bean6.bean.value==1

 

和bean4应该是一样的

 

接着我们改变bean6的值

 

		bean6.setId(6);
		bean6.setName("name6");
		bean6.bean.setValue();//把i的值赋为2

 

打印出来

 

clone after bean4.id==4,bean4.name==name4,bean4.bean.value==2
clone after bean5.id==4,bean5.name==name4,bean5.bean.value==2
clone after bean6.id==6,bean5.name==name6,bean6.bean.value==2

 

发现bean6的id 和name改变了,而bean6中的TestBean1的value值改变了,连bean4和bean5的value一起改变了,

 

说明克隆失败,为什么了,因为,这只是实行了影子克隆,对深一步的代码没实现克隆,也就是深度克隆

 

3.深度克隆,

 

有了上面影子克隆,现在解决上面没解决的问题,

 

首先,把TestBean1中也实现克隆方法

 

package com.linpyi.clone;

/**
 * bean1主要测试深度克隆
 * @author work
 *
 */
public class TestBean1 implements Cloneable{
	public int i=1;
	public TestBean1(){
	}
	public void setValue(){
		i=2;
	}
	public Object clone(){
		TestBean1 o = null;
        try{
            o = (TestBean1)super.clone();
        }catch(CloneNotSupportedException e){
            e.printStackTrace();
        }
        return o;
    }
}

 

 

在TestBean中加入o.bean = (TestBean1)bean.clone();

package com.linpyi.clone;

/**
 * 测试JAVABEAN
 * @author work
 *
 */
public class TestBean implements Cloneable{
	private int id;
	private String name;
	
	public StringBuffer sb;//公有的变量,测试stringbuffer
	
	public String pwd;//公有变量,测试string
	
	public int sum;//公有变量,测试int
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	
	public TestBean1 bean = new TestBean1();
	
	public Object clone(){
		TestBean o = null;
        try{
            o = (TestBean)super.clone();
        }catch(CloneNotSupportedException e){
            e.printStackTrace();
        }
        o.bean = (TestBean1)bean.clone();
        return o;
    }

}

 

原理应该和上面的一样,只是多了一个克隆

 

运行结果

 

***************clone2**************

clone  bean4.id==4,bean4.name==name4,bean4.bean.value==1
clone  bean5.id==4,bean5.name==name4,bean5.bean.value==1
clone before bean6.id==4,bean5.name==name4,bean6.bean.value==1
clone after bean4.id==4,bean4.name==name4,bean4.bean.value==1
clone after bean5.id==4,bean5.name==name4,bean5.bean.value==1
clone after bean6.id==6,bean5.name==name6,bean6.bean.value==2

 

实现了影子克隆和深度克隆,克隆也有例外,那就是String和StringBuffer也就是我们克隆3做的事

 

3.关于String和StringBuffer

 

String和StringBuffer是比较特殊的类型

 

同样的代码

		System.out.println("\r\n***************clone3**************\r\n");
		//以下是对克隆String 和 StringBuffer的测试代码
		TestBean bean7 = new TestBean();
		bean7.setName("name7");
		bean7.pwd="hello world.";
		bean7.sb = new StringBuffer("hello world.");
		bean7.sum=10;
		System.out.println("clone after bean7.pwd=="+bean7.pwd+",     bean7.sb=="+bean7.sb+"    bean7.sum=="+bean7.sum+",     bean7.name=="+bean7.getName());
		TestBean bean8 = new TestBean();
		bean8=(TestBean)bean7.clone();
		System.out.println("clone after bean8.pwd=="+bean8.pwd+",     bean8.sb=="+bean8.sb+"    bean8.sum=="+bean8.sum+",     bean7.name=="+bean8.getName());
		//这里实现了对String的克隆,如果改为bean8.pwd=bean8.pwd.substring(0,5)才会有效果,这又是一个赋值,达到假效果;单纯的bean8.pwd.substring(0,5)不会出现任何效果,所以对String和StringBuffer的克隆比较特别
		bean8.pwd.substring(0,5);
		//修改后,bean7的值也改变了
		bean8.sb.append("this is world.");
		//而int型的确没有改变
		bean8.sum-=20;
		//用set方法会就等于重新给他赋值,也可以实现clone的效果
		bean8.setName("name8");
		System.out.println("clone before bean7.pwd=="+bean7.pwd+",    bean7.sb=="+bean7.sb+"    bean7.sum=="+bean7.sum+",     bean7.name=="+bean7.getName());
		System.out.println("clone before bean8.pwd=="+bean8.pwd+",    bean8.sb=="+bean8.sb+"    bean8.sum=="+bean8.sum+",     bean8.name=="+bean8.getName());

 

在bean中增加

 

 public StringBuffer sb;//公有的变量,测试stringbuffer
 
 public String pwd;//公有变量,测试string
 
 public int sum;//公有变量,测试int

 

利用公有的,是为了方便测试,不用set方法

 

实例话bean7给bean7的setName,pwd,StringBuffer,int赋值

 

使用克隆方法

 

最后打印出来会发现只有int和setName的值实现了克隆方法,

 

直接对String和StringBuffer的操作,没有效果,会改变成原来的bean7

 

如果是bean8.pwd=bean8.pwd.substring(0,5),重新给他赋值的话就可以实现,当然这样属于假克隆

 

应该知道的是在Java中所有的基本数据类型都有一个相对应的类,象Integer类对应int类型,Double类对应double类型

 

等等,这些类也与String类相同,都是不可以改变的类。也就是说,这些的类中的所有方法都是不能改变其自身的值的。这

 

也让我们在编clone类的时候有了一个更多的选择。同时我们也可以把自己的类编成不可更改的类。当然如果想实现就像上

 

面那样实现辅值,也许能实现克隆.但是StringBuffer在jdk中的不属于final,所以.....

 

写晕了,

 

 

### Java 中的对象克隆Java中,对象克隆指的是创建一个新对象,该对象的状态与现有对象完全相同。为了实现这一目标,通常有两种方式:浅拷贝和深拷贝。 #### 实现 Cloneable 接口并重写 `clone()` 方法 要使某个类支持克隆操作,需让其继承`Cloneable`接口,并覆写来自`Object`类的受保护方法`clone()`。需要注意的是,由于`clone()`默认为`protected`级别,所以在子类中重新定义时应调整访问权限以便外部能够调用此函数[^4]。 对于实现了`Cloneable`接口的类而言,当调用它的实例上的`clone()`方法时,默认行为会执行字段级别的复制,这即是所谓的浅拷贝。在这种模式下,基本数据类型的成员会被直接赋值给新的对象;而对于引用类型,则只是简单地把原对象内部持有的引用地址传递过去,两个对象实际上指向同一内存位置的数据结构[^1]。 ```java public class Person implements Cloneable { private String name; private Address address; @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } } ``` 上述代码展示了最基础形式下的克隆机制,但它仅适用于不需要处理复杂嵌套关系的情况。如果希望得到独立于源对象之外的新副本(即深拷贝),则需要进一步定制化逻辑: - 对于不可变对象或基本数据类型组成的属性可以直接沿用父类提供的`super.clone()`; - 针对可变组件如数组、集合以及自定义实体类等,则应当单独为其分配空间并通过适当手段完成初始化工作,比如再次调用各自的`clone()`或是构造器来构建全新个体[^3]。 另一种常见的做法是利用序列化技术达成深层次的复制效果。具体来讲就是先将待仿制的目标转换成字节流保存起来,再读取这些二进制信息重建出另一个同等规格的事物出来。这种方式的优势在于无需关心内部细节就能获得彻底分离的结果,不过代价可能是性能开销较大而且依赖特定环境配置[^5]。 ```java import java.io.*; public class DeepCloneableObject implements Serializable { public static T deepCopy(T obj) throws IOException, ClassNotFoundException { // 序列化 ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(bos); out.writeObject(obj); // 反序列化 ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream in = new ObjectInputStream(bis); return (T)in.readObject(); } } ``` 以上介绍了两种主流途径用于解决Java里头的对象复制难题,开发者可以根据实际需求选取合适方案加以应用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值