Java的深浅拷贝 或者 复制
1.基本类型之间的拷贝如:
int apples = 5;
int pears = apples;
不仅仅是int类型,其它七种原始数据类型(boolean,char,byte,short,float,double,long)同样适用于该类情况,其实就是赋值。但是如果你复制的是一个对象,情况就有些复杂了。
2.异常现象:
创建一个类:
@Data
public class GoodStudent{
private String classNum;
}
创建一个测试类:
@Test
public void test01() {
GoodStudent goodStudent1 = new GoodStudent();
GoodStudent goodStudent2 = goodStudent1;
/**
* 输出结果:
* GoodStudent(classNum=11)
* GoodStudent(classNum=11)
* 原因是:
* 上面是将new GoodStudent()对象的引用给了goodStudent2,
* 然后改了goodStudent2,goodStudent1也就跟着一起变
*/
goodStudent2.setClassNum("11");
System.out.println(goodStudent1);
System.out.println(goodStudent2);
}
测试结果:
GoodStudent(classNum=11)
GoodStudent(classNum=11)
为什么改动了goodStudent2 ,而goodStudent1也跟着一起改动呢,原来是
GoodStudent goodStudent2 = goodStudent1;
只是将goodStudent1引用的对象赋给了goodStudent2,复制的只是对象引用,所有改动goodStudent2,本会就是改动了引用的对象,所以goodStudent1也会改变!
goodStudent1和goodStudent2 指向内存堆中同一个对象
那么如果不想让goodStudent1 也改变应该怎样做呢?这就要了解对象的拷贝了:
3.浅拷贝:
那么,怎样才能达到复制一个对象呢?
是否记得万类之王Object。它有11个方法,有两个protected的方法,其中一个为clone方法。
该方法的签名是:
protected native Object clone() throws CloneNotSupportedException;
因为每个类直接或间接的父类都是Object,因此它们都含有clone()方法,但是因为该方法是protected,所以都不能在类外进行访问。
要想对一个对象进行复制,就需要对clone方法覆盖。
步骤:
1.新建一个实体类,里面重写了clone() 方法,必须要实现Cloneable接口
@Data
public class GoodStudent implements Cloneable {
private String classNum;
public Object clone() {
GoodStudent stu = null;
try{
stu = (GoodStudent)super.clone();
}catch(CloneNotSupportedException e) {
e.printStackTrace();
}
return stu;
}
}
2.新建测试类:
@Test
public void test02() throws CloneNotSupportedException {
GoodStudent stu1 = new GoodStudent();
stu1.setClassNum("1");
/**
* 这是浅拷贝 调用的是GoodStudent里面对clone方法的重写
*/
GoodStudent stu2 = (GoodStudent)stu1.clone();
System.out.println("好学生1:" + stu1.getClassNum());
System.out.println("好学生2:" + stu2.getClassNum());
stu2.setClassNum("2");
System.out.println("好学生1:" + stu1.getClassNum());
System.out.println("好学生2:" + stu2.getClassNum());
}
输出结果如下:
好学生1:1
好学生2:1
好学生1:1
好学生2:2
这样就实现了 对 对象的浅拷贝。问题又来了,如果GoodStudent中又有一个对象引用 比如 private Address address,如下代码所示,如果继续使用上面的复制方法,使类实现Cloneable接口,重写protected修饰的clone() 方法,但是里面的Address对象没有实现Cloneable接口,复制的时候,也只是复制的是对象的引用,难道让Address也要实现Cloneable接口吗,如果里面有十个八个对象呢,难道每个都需要吗,如果引用的是第三方的对象又该如何呢?
@Data
public class GoodStudent implements Cloneable {
private Address address;
private String classNum;
public Object clone() {
GoodStudent stu = null;
try{
stu = (GoodStudent)super.clone();
}catch(CloneNotSupportedException e) {
e.printStackTrace();
}
return stu;
}
}
如果进行上面的操作就是浅拷贝,那如何进行深拷贝呢?
4.深拷贝:
新建一个类:
@Data
public class Dog implements Serializable {
private String dogName;
}
再建一个类并引用Dog对象:
@Data
public class Animal implements Serializable {
private String name;
private Dog dog;
}
这两个类都实现Serializable接口:(必须要实现)
再建一个实现深拷贝的接口实现类:
public class ObjCloner {
@SuppressWarnings("unchecked")
public static <T>T cloneObj(T obj){
T retVal = null;
try{
// 将对象写入流中
ByteArrayOutputStream ins = new ByteArrayOutputStream();
ObjectOutputStream inos = new ObjectOutputStream(ins);
inos.writeObject(obj);
// 从流中读出对象
ByteArrayInputStream outs = new ByteArrayInputStream(ins.toByteArray());
ObjectInputStream ois = new ObjectInputStream(outs);
retVal = (T)ois.readObject();
}catch(Exception e){
e.printStackTrace();
}
return retVal;
}
}
测试类:
@Test
public void test03(){
Animal animal = new Animal();
animal.setName("大象");
Dog dog = new Dog();
dog.setDogName("小狗");
animal.setDog(dog);
Animal animal1 = ObjCloner.cloneObj(animal);
System.out.println("动物的名称:"+animal.getName()+" 狗的名称:"
+ animal.getDog().getDogName());
System.out.println("克隆后动物名称:"+animal1.getName()+" 克隆后狗的名称:"
+ animal1.getDog().getDogName());
System.out.println(" ----------------------------------------------------------");
animal1.setName("狮子");
animal1.getDog().setDogName("小狗狗");
System.out.println("动物的名称:"+animal.getName()+" 狗的名称:"
+ animal.getDog().getDogName());
System.out.println("克隆后动物名称:"+animal1.getName()+" 克隆后狗的名称:"
+ animal1.getDog().getDogName());
}
打印结果如下:
动物的名称:大象 狗的名称:小狗
克隆后动物名称:大象 克隆后狗的名称:小狗
---------------------------------------------------------------------------------------
动物的名称:大象 狗的名称:小狗
克隆后动物名称:狮子 克隆后狗的名称:小狗狗
这就实现了,Java对象的复制,也就是深浅拷贝