在这个神奇的星球上,有这样一个群体,比较呆萌,天天沉浸在代码的世界。这个代码的世界里他们天天面向对象Coding,而且这个对象还会克隆,进行分身。
1、什么时候会用到Clone呢?
一般是想对一个对象进行处理,又想保留原有的数据进行接下来的操作,这时候就需要克隆了。好比来了一件事,复制一个分身,分身去处理;自身还是继续干自己的事,分身和自身的行为和状态互不干扰影响。
2、既然Clone这么有用,那如何实现Clone呢?
Java提供了一个Cloneable接口,这只是一个标示接口,没有定义方法,不写实现这个接口的话,克隆时会报异常CloneNotSupportedException。
需要覆盖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会分身