天天看点

我的对象Girl会分身,浅克隆、深克隆

我的对象Girl会分身,浅克隆、深克隆

在这个神奇的星球上,有这样一个群体,比较呆萌,天天沉浸在代码的世界。这个代码的世界里他们天天面向对象Coding,而且这个对象还会克隆,进行分身。

1、什么时候会用到Clone呢?

一般是想对一个对象进行处理,又想保留原有的数据进行接下来的操作,这时候就需要克隆了。好比来了一件事,复制一个分身,分身去处理;自身还是继续干自己的事,分身和自身的行为和状态互不干扰影响。

2、既然Clone这么有用,那如何实现Clone呢?

我的对象Girl会分身,浅克隆、深克隆

Java提供了一个Cloneable接口,这只是一个标示接口,没有定义方法,不写实现这个接口的话,克隆时会报异常CloneNotSupportedException。

我的对象Girl会分身,浅克隆、深克隆
我的对象Girl会分身,浅克隆、深克隆

需要覆盖Object类中的clone()方法,该方法是native修饰的。

定义一个女友类:Girl,很有钱有一辆Car,还是一个猫控,有一群猫咪。

package com.test.clone;

import java.util.List;

public class Girl implements Cloneable{
	private String name;
	private int age;
	private Car car;
	private List<Cat> catList;
	
	@Override
	public String toString() {
		return "name:"+name+", age:"+age+", car:"+car+", catList:"+catList;
	}
	
	@Override
	protected Object clone() throws CloneNotSupportedException {
		return super.clone();
	}
		
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public Car getCar() {
		return car;
	}
	public void setCar(Car car) {
		this.car = car;
	}
	public List<Cat> getCatList() {
		return catList;
	}
	public void setCatList(List<Cat> catList) {
		this.catList = catList;
	}
}

           

Car类:

package com.test.clone;

public class Car {
	private String brand;
	private double price;
	
	@Override
	public String toString() {
		return "{brand:"+brand+", price:"+price+"}";
	}
	
	public String getBrand() {
		return brand;
	}
	public void setBrand(String brand) {
		this.brand = brand;
	}
	public double getPrice() {
		return price;
	}
	public void setPrice(double price) {
		this.price = price;
	}
	
}

           

Cat类:

package com.test.clone;

public class Cat {
	private String name;

	@Override
	public String toString() {
		return "{name:"+name+"}";
	}
	
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	
}
           

测试类:

package com.test.clone;

import java.util.ArrayList;
import java.util.List;

public class TestClone {
	
	public static void main(String[] args) throws CloneNotSupportedException {
		Car car = new Car();
		car.setBrand("Audi");
		car.setPrice(213456.78);
		
		List<Cat> catList = new ArrayList<Cat>();
		Cat c1 = new Cat();
		c1.setName("cat1");
		Cat c2 = new Cat();
		c2.setName("cat2");
		catList.add(c1);
		catList.add(c2);
		
		Girl g = new Girl();
		g.setName("LiLi");
		g.setAge(18);
		g.setCar(car);
		g.setCatList(catList);
		
		System.out.println("原来的女友:"+g);
		//进行克隆
		Girl g2 = (Girl)g.clone();
		//修改属性和引用对象的属性
		g.setName("Lucy");
		g.setAge(19);
		car.setBrand("QQ");
		car.setPrice(1.99);
		c1.setName("cat1-1");
		
		System.out.println("克隆的女友:"+g2);
	}
	
}
           

运行结果:

原来的女友:name:LiLi, age:18, car:{brand:Audi, price:213456.78}, catList:[{name:cat1}, {name:cat2}]
克隆的女友:name:LiLi, age:18, car:{brand:QQ, price:1.99}, catList:[{name:cat1-1}, {name:cat2}]

           

我们可以看到,设置的Lucy和19岁没有生效,还是LiLi 18岁,设置的car和cat的属性值变化了。说明对象的非引用类型是成功复制的,而引用类型没有进行复制,还是一份。这种克隆称之为:浅克隆。

本来女友g挺有钱的,有一辆21万多的Audi;克隆后女友g2变没钱了,只有一辆QQ价值1.99元,容我哭会~~

那如何实现全部属性的克隆,让女友g2还是那么有钱呢? 这就需要实现对象的深度克隆了,简称:深克隆。

实现深克隆有以下方法:

a、所有引用类型属性实现Cloneable接口

b、利用序列化和反序列化实现

c、利用json实现

d、利用反射实现

3、实现深克隆:所有引用类型属性实现Cloneable接口

类Girl修改方法clone():

@Override
	protected Object clone() throws CloneNotSupportedException {
		Girl g = null;
		g = (Girl)super.clone();
		if(g!=null) {
			g.setCar((Car)car.clone());
			if(catList!=null) {
				List<Cat> cList = new ArrayList<Cat>();
				for(Cat c: catList) {
					cList.add((Cat)c.clone());
				}
				g.setCatList(cList);
			}
		}
		return g;
	}
           

类Car和类cat实现Cloneable接口,重写方法clone():

@Override
	protected Object clone() throws CloneNotSupportedException {
		return super.clone();
	}
           

再次运行测试类,结果如下:

原来的女友:name:LiLi, age:18, car:{brand:Audi, price:213456.78}, catList:[{name:cat1}, {name:cat2}]
克隆的女友:name:LiLi, age:18, car:{brand:Audi, price:213456.78}, catList:[{name:cat1}, {name:cat2}]

           

有钱的女友LiLi成功复制了,还是有Audi车,还是那么有钱。

不过这种方法比较繁琐,不推荐。

4、实现深克隆:利用序列化和反序列化实现

类Girl、Car、Cat实现Serializable接口;

添加方法deepCloneObject:

/**
    * 利用序列化和反序列化实现深克隆
    *
    * @param object 待克隆的对象
    * @param <T> 待克隆对象的数据类型
    * @return 已经深度克隆过的对象
    */
public static <T extends Serializable> T deepCloneObject(T object) {
       T deepClone = null;
       ByteArrayOutputStream baos = null;
       ObjectOutputStream oos = null;
       ByteArrayInputStream bais = null;
       ObjectInputStream ois = null;
       try {
           baos = new ByteArrayOutputStream();
           oos = new ObjectOutputStream(baos);
           oos.writeObject(object);
           bais = new ByteArrayInputStream(baos.toByteArray());
           ois = new ObjectInputStream(bais);
           deepClone = (T)ois.readObject();
       } catch (IOException | ClassNotFoundException e) {
          e.printStackTrace();
       } finally {
       	//资源关闭
           try {
               if(baos != null) baos.close();
           } catch (IOException e) {
               e.printStackTrace();
           }
           try {
               if(oos != null) oos.close();
           } catch (IOException e) {
               e.printStackTrace();
           }
           try{
               if(bais != null) bais.close();
           } catch (IOException e) {
               e.printStackTrace();
           }
           try{
               if(ois != null) ois.close();
           } catch (IOException e) {
               e.printStackTrace();
           }
       }
       return deepClone;
}
           

修改测试类:

//进行克隆
//Girl g2 = (Girl)g.clone();
Girl g2 = deepCloneObject(g);
           

运行结果为:

原来的女友:name:LiLi, age:18, car:{brand:Audi, price:213456.78}, catList:[{name:cat1}, {name:cat2}]
克隆的女友:name:LiLi, age:18, car:{brand:Audi, price:213456.78}, catList:[{name:cat1}, {name:cat2}]
           

已经成功深度克隆,这种方法比第一种简单很多,推荐使用。

5、实现深克隆:利用json实现

修改测试类,运行也是ok的

String json = new Gson().toJson(g);
Girl g2 = new Gson().fromJson(json, Girl.class);
           

6、实现深克隆:利用反射实现

添加方法reflectClone():

/**
* 拷贝对象方法(适合同一类型的对象复制)
 * 
 * @param objSource 源对象
 * @param clazz 目标类
 * @return
 * @throws InstantiationException
 * @throws IllegalAccessException
 */
public static <T> T reflectClone(Object objSource, Class<T> clazz)
		throws InstantiationException, IllegalAccessException {
	// 如果源对象为空,则直接返回null
	if (null == objSource) {
		return null;
	}
	
	T objDes = clazz.newInstance();
	// 获得源对象所有属性
	Field[] fields = clazz.getDeclaredFields();

	// 循环遍历字段,获取字段对应的属性值
	for (Field field : fields) {
		// 如果不为空,设置可见性,然后返回
		field.setAccessible(true);

		try {
			// 设置字段可见,即可用get方法获取属性值。
			field.set(objDes, field.get(objSource));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	return objDes;
}
           

修改测试类,运行也是ok的

Girl g2 = reflectClone(g, Girl.class);
g2.setCar(reflectClone(g.getCar(), Car.class));
List<Cat> catList2 = new ArrayList<Cat>();
g2.setCatList(catList2);
for(Cat c: g.getCatList()) {
	catList2.add(reflectClone(c, Cat.class));
}
           

实现深克隆的方式比较多,根据项目本身需要进行选择~~

欢迎朋友们关注转发点赞,谢谢~~

浏览更多文章可关注微信公众号:diggkr

我的对象Girl会分身,浅克隆、深克隆

原文参看:我的对象Girl会分身