最近工作中需要用到实体对象的复制,于是复习一下java中关于复制的知识。
一、概念
1.浅复制:复制出来的对象中的变量(包括基本类型和字符串)和原来的对象的值都相同,但是引用对象仍然指向原来的对象。
2.深复制:复制出来的对象中的变量(包括基本类型和字符串)和原来的对象的值都相同,引用对象也会指向复制出来的对象。
浅复制与深复制的不同之处就在于深复制还会复制对象的引用对象。
二、实现复制
1.使用Cloneable接口实现对象的复制
⑴浅复制
代码如下
public class Food {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class Animal implements Cloneable {
// 动物名
private String name;
// 食物
private Food food;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Food getFood() {
return food;
}
public void setFood(Food food) {
this.food = food;
}
public Animal clone() {
Animal animal = null;
try {
animal = (Animal) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return animal;
}
}
public class TestCopy {
public static void main(String[] args) {
Food food = new Food();
food.setName("草");
Animal animal = new Animal();
animal.setName("羊");
animal.setFood(food);
Animal animal2 = animal.clone();
System.out.println("animal动物名:"+animal.getName());
System.out.println("animal食物名:"+animal.getFood().getName());
System.out.println("animal2动物名:"+animal2.getName());
System.out.println("animal2食物名:"+animal2.getFood().getName());
}
}
打印结果是
animal动物名:羊
animal食物名:草
animal2动物名:羊
animal2食物名:草
此时已成功的实现了复制,但是这只是浅复制,引用对象Food并没有被复制。
验证代码
public class TestCopy {
public static void main(String[] args) {
Food food = new Food();
food.setName("草");
Animal animal = new Animal();
animal.setName("羊");
animal.setFood(food);
Animal animal2 = animal.clone();
System.out.println("animal动物名:"+animal.getName());
System.out.println("animal食物名:"+animal.getFood().getName());
System.out.println("animal2动物名:"+animal2.getName());
System.out.println("animal2食物名:"+animal2.getFood().getName());
//改变animal中food对象的值
food.setName("树叶");
System.out.println("animal动物名:"+animal.getName());
System.out.println("animal食物名:"+animal.getFood().getName());
System.out.println("animal2动物名:"+animal2.getName());
System.out.println("animal2食物名:"+animal2.getFood().getName());
}
}
打印结果是
animal动物名:羊
animal食物名:草
animal2动物名:羊
animal2食物名:草
animal动物名:羊
animal食物名:树叶
animal2动物名:羊
animal2食物名:树叶
结果证明改变animal中的引用对象food,同时也改变了animal2中的引用对象food,说明animal和animal2的引用对象food是同一对象。
⑵深复制
要实现深复制,就必须同时复制引用对象,引用对象需实现Cloneable接口,Food修改如下
public class Food implements Cloneable{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Food clone() {
Food food = null;
try {
food = (Food) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return food;
}
}
Animal修改如下
public class Animal implements Cloneable {
// 动物名
private String name;
// 食物
private Food food;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Food getFood() {
return food;
}
public void setFood(Food food) {
this.food = food;
}
public Animal clone() {
Animal animal = null;
try {
animal = (Animal) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
//复制引用对象
animal.food = food.clone();
return animal;
}
}
在clone方法中添加了引用对象的复制
使用上面的main方法测试,打印结果如下
animal动物名:羊
animal食物名:草
animal2动物名:羊
animal2食物名:草
animal动物名:羊
animal食物名:树叶
animal2动物名:羊
animal2食物名:草
结果证明animal和animal2的引用对象food已经不是同一对象了,此时成功地实现了深复制。
2.使用序列化(Serializable接口)实现对象的复制
使用Cloneable接口每个类都需要写clone方法,工作量是很大的,我们可以使用序列化来实现对象的拷贝,这需要对象实现java.io.Serializable接口。
将上面的Animal和Food修改一下
import java.io.Serializable;
public class Food implements Serializable{
private static final long serialVersionUID = 6466656398591229036L;
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
import java.io.Serializable;
public class Animal implements Serializable {
private static final long serialVersionUID = -8424013303049171827L;
// 动物名
private String name;
// 食物
private Food food;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Food getFood() {
return food;
}
public void setFood(Food food) {
this.food = food;
}
}
再写一个复制的工具类
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class CopyUtil {
@SuppressWarnings("unchecked")
public static <T extends Serializable> T clone(T obj) {
T cloneObj = null;
ByteArrayOutputStream baos = null;
ByteArrayInputStream bais = null;
ObjectOutputStream oos = null;
ObjectInputStream ois = null;
try {
// 序列化
baos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(baos);
oos.writeObject(obj);
// 反序列化,生成新对象
bais = new ByteArrayInputStream(
baos.toByteArray());
ois = new ObjectInputStream(bais);
cloneObj = (T) ois.readObject();
} catch (Exception e) {
e.printStackTrace();
} finally{
try {
if(oos!=null){
oos.close();
}
if(baos!=null){
baos.close();
}
if(ois!=null){
ois.close();
}
if(bais!=null){
bais.close();
}
} catch (Exception e2) {
e2.printStackTrace();
}
}
return cloneObj;
}
}
测试代码
public class TestCopy {
public static void main(String[] args) {
Food food = new Food();
food.setName("草");
Animal animal = new Animal();
animal.setName("羊");
animal.setFood(food);
Animal animal2 = CopyUtil.clone(animal);
System.out.println("animal动物名:"+animal.getName());
System.out.println("animal食物名:"+animal.getFood().getName());
System.out.println("animal2动物名:"+animal2.getName());
System.out.println("animal2食物名:"+animal2.getFood().getName());
//改变animal中food对象的值
food.setName("树叶");
System.out.println("animal动物名:"+animal.getName());
System.out.println("animal食物名:"+animal.getFood().getName());
System.out.println("animal2动物名:"+animal2.getName());
System.out.println("animal2食物名:"+animal2.getFood().getName());
}
}
打印结果如下
animal动物名:羊
animal食物名:草
animal2动物名:羊
animal2食物名:草
animal动物名:羊
animal食物名:树叶
animal2动物名:羊
animal2食物名:草
利用序列化成功的实现了深复制。