Java 复制与拷贝
大纲
浅拷贝和深拷贝
浅拷贝就是复制对象的一个精确副本
==对于基本数据类型就是复制值,对于引用数据类型而言就是复制对象地址==
深拷贝就是复制对象时
会给==引用数据类型生成一个新的空间==
浅拷贝
==基本数据类型不论是浅拷贝还是深拷贝都是传递值==
对于引用数据类型而言
浅拷贝只是拷贝了地址
一旦修改就会引起源数据的修改
==大多数对象的拷贝==一般都是浅拷贝
深拷贝
==基本数据类型不论是浅拷贝还是深拷贝都是传递值==
对于引用数据而言
深拷贝实实在在从新创建了一个内存区域存放一样的数据对象
这样修改是完全独立的
==深拷贝一般要自己实现==
一(二)维数组的拷贝
先看一个实验例子
public static void TlistClone(){
System.out.println("一维数组(基本数据包装对象)的clone是克隆值 但是都是基本数据类型的所以值不同");
Integer[] d1= new Integer[]{1,1,2,3};
Integer[] d2 = d1.clone();
d2[1]=Integer.valueOf(333);
Arrays.stream(d1).forEach(integer -> System.out.print(integer+" "));
System.out.println();
Arrays.stream(d2).forEach(integer -> System.out.print(integer+" "));
System.out.println();
System.out.println("****************************************");
System.out.println("一维数组(基本类型)的clone是克隆值 改变的基本数据类型的值");
int[] a1 = new int[]{1,2,3};
int[] a2 = a1.clone();
a2[1]=444;
System.out.println("源"+Arrays.toString(a1));
System.out.println("拷贝"+Arrays.toString(a2));
System.out.println("****************************************");
System.out.println("一维数组的System.array(Arrays.copyOf())是克隆值 改变的也是基本数据类型的值");
int[] b1 = new int[]{1,2,3};
// Arrays.copy 底层是 System.arraycopy();
int[] b2 = Arrays.copyOf(b1,b1.length);
b2[1]=1110;
System.out.println("源"+Arrays.toString(b1));
System.out.println("拷贝"+Arrays.toString(b2));
System.out.println("****************************************");
System.out.println("二维数组的直接clone是浅拷贝,因为二维数组存储其中的一维数组的是地址所以值会同步修改");
int[][] c1 = new int[][]{{1,2},{3,4}};
int[][] c2 = c1.clone();
c2[1][1]=2222;
c2[0][0]=1;
System.out.print("源");
for (int i = 0; i < 2; i++) {
System.out.println(Arrays.toString(c1[i]));
}
System.out.print("拷贝");
for (int i = 0; i < 2; i++) {
System.out.println(Arrays.toString(c2[i]));
}
System.out.println("****************************************");
System.out.println("要二维数组正确clone全部值应该对二维数组每一行进行clone");
int[][] c3 = new int[][]{{1,2},{3,4}};
int[][] c4 = new int[2][2];
for (int i = 0; i < 2; i++) {
c4[i]=c3[i].clone();
}
System.out.print("源");
for (int i = 0; i < 2; i++) {
System.out.println(Arrays.toString(c3[i]));
}
System.out.print("拷贝");
for (int i = 0; i < 2; i++) {
System.out.println(Arrays.toString(c4[i]));
}
System.out.println("这样才正确");
}
结果如下
一维数组(基本数据包装对象)的clone是克隆值 但是都是基本数据类型的所以值不同
1 1 2 3
1 333 2 3
****************************************
一维数组(基本类型)的clone是克隆值 改变的基本数据类型的值
源[1, 2, 3]
拷贝[1, 444, 3]
****************************************
一维数组的System.array(Arrays.copyOf())是克隆值 改变的也是基本数据类型的值
源[1, 2, 3]
拷贝[1, 1110, 3]
****************************************
二维数组的直接clone是浅拷贝,因为二维数组存储其中的一维数组的是地址所以值会同步修改
源[1, 2]
[3, 2222]
拷贝[1, 2]
[3, 2222]
****************************************
要二维数组正确clone全部值应该对二维数组每一行进行clone
源[1, 2]
[3, 4]
拷贝[1, 2]
[3, 4]
这样才正确
对象的拷贝
对象stu类
class Stu implements Cloneable,Serializable{
// 实现Cloneable是为了clone方法 ,实现Serializable是为了序列化对象
int id;
String name;
Location location;
public Stu(){
super();
}
public Stu(int id, String name,Location location) {
this.id = id;
this.name = name;
this.location =location;
}
@Override
protected Object clone() throws CloneNotSupportedException {
// 对location进行深拷贝需要传入location
Stu stu = (Stu) super.clone();
Location location1 = (Location) stu.getLocation().clone();
stu.setLocation(location1);
return stu;
}
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 Location getLocation() {
return location;
}
public void setLocation(Location location) {
this.location = location;
}
@Override
public String toString() {
return "Stu{" +
"id=" + id +
", name='" + name + '\'' +
", location=" + location +
'}';
}
}
对象location 类
class Location implements Cloneable{
String city;
public Location(String city) {
this.city = city;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
@Override
public String toString() {
return "Location{" +
"city='" + city + '\'' +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
测试类
public static void ObejctClone() throws CloneNotSupportedException {
System.out.println("****************************************");
System.out.println("对象的clone(实现Cloneable接口)是默认浅拷贝(即实现接口但是不重写) 引用数据类型会被同步修改了");
System.out.println("只有重写stu clone 方法的时候传入location的拷贝才可以 这里演示的是正确传入了location的拷贝");
Location location1 = new Location("格陵兰岛");
Stu stu1 = new Stu(1,"小明",location1);
Stu stu2 = (Stu) stu1.clone();
stu2.setId(2);
stu2.setName("小红");
Location location2 = stu2.getLocation();
location2.setCity("中国");
System.out.println(stu1);
System.out.println(stu2);
System.out.println("重写之后(添加对location的拷贝 两个location不一样了");
System.out.println("****************************************");
System.out.println("对象数组clone方法是浅拷贝");
Stu[] stus1 = new Stu[]{stu1,stu2};
Stu[] stus2 = stus1.clone();
System.out.println("原来");
Arrays.stream(stus1).forEach(stu -> System.out.print(stu+" "));
System.out.println();
Arrays.stream(stus2).forEach(stu -> System.out.print(stu+" "));
System.out.println();
System.out.println("更改后");
stus1[0].setLocation(new Location("日本"));
stus1[0].setName("小车");
Arrays.stream(stus1).forEach(stu -> System.out.print(stu+" "));
System.out.println();
Arrays.stream(stus2).forEach(stu -> System.out.print(stu+" "));
}
结果
****************************************
对象的clone(实现Cloneable接口)是默认浅拷贝(即实现接口但是不重写) 引用数据类型会被同步修改了
只有重写stu clone 方法的时候传入location的拷贝才可以 这里演示的是正确传入了location的拷贝
Stu{id=1, name='小明', location=Location{city='格陵兰岛'}}
Stu{id=2, name='小红', location=Location{city='中国'}}
重写之后(添加对location的拷贝 两个location不一样了
****************************************
对象数组clone方法是浅拷贝
原来
Stu{id=1, name='小明', location=Location{city='格陵兰岛'}} Stu{id=2, name='小红', location=Location{city='中国'}}
Stu{id=1, name='小明', location=Location{city='格陵兰岛'}} Stu{id=2, name='小红', location=Location{city='中国'}}
更改后
Stu{id=1, name='小车', location=Location{city='日本'}} Stu{id=2, name='小红', location=Location{city='中国'}}
Stu{id=1, name='小车', location=Location{city='日本'}} Stu{id=2, name='小红', location=Location{city='中国'}}
集合对象的拷贝
public static void ColletionClone() throws IOException, ClassNotFoundException {
System.out.println("Colletions.copy方法是浅拷贝");
List<Stu> list = new ArrayList<>();
list.add(new Stu(1,"a",null));
list.add(new Stu(2,"b",null));
List<Stu> list2 = new ArrayList<>();
list2.add(null);
list2.add(null);
Collections.copy(list2,list);
list.get(1).setName("C");
System.out.println(list);
System.out.println(list2);
System.out.println("************************************");
System.out.println("addAll方法是浅拷贝");
List<Stu> liststudent = new ArrayList<>();
liststudent.add(new Stu(1,"a",null));
liststudent.add(new Stu(2,"b",null));
List<Stu> liststudent2 = new ArrayList<>();
liststudent2.addAll(liststudent);
liststudent.get(1).setName("C");
System.out.println(liststudent);
System.out.println(liststudent2);
System.out.println("************************************");
System.out.println("构造方法传入是浅拷贝");
List<Stu> listcon = new ArrayList<>();
listcon.add(new Stu(1,"a",null));
listcon.add(new Stu(2,"b",null));
List<Stu> listcon2 = new ArrayList<>(listcon);
listcon.get(1).setName("C");
System.out.println(listcon);
System.out.println(listcon2);
System.out.println("************************************");
System.out.println("序列化 是深拷贝");
List<Stu> listserial = new ArrayList<>();
listserial.add(new Stu(1,"a",null));
listserial.add(new Stu(2,"b",null));
List<Stu> listseria2;
listseria2=deepCopy(listserial);
listserial.get(1).setName("C");
System.out.println(listserial);
System.out.println(listseria2);
}
序列化对象的方法
public static <T> List<T> deepCopy(List<T> src) throws IOException, ClassNotFoundException {
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(byteOut);
out.writeObject(src);
ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());
ObjectInputStream in = new ObjectInputStream(byteIn);
@SuppressWarnings("unchecked")
List<T> dest = (List<T>) in.readObject();
return dest;
}
结果:
Colletions.copy方法是浅拷贝
[Stu{id=1, name='a', location=null}, Stu{id=2, name='C', location=null}]
[Stu{id=1, name='a', location=null}, Stu{id=2, name='C', location=null}]
************************************
addAll方法是浅拷贝
[Stu{id=1, name='a', location=null}, Stu{id=2, name='C', location=null}]
[Stu{id=1, name='a', location=null}, Stu{id=2, name='C', location=null}]
************************************
构造方法传入是浅拷贝
[Stu{id=1, name='a', location=null}, Stu{id=2, name='C', location=null}]
[Stu{id=1, name='a', location=null}, Stu{id=2, name='C', location=null}]
************************************
序列化 是深拷贝
[Stu{id=1, name='a', location=null}, Stu{id=2, name='C', location=null}]
[Stu{id=1, name='a', location=null}, Stu{id=2, name='b', location=null}]
总结
对于基本数据类型而言
==没有深拷贝和浅拷贝==,因为都是要修改实际的值
对于引用对象类型
==一般的方法都是浅拷贝==
==只有正确重写了clone方法或者序列化了才是深拷贝==