- 首先将会用到的类及方法贴出来(实体类省去了get/set/toString方法以及无参构造)
public class Books implements Cloneable,Serializable{
private Integer id;
private String bookname;
private Double prices;
private Integer counts;
private int typeid;
public Books() {
super();
}
public Books(Integer id, String bookname, Double prices, Integer counts, int typeid) {
super();
this.id = id;
this.bookname = bookname;
this.prices = prices;
this.counts = counts;
this.typeid = typeid;
}
@Override
public Books clone() {
// TODO Auto-generated method stub
Books com = null;
try {
com = (Books) super.clone();
} catch (CloneNotSupportedException e) {
// TODO: handle exception
e.printStackTrace();
}
return com;
}
}
public class Computer implements Cloneable,Serializable{//实现Cloneable接口,然后重写clone方法,将其改为public即可
//不可变类
private String userName;
//基本类型
private double prices;
//可变类
private StringBuilder company;
//内联实体
private Books instructions;
public Computer(String userName, double prices, StringBuilder company, Books instructions) {
super();
this.userName = userName;
this.prices = prices;
this.company = company;
this.instructions = instructions;
}
//实现Cloneable接口,然后重写clone方法,将其改为public,并且返回类型改为当前类型
@Override
public Computer clone() {
// TODO Auto-generated method stub
Computer com = null;
try {
com = (Computer) super.clone();
} catch (CloneNotSupportedException e) {
// TODO: handle exception
e.printStackTrace();
}
return com;
}
}
public class Computer2 implements Cloneable,Serializable{//实现Cloneable接口,然后重写clone方法,将其改为public即可
//不可变类
private String userName;
//基本类型
private double prices;
//可变类
private StringBuilder company;
//内联实体
private Books instructions;
public Computer2(String userName, double prices, StringBuilder company, Books instructions) {
super();
this.userName = userName;
this.prices = prices;
this.company = company;
this.instructions = instructions;
}
//实现Cloneable接口,然后重写clone方法,将其改为public,并且返回类型改为当前类型
@Override
public Computer2 clone() {
// TODO Auto-generated method stub
Computer2 com = null;
try {
com = (Computer2) super.clone();
} catch (CloneNotSupportedException e) {
// TODO: handle exception
e.printStackTrace();
}
com.company = new StringBuilder(this.getCompany());//stringBuilder的构造方法
com.instructions = this.getInstructions().clone();//这里books这个类实现了cloneAble接口
//由于books里面都是基本变量和不可变类,所以,即使使用get来获取值,因为final类的存在,也可以看作是深复制来使用,
//所以下面的使用方式也是正确的,但是只限类中只包含基本类型和不可变类
//Books b = this.getInstructions();
//com.instructions = new Books(b.getId(),b.getBookname(),b.getPrices(),b.getCounts(),b.getTypeid());
return com;
}
}
private List<Computer> buildEntityList_1(){
List<Computer> list = new ArrayList<>();
list.add(new Computer("peter", 7895.25, new StringBuilder("lenovo"),
new Books(2, "产品使用说明书", 25.66, 50, 5)));
list.add(new Computer("Adrian", 7895.25, new StringBuilder("lenovo"),
new Books(2, "产品使用说明书", 25.66, 50, 5)));
list.add(new Computer("Elvis", 7895.25, new StringBuilder("lenovo"),
new Books(2, "产品使用说明书", 25.66, 50, 5)));
return list;
}
private List<Computer2> buildEntityList_2(){
List<Computer2> list = new ArrayList<>();
list.add(new Computer2("peter", 7895.25, new StringBuilder("lenovo"),
new Books(2, "产品使用说明书", 25.66, 50, 5)));
list.add(new Computer2("Adrian", 7895.25, new StringBuilder("lenovo"),
new Books(2, "产品使用说明书", 25.66, 50, 5)));
list.add(new Computer2("Elvis", 7895.25, new StringBuilder("lenovo"),
new Books(2, "产品使用说明书", 25.66, 50, 5)));
return list;
}
private List<Integer> buildBaseList_1(){
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);
return list;
}
private Map<String,Integer> buildBaseMap_1(){
Map<String,Integer> map = new HashMap<String,Integer>();
map.put("1", 1);
map.put("2", 2);
map.put("3", 4);
return map;
}
private Map<String,Computer> buildEntityMap_1(){
Map<String,Computer> map = new HashMap<String,Computer>();
map.put("1", new Computer("peter", 7895.25, new StringBuilder("lenovo"),
new Books(2, "产品使用说明书", 25.66, 50, 5)));
map.put("2", new Computer("peter", 7895.25, new StringBuilder("lenovo"),
new Books(2, "产品使用说明书", 25.66, 50, 5)));
map.put("3", new Computer("peter", 7895.25, new StringBuilder("lenovo"),
new Books(2, "产品使用说明书", 25.66, 50, 5)));
return map;
}
-
. 一些基本知识
(1). 基本类型是按值传递的,每个基本类型都是独立的
(2).不可变量虽然是按照引用传递的,但是在改变值时,就会在内存中创建新值,并指向新值
(3).所以从应用角度来说,可以把基本类型和不可变量看成独立的个体
(4).引用类型(必须用new创建)是按照地址传递的,且修改是在原基础上修改,不会创建新值
(5). X = new X(abc) 是在内存中重新开辟新的空间abc,然后X重新指向该内存,和不可变量的改变值的方式同理String str = "233"; Computer computer = new Computer();
-
get的一些使用注意事项
(1).get(list、map、实体等)得到的是引用(除基本变量)(2).get(list、map、实体等)一般是用来创建新值的,与某个元素相等的新值,而不是用来修改值的,因为即使修改成功了,浅复制中,拷贝的另一方也会被修改掉,当然深复制你想怎么修改都行
Computer computer = new Computer("peter", 7895.25, new StringBuilder("lenovo"), new Books(2, "产品使用说明书", 25.66, 50, 5)); Computer computer2 = new Computer(); BeanUtils.copyProperties(computer, computer2); //创建了新值username String userName = computer.getUserName(); //获取到了引用类型的引用 StringBuilder company = computer.getCompany(); company.append("公司"); //修改了引用类型的值,拷贝的双方的值都被修改了 System.out.println("=computer="+computer.toString()); System.out.println("=computer2="+computer2.toString()); 输出为:
=computer=Computer [userName=peter, prices=7895.25, company=lenovo公司, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]]
=computer2=Computer [userName=peter, prices=7895.25, company=lenovo公司, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]]
(3).一些错误的使用get例子
<1>.get(list、map、实体)得到的基本类型和不可变量,然后再赋予新值,是不可能改变源数据的值的,而是创建了新值,这是一种错误的修改源数据方式。
<2>.同理get(list、map、实体)得到引用类型,然后用new去赋予新值,同样不可能改变源数据,而是创建了新数据。
<3>.get(list、map、实体)修改源数据的方式只能修改非final引用类型(必须用new创建的类型),得到的是引用,且不能用等于new的方式修改(因为new又指向了新的内存),要用封装好的修改方法,例如实体的set,StringBuilder的append等,且浅拷贝会影响拷贝另一方的值。
Computer computer = new Computer("peter", 7895.25, new StringBuilder("lenovo"),
new Books(2, "产品使用说明书", 25.66, 50, 5));
Computer computer2 = new Computer();
BeanUtils.copyProperties(computer, computer2);
//创建了新值username
String userName = computer.getUserName();
//获取到了引用类型的引用
StringBuilder company = computer.getCompany();
company.append("公司");
//指向了新的内存,并没有修改源数据的值
Books instructions = computer.getInstructions();
instructions = new Books();
//修改了引用类型的值,拷贝的双方的值都被修改了
System.out.println("=computer="+computer.toString());
System.out.println("=computer2="+computer2.toString());
输出:
=computer=Computer [userName=peter, prices=7895.25, company=lenovo公司, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]]
=computer2=Computer [userName=peter, prices=7895.25, company=lenovo公司, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]]
List<Computer> list5 = buildEntityList_1();
List<Computer> list6 = new ArrayList<>();
list6.addAll(list5);
//要用封装好的改变值方法
// 因为list的浅复制为泛型的赋值,这种情况下会影响拷贝另一方的值,下面会详细讲到
Computer computer_1 = list6.get(0);
computer_1.setUserName("lisa");
computer_1.setCompany(new StringBuilder("cocaCola"));
//创建了新值,并没有修改源数据
Computer computer3 = list6.get(1);
computer3 = new Computer();
System.out.println("==list5=="+list5.toString());
System.out.println("==list6=="+list6.toString());
输出:
==list5==[Computer [userName=lisa, prices=7895.25, company=cocaCola, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]], Computer [userName=Adrian, prices=7895.25, company=lenovo, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]], Computer [userName=Elvis, prices=7895.25, company=lenovo, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]]]
==list6==[Computer [userName=lisa, prices=7895.25, company=cocaCola, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]], Computer [userName=Adrian, prices=7895.25, company=lenovo, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]], Computer [userName=Elvis, prices=7895.25, company=lenovo, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]]]
Map<String,Computer> map_3 = buildEntityMap_1();
Map<String,Computer> map_4 = map_3;
//要用封装好的set方法才能修改
//所以任何方式修改一方的值,另一方都会受到影响
Computer s1 = map_4.get("1");
s1.setUserName("lisa");
//只是创建了新值,并没有修改源数据
Computer s2 = map_4.get("2");
s2 = new Computer();
System.out.println("==map_3=="+map_3.toString());
System.out.println("==map_4=="+map_4.toString());
==map_3=={1=Computer [userName=lisa, prices=7895.25, company=lenovo, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]], 2=Computer [userName=peter, prices=7895.25, company=lenovo, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]], 3=Computer [userName=peter, prices=7895.25, company=lenovo, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]]}
==map_4=={1=Computer [userName=lisa, prices=7895.25, company=lenovo, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]], 2=Computer [userName=peter, prices=7895.25, company=lenovo, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]], 3=Computer [userName=peter, prices=7895.25, company=lenovo, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]]}
- set浅拷贝中不影响引用的使用方法(只考虑可变类,基本变量和不可变类可看成独立的个体,怎么set/put都不会影响)
(1).Set(list,实体)/put(map)一般是用来修改值的,用来将某个元素指向新的地址;
(2).所以set(new())/set(1,new())/put(new())都会给对应的元素分配新的内存,不会影响在浅拷贝下,拷贝的另一方的值
(3).但是set(x.getUser)那么X在改变user的时候,由于set也指向了该地址,所以set也就改变了,put也是如此
实体形式:
Computer computer = new Computer("peter", 7895.25, new StringBuilder("lenovo"),
new Books(2, "产品使用说明书", 25.66, 50, 5));
Computer computer2 = new Computer();
Computer computer3 = new Computer("peter", 7895.25, new StringBuilder("lenovo"),
new Books(2, "产品使用说明书", 25.66, 50, 5));
BeanUtils.copyProperties(computer, computer2);
//set赋予新值 不会影响浅复制下复制的一方
computer2.setUserName("lisa");
computer2.setInstructions(new Books());
//从computer3引用的stringBuilder computer3该属性变了,它也会跟着变
computer2.setCompany(computer3.getCompany());
//改变computer3的值,注意是改变引用,所以用get 用set(new的方法就不会改变了)
StringBuilder company = computer3.getCompany();
company.append("公司");
System.out.println("=computer="+computer.toString());
System.out.println("=computer2="+computer2.toString());
输出为:
=computer=Computer [userName=peter, prices=7895.25, company=lenovo, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]]
=computer2=Computer [userName=lisa, prices=7895.25, company=lenovo公司, instructions=Books [id=null, bookname=null, prices=null, counts=null, typeid=0]]
可以看到用new方式创建的,并没有影响到拷贝另一方的值,但是set(X.get)方式,X通过get方式做出了改变,被set的对象也做出了改变。
list/Map形式:(注意:list/map的浅拷贝,相对于list/map整体是浅拷贝,但是相对于泛型/value是赋值,下面会详细说到,所以这里说的set指的是最外层list的,put指的是最外层map的)
List<Computer> list1 = buildEntityList_1();
List<Computer> list2 = new ArrayList<>();
list2.addAll(list1);
//list级别的set不会影响拷贝的一方
list2.set(0, new Computer("jone", 7895.25, new StringBuilder("lenovo"),
new Books(2, "钢铁是怎样炼成的", 25.66, 50, 5)));
//但是,浅复制下用内部的set就会收到影响 因为内部是赋值
Computer computer4 = list2.get(1);
computer4.setUserName("jodan");
computer4.setCompany(new StringBuilder("zhouheiya"));
System.out.println("=list1="+list1.toString());
System.out.println("=list2="+list2.toString());
输出为:
=list1=[Computer [userName=peter, prices=7895.25, company=lenovo, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]], Computer [userName=jodan, prices=7895.25, company=zhouheiya, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]], Computer [userName=Elvis, prices=7895.25, company=lenovo, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]]]
=list2=[Computer [userName=jone, prices=7895.25, company=lenovo, instructions=Books [id=2, bookname=钢铁是怎样炼成的, prices=25.66, counts=50, typeid=5]], Computer [userName=jodan, prices=7895.25, company=zhouheiya, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]], Computer [userName=Elvis, prices=7895.25, company=lenovo, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]]]
可以看到,list2第0个实体,虽然改变了,但是list1并没有跟着改变,但是list2第1个实体,用内部的set来做,list1跟着改变了
Map<String,Computer> map_7 = buildEntityMap_1();
Map<String,Computer> map_8 = new HashMap<>();
map_8.putAll(map_7);
//put new的办法
map_8.put("1", new Computer());
//浅复制下用内部的set就会收到影响 因为内部是赋值
Computer computer5 = map_8.get("2");
computer5.setUserName("rainy");
computer5.setCompany(new StringBuilder("老干妈"));
System.out.println("=map_7="+map_7.toString());
System.out.println("=map_8="+map_8.toString());
=map_7={1=Computer [userName=peter, prices=7895.25, company=lenovo, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]], 2=Computer [userName=rainy, prices=7895.25, company=老干妈, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]], 3=Computer [userName=peter, prices=7895.25, company=lenovo, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]]}
=map_8={1=Computer [userName=null, prices=0.0, company=null, instructions=null], 2=Computer [userName=rainy, prices=7895.25, company=老干妈, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]], 3=Computer [userName=peter, prices=7895.25, company=lenovo, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]]}
可以看到map8的第一个value被清空了,但是map7并没有收到影响,但是get,然后用value的set方法影响到拷贝双方的值。
下面介绍各种拷贝等级以及实现方式
- 数据的拷贝操作一般有三种方式:赋值操作(等号拷贝)、浅复制操作(常见工具类拷贝)和深复制操作(自写工具类拷贝),然后对于不同操作下,不同数据的表现为:赋值,浅复制和深复制。关系表如下:
操作 | 说明 | 表现 | 说明 |
---|---|---|---|
赋值操作 | =号拷贝 | 赋值 | 以任何方式改变拷贝一方的值,另一方也会跟着改变 |
浅复制操作 | 常见工具类拷贝 | 浅复制 | 以某种方式可以独立的改变拷贝一方的值,另一方不受影响(下面会讲) |
深复制操作 | 自定义工具类拷贝 | 深复制 | 拷贝一方的值随意变化,另一方都不会受到影响 |
拷贝情况概览
符号 | 含义 | 符号 | 含义 | 符号 | 含义 |
---|---|---|---|---|---|
X | 基本类型或不可变量 | Y | 只含基本类型或不可变量实体 | Z | 含可变类(必须new创建)属性的实体 |
List< X > | 泛型为基本类型或不可变量 | List< Y > | 泛型实体,只含基本类型或不可变量属性 | List< Z > | 泛型实体,含有可变类(必须new创建)属性 |
Map< X > | value为基本类型或不可变量 | Map< Y > | value为实体,只含基本类型或不可变量属性 | Map< Z > | value为实体,含有可变类(必须new创建)属性 |
表现 | 赋值操作 | 浅复制操作 | 深复制操作 |
---|---|---|---|
X | 深复制 | 深复制 | 深复制 |
Y | 赋值 | 深复制 | 深复制 |
Z | 赋值 | 浅复制 | 深复制 |
List< X > | 赋值 | 深复制 | 深复制 |
List< Y > | 赋值 | 浅复制,Y为赋值 | 深复制 |
List< Z > | 赋值 | 浅复制,Z为赋值 | 深复制 |
Map< X > | 赋值 | 深复制 | 深复制 |
Map< Y > | 赋值 | 浅复制,Y为赋值 | 深复制 |
Map< Z > | 赋值 | 浅复制,Z为赋值 | 深复制 |
- 最浅层的拷贝→赋值
(1). 实现方式:指的是相同类型(实体、map、list等)之间用=号进行拷贝;如:User userA = UserB Map mapA = mapB List listA = listB
(2). 适用对象说明:适用于一切类型,对于基本类型和不可变量是深复制
(3).数据表现:除基本类型和不可变量外,以任何形式改变拷贝的一方的值,另一方同样会被改变
//基本类型
int i = 2;
int j = i;
j = 3;
System.out.println("=i="+i);
System.out.println("=j="+j);
//基本类型 list
List<Integer> list1 = buildBaseList_1();
List<Integer> list2 = list1;
list2.set(0, 9);
System.out.println("==list1=="+list1.toString());
System.out.println("==list2=="+list2.toString());
//基本类型 map
Map<String,Integer> map_1 = buildBaseMap_1();
Map<String,Integer> map_2 = map_1;
map_2.put("1", 2);
System.out.println("==map_1=="+map_1.toString());
System.out.println("==map_2=="+map_2.toString());
//实体类型
Computer computer_1 = new Computer("peter", 7895.25, new StringBuilder("lenovo"),
new Books(2, "产品使用说明书", 25.66, 50, 5));
Computer computer_2 = computer_1;
computer_2.setUserName("lisa");
computer_2.setPrices(78.88);
computer_2.setCompany(new StringBuilder("cocaCola"));
System.out.println("==computer_1=="+computer_1.toString());
System.out.println("==computer_2=="+computer_2.toString());
//list 实体类型
List<Computer> list5 = buildEntityList_1();
List<Computer> list6 = list5;
list6.set(0, new Computer("jone", 7895.25, new StringBuilder("lenovo"),
new Books(2, "钢铁是怎样炼成的", 25.66, 50, 5)));
System.out.println("=list5="+list5.toString());
System.out.println("=list6="+list6.toString());
//map实体
Map<String,Computer> map_3 = buildEntityMap_1();
Map<String,Computer> map_4 = map_3;
map_4.put("1", new Computer());
System.out.println("==map_3=="+map_3.toString());
System.out.println("==map_4=="+map_4.toString());
输出:
=i=2
=j=3
==list1==[9, 2, 3, 4, 5]
==list2==[9, 2, 3, 4, 5]
==map_1=={1=2, 2=2, 3=4}
==map_2=={1=2, 2=2, 3=4}
==computer_1==Computer [userName=lisa, prices=78.88, company=cocaCola, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]]
==computer_2==Computer [userName=lisa, prices=78.88, company=cocaCola, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]]
=list5=[Computer [userName=jone, prices=7895.25, company=lenovo, instructions=Books [id=2, bookname=钢铁是怎样炼成的, prices=25.66, counts=50, typeid=5]], Computer [userName=Adrian, prices=7895.25, company=lenovo, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]], Computer [userName=Elvis, prices=7895.25, company=lenovo, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]]]
=list6=[Computer [userName=jone, prices=7895.25, company=lenovo, instructions=Books [id=2, bookname=钢铁是怎样炼成的, prices=25.66, counts=50, typeid=5]], Computer [userName=Adrian, prices=7895.25, company=lenovo, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]], Computer [userName=Elvis, prices=7895.25, company=lenovo, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]]]
==map_3=={1=Computer [userName=null, prices=0.0, company=null, instructions=null], 2=Computer [userName=peter, prices=7895.25, company=lenovo, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]], 3=Computer [userName=peter, prices=7895.25, company=lenovo, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]]}
==map_4=={1=Computer [userName=null, prices=0.0, company=null, instructions=null], 2=Computer [userName=peter, prices=7895.25, company=lenovo, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]], 3=Computer [userName=peter, prices=7895.25, company=lenovo, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]]}
可以看到,除单个的基本类型和不可变量是独立的以外,即表现为深复制互不影响,其他的都是相互影响的
- . 再深一层的拷贝→浅复制
(1).也是数据逻辑最为繁杂的拷贝方式
(2).实现方式:常见于99.99%拷贝工具类,基本不存在深复制工具类:如实体的beanUtils、mapStruct、 clone的初级实现;列表list的addAll, System.arraycopy() ;Map 的 putAll等
(3) 适用对象说明:
< 1 >. 以下统一将list的每一个泛型,map的每一个value 称为单位X;
< 2 >. 对于单个复制单位 ,如果是基本类型和不可变量,以及只含有基本类型和不可变量的实体,已经算是深复制了.如果是实体,且实体中存在的非final引用类型(内嵌的实体,list,可变类等必须用new创建的属性),对于该部分就是浅复制;
< 3 >.对于X,如果X是基本类型或不可变量,已经算是深复制了.如果X可变类(必须new创建),该部分就是浅复制,且X表现为赋值;
(4).数据表现:
< 1 >.对于单个复制单位 ,如果是基本类型和不可变量,以及只含有基本类型和不可变量的实体,已经算是深复制了,所以以任何方式改变一方的值另一方都不会收到影响;
如果是实体,且实体中存在的非final引用类型(内嵌的实体,list,可变类等必须用new创建的属性),对于该部分就是浅复制;set是将该部分指向了另一部分内存,不会影响在浅复制下另一方的数据,但是当set(X.getUser)这样传引用的话,那么当X的user对象改变的情况下,被传的对象也会受影响;get是获得引用,和前面说的一样,对于基本类型和不可变量,不可以用get来修改,即使get出来了,赋予新值也是产生了新的变量,对于可变类属性,get也不能用new的办法来修改,否则产生的也是新值,要用封装好的修改方法(entity.set ;StringBuilder.append; list.set())来修改
< 2 >.List,map的浅复制,表现为list,map的浅复制,x的赋值,也就是说对于X对于基本类型和不可变量,list和map表现为深复制.
但是当X为可变类(非final的引用类型),则表现为(list,map)的浅复制,X的赋值.
list的set.map的put,是将该部分指向了另一部分内存,不会影响在浅复制下另一方的数据,但是当set(X.getUser),put(X.getUser)这样传引用的话,那么当X的user对象改变的情况下,被传的对象也会受影响。list.get和map.get可以看作是单个被复制的元素(或实体)Y,Y的get/set表现同单个赋值一样,即如果不是基本类型和不可变量,一方以任何形式改变,另一方必改变。
< 2 > 单个复制单位的浅复制操作有 工具类 beanUtils,mapStruct等 clone的初级实现,下面将分别进行讲解
a. 工具类 beanUtils,mapStruct等只针对 beanUtils进行讲解,示例如下:
Computer computer_1 = new Computer("peter", 7895.25, new StringBuilder("lenovo"),
new Books(2, "产品使用说明书", 25.66, 50, 5));
Computer computer_2 = new Computer();
BeanUtils.copyProperties(computer_1, computer_2);
Computer computer_3 = new Computer("peter", 7895.25, new StringBuilder("lenovo"),
new Books(2, "产品使用说明书", 25.66, 50, 5));
//set用new的方式不会改变双方的值
computer_2.setUserName("lisa");
computer_2.setCompany(new StringBuilder("周黑鸭"));
//set(X.get)方式是传入了别的地方的引用,别的地方数据改变 ,它的数据也跟着变了
computer_2.setInstructions(computer_3.getInstructions());
//改变computer_3的值 get方式修改,会得到引用的值 set(new 的方法并不会改变引用的值)
//computer_3.setInstructions(new Books());
Books books = computer_3.getInstructions();
books.setBookname("鲁宾孙漂流记");
System.out.println("=computer_1="+computer_1.toString());
System.out.println("=computer_2="+computer_2.toString());
//因为上面的都指向了新的内存,所以重新做数据验证get
Computer computer_4 = new Computer("peter", 7895.25, new StringBuilder("lenovo"),
new Books(2, "产品使用说明书", 25.66, 50, 5));
Computer computer_5 = new Computer();
BeanUtils.copyProperties(computer_4, computer_5);
//get 方式不能用来修改基本类型和不可变量
String userName = computer_5.getUserName();
userName = "black";
//get方式修改可变类 用new的方式也是错误的,也是创建了新值
Books instructions = computer_5.getInstructions();
instructions = new Books();
//get得到的引用,要用封装好的改变值的方法去修改 并且会影响到浅引用下的值
StringBuilder company = computer_5.getCompany();
company.append("公司");
System.out.println("=computer_4="+computer_4.toString());
System.out.println("=computer_5="+computer_5.toString());
输出
=computer_1=Computer [userName=peter, prices=7895.25, company=lenovo, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]]
=computer_2=Computer [userName=lisa, prices=7895.25, company=周黑鸭, instructions=Books [id=2, bookname=鲁宾孙漂流记, prices=25.66, counts=50, typeid=5]]
=computer_4=Computer [userName=peter, prices=7895.25, company=lenovo公司, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]]
=computer_5=Computer [userName=peter, prices=7895.25, company=lenovo公司, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]]
可以看到,set(new)是将该变量指向了新的内存地址,并不会改变引用的值,get得到的是引用,牵一发动全身,且不能修改基本类型和不可变量的值
b.clone工具浅复制(相对于只含基本类型和不可变量是深复制)的实现
实现Cloneable接口,然后重写clone方法,最上面有源码
然后直接使用即可,效果同上
Computer computer_1 = new Computer("peter", 7895.25, new StringBuilder("lenovo"),
new Books(2, "产品使用说明书", 25.66, 50, 5));
Computer computer_2 = computer_1.clone();
Computer computer_3 = new Computer("peter", 7895.25, new StringBuilder("lenovo"),
new Books(2, "产品使用说明书", 25.66, 50, 5));
//set用new的方式不会改变双方的值
computer_2.setUserName("lisa");
computer_2.setCompany(new StringBuilder("周黑鸭"));
//set(X.get)方式是传入了别的地方的引用,别的地方数据改变 ,它的数据也跟着变了
computer_2.setInstructions(computer_3.getInstructions());
//改变computer_3的值 get方式修改,会得到引用的值 set(new 的方法并不会改变引用的值)
//computer_3.setInstructions(new Books());
Books books = computer_3.getInstructions();
books.setBookname("鲁宾孙漂流记");
System.out.println("=computer_1="+computer_1.toString());
System.out.println("=computer_2="+computer_2.toString());
//因为上面的都指向了新的内存,所以重新做数据验证get
Computer computer_4 = new Computer("peter", 7895.25, new StringBuilder("lenovo"),
new Books(2, "产品使用说明书", 25.66, 50, 5));
Computer computer_5 = computer_4.clone();
//get 方式不能用来修改基本类型和不可变量
String userName = computer_5.getUserName();
userName = "black";
//get方式修改可变类 用new的方式也是错误的,也是创建了新值
Books instructions = computer_5.getInstructions();
instructions = new Books();
//get得到的引用,要用封装好的改变值的方法去修改 并且会影响到浅引用下的值
StringBuilder company = computer_5.getCompany();
company.append("公司");
System.out.println("=computer_4="+computer_4.toString());
System.out.println("=computer_5="+computer_5.toString());
输出为:
=computer_1=Computer [userName=peter, prices=7895.25, company=lenovo, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]]
=computer_2=Computer [userName=lisa, prices=7895.25, company=周黑鸭, instructions=Books [id=2, bookname=鲁宾孙漂流记, prices=25.66, counts=50, typeid=5]]
=computer_4=Computer [userName=peter, prices=7895.25, company=lenovo公司, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]]
=computer_5=Computer [userName=peter, prices=7895.25, company=lenovo公司, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]]
< 3 >list,map的浅复制操作
list的浅复制实现方式很多包括循环遍历插入,构造函数,AddAll,和System.arraycopy(),Map的浅复制方式是putAll,下面以addAll/putAll进行举例
List<Integer> list3 = buildBaseList_1();
List<Integer> list4 = new ArrayList<>();
list4.addAll(list3);
list4.add(6);
System.out.println("==list3=="+list3.toString());
System.out.println("==list4=="+list4.toString());
List<Computer> list1 = buildEntityList_1();
List<Computer> list2 = new ArrayList<>();
list2.addAll(list1);
//list级别的set不会影响拷贝的一方
list2.set(0, new Computer("jone", 7895.25, new StringBuilder("lenovo"),
new Books(2, "钢铁是怎样炼成的", 25.66, 50, 5)));
//但是,浅复制下用内部的set就会收到影响 因为内部是赋值
Computer computer4 = list2.get(1);
computer4.setUserName("jodan");
computer4.setCompany(new StringBuilder("zhouheiya"));
System.out.println("=list1="+list1.toString());
System.out.println("=list2="+list2.toString());
Map<String,Integer> map_5 = buildBaseMap_1();
Map<String,Integer> map_6 = new HashMap<>();
map_6.putAll(map_5);
map_6.put("1", 2);
System.out.println("==map_5=="+map_5.toString());
System.out.println("==map_6=="+map_6.toString());
Map<String,Computer> map_7 = buildEntityMap_1();
Map<String,Computer> map_8 = new HashMap<>();
map_8.putAll(map_7);
//put new的办法
map_8.put("1", new Computer());
//浅复制下用内部的set就会收到影响 因为内部是赋值
Computer computer5 = map_8.get("2");
computer5.setUserName("rainy");
computer5.setCompany(new StringBuilder("老干妈"));
System.out.println("=map_7="+map_7.toString());
System.out.println("=map_8="+map_8.toString());
输出为:
==list3==[1, 2, 3, 4, 5]
==list4==[1, 2, 3, 4, 5, 6]
=list1=[Computer [userName=peter, prices=7895.25, company=lenovo, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]], Computer [userName=jodan, prices=7895.25, company=zhouheiya, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]], Computer [userName=Elvis, prices=7895.25, company=lenovo, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]]]
=list2=[Computer [userName=jone, prices=7895.25, company=lenovo, instructions=Books [id=2, bookname=钢铁是怎样炼成的, prices=25.66, counts=50, typeid=5]], Computer [userName=jodan, prices=7895.25, company=zhouheiya, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]], Computer [userName=Elvis, prices=7895.25, company=lenovo, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]]]
==map_5=={1=1, 2=2, 3=4}
==map_6=={1=2, 2=2, 3=4}
=map_7={1=Computer [userName=peter, prices=7895.25, company=lenovo, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]], 2=Computer [userName=rainy, prices=7895.25, company=老干妈, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]], 3=Computer [userName=peter, prices=7895.25, company=lenovo, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]]}
=map_8={1=Computer [userName=null, prices=0.0, company=null, instructions=null], 2=Computer [userName=rainy, prices=7895.25, company=老干妈, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]], 3=Computer [userName=peter, prices=7895.25, company=lenovo, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]]}
可以看到,当list泛型/map value为基本类型和不变量时,表现为深复制,即使用任何方法改变一方的值,另一方都不会受到影响,但是当泛型、value为引用类型,表现为浅复制,内部表现为赋值,因为基本类型和不可变量属性的改变也会影响到另一方的值,list等级的set(new)/map的put(new)不会影响另一方的值,list.get()/map.get()可以看成一个通过赋值操作过来的值,以任何方式改变,都会影响另一方的值
-
最深层的拷贝→深复制
(1)实现的方式一般是构造函数,clone的深层实现,以及序列化,没有工具类,需要自己编写;
(2)单个复制单位,list的每一个泛型,map的每一个value 称为单位X,如果X是实体,存在引用类型(内嵌的实体,list,可变类等必须用new创建的属性),那么就需要深复制来阻断引用的传递。其他情况在之前的两种情况已经变为了深复制。
(3)数据改变:拷贝的一方无论怎么改变,另一方都不会受到影响
(4).具体实现
< 1 > 单个复制单位的深复制(构造函数、clone深层实现、序列化)
1)构造函数方式
Computer computer_1 = new Computer("peter", 7895.25, new StringBuilder("lenovo"),
new Books(2, "产品使用说明书", 25.66, 50, 5));
Computer computer_2 = new Computer("peter", 7895.25, new StringBuilder("lenovo"),
new Books(2, "产品使用说明书", 25.66, 50, 5));
// 使用get都会影响到引用 所以这么做是浅复制 get其实就是将引用传过来
Computer computer_3 = new Computer(computer_1.getUserName(), computer_1.getPrices(), computer_1.getCompany(),
computer_1.getInstructions());
computer_3.setUserName("jone");
StringBuilder company_3 = computer_3.getCompany();
company_3.append("公司");
System.out.println("==computer_1==" + computer_1.toString());
System.out.println("==computer_2==" + computer_2.toString());
System.out.println("==computer_3==" + computer_3.toString());
输出为
==computer_1==Computer [userName=peter, prices=7895.25, company=lenovo公司, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]]
==computer_2==Computer [userName=peter, prices=7895.25, company=lenovo, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]]
==computer_3==Computer [userName=jone, prices=7895.25, company=lenovo公司, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]]
可以看到,构造函数方式创建的是独立的两个个体,但是参数如果是get获得到的,那么就不是深复制了,因为拿到的是别的地方的引用,别的地方会修改引用的值。
2)clone深复制
实现cloneable接口,并重写clone方法,而且方法中对于引用类型,非自定义的可变类,用它本身封装的构造函数来创建新值。
自定的可变类属性A,如果A只含有基本类型和不可变量,那么利用A的构造函数,或者浅层clone(因为对于只含有基本类型和不可变量的实体,浅层clone也能做到深复制)。
如果A含有引用类型B,就需要B实现深层clone,A也实现深层clone,总之不能传任何可变类的引用到新的数据里
具体代码见最上面
Computer2 computer2_1 = new Computer2("peter", 7895.25, new StringBuilder("lenovo"),
new Books(2, "产品使用说明书", 25.66, 50, 5));
Computer2 computer2_2 = computer2_1.clone();
computer2_2.setUserName("jone");
StringBuilder company2 = computer2_2.getCompany();
company2.append("公司");
System.out.println("==computer2_1=="+computer2_1.toString());
输出为:
==computer2_1==Computer [userName=peter, prices=7895.25, company=lenovo, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]]
3)序列化
需要被拷贝的实现序列化接口,内嵌的可变类也实现序列化接口
然后序列化工具类
//hashMap序列化深复制工具类 不能序列化map因为map没有实现序列化接口
//同时也可以深复制实体(实现序列化接口)
public static <T extends Serializable> T deepCopy(T obj) {
T clonedObj = null;
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(obj);
oos.close();
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
clonedObj = (T) ois.readObject();
ois.close();
} catch (Exception e) {
e.printStackTrace();
}
return clonedObj;
}
使用情况:
Computer2 computer2_3 = new Computer2("peter", 7895.25, new StringBuilder("lenovo"),
new Books(2, "产品使用说明书", 25.66, 50, 5));
Computer2 computer2_4 = deepCopy(computer2_3);
computer2_4.getCompany().append("公司");
System.out.println("==computer2_4=="+computer2_4.toString());
System.out.println("==computer2_3=="+computer2_3.toString());
输出为:
==computer2_4==Computer [userName=peter, prices=7895.25, company=lenovo公司, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]]
==computer2_3==Computer [userName=peter, prices=7895.25, company=lenovo, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]]
< 2 >Map深复制
value 也可以用构造函数的方式创建,也可以通过深层clone来创建,参考上面,实现为:
Map<String,Computer> map_7 = buildEntityMap_1();
Map<String,Computer> map_8 = new HashMap<>();
//循环遍历map_7
for(Map.Entry<String, Computer> entry : map_7.entrySet()){
String mapKey = entry.getKey();
Computer mapValue = entry.getValue();
//注意,这里的clone必须是经过深复制处理的,这里借浅复制演示下
map_8.put(mapKey, mapValue.clone());
}
Computer computer = map_8.get("1");
computer.setUserName("zhugeliang");
System.out.println("=map_7="+map_7.toString());
System.out.println("=map_8="+map_8.toString());
输出为:
=map_7={1=Computer [userName=peter, prices=7895.25, company=lenovo, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]]]}
=map_8={1=Computer [userName=zhugeliang, prices=7895.25, company=lenovo, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]]]}
Map<String,Computer> map_7 = buildEntityMap_1();
Map<String,Computer> map_8 = new HashMap<>();
//循环遍历map_7
for(Map.Entry<String, Computer> entry : map_7.entrySet()){
String mapKey = entry.getKey();
//注意,这里的clone必须是经过深复制处理的,这里借浅复制演示下
map_8.put(mapKey, new Computer());
}
下面介绍,序列化深复制
首先value要实现序列化,然后用上面单体复制的工具类来拷贝即可(注意,由于MAP没有实现序列化接口,所以只适用于hashmap)
HashMap<String,Computer> map_1 = (HashMap<String, Computer>) buildEntityMap_1();
Map<String,Computer> map_2 = new HashMap<>();
map_2 = deepCopy(map_1);
Computer computer2 = map_2.get("1");
computer2.getCompany().append("公司");
System.out.println("==map_1=="+map_1.toString());
System.out.println("==map_2=="+map_1.toString());
输出为
==map_1=={1=Computer [userName=peter, prices=7895.25, company=lenovo, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]], 2=Computer [userName=peter, prices=7895.25, company=lenovo, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]], 3=Computer [userName=peter, prices=7895.25, company=lenovo, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]]}
==map_2=={1=Computer [userName=peter, prices=7895.25, company=lenovo, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]], 2=Computer [userName=peter, prices=7895.25, company=lenovo, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]], 3=Computer [userName=peter, prices=7895.25, company=lenovo, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]]}
< 3 >list的深复制
1)list的深复制,也可以用构造函数,深层clone和序列化来解决,构造函数和深层clone用for循环,依次插入即可
List<Computer2> list11 = new ArrayList<>();
for(Computer2 c :list9) {
list11.add(c.clone());
}
list11.get(0).getCompany().append("公司");
list11.get(0).getInstructions().setBookname("武林外传");
System.out.println("==list9=="+list9.toString());
输出为:
2)下面介绍序列化
首先泛型也必须实现序列化接口
然后序列化工具类
//实现序列化后的list深复制
private static <T> List<T> depCopy(List<T> srcList) {
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
try {
ObjectOutputStream out = new ObjectOutputStream(byteOut);
out.writeObject(srcList);
ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());
ObjectInputStream inStream = new ObjectInputStream(byteIn);
List<T> destList = (List<T>) inStream.readObject();
return destList;
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
然后使用
//序列化方法
List<Computer2> list9 = buildEntityList_2();
List<Computer2> list10 = depCopy(list9);
list10.get(0).getCompany().append("公司");
System.out.println("==list9=="+list9.toString());
//clone方法
List<Computer2> list11 = new ArrayList<>();
for(Computer2 c :list9) {
list11.add(c.clone());
}
list11.get(0).getCompany().append("公司");
list11.get(0).getInstructions().setBookname("武林外传");
System.out.println("==list9=="+list9.toString());
输出为:
==map_1=={1=Computer [userName=peter, prices=7895.25, company=lenovo, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]], 2=Computer [userName=peter, prices=7895.25, company=lenovo, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]], 3=Computer [userName=peter, prices=7895.25, company=lenovo, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]]}
==map_2=={1=Computer [userName=peter, prices=7895.25, company=lenovo, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]], 2=Computer [userName=peter, prices=7895.25, company=lenovo, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]], 3=Computer [userName=peter, prices=7895.25, company=lenovo, instructions=Books [id=2, bookname=产品使用说明书, prices=25.66, counts=50, typeid=5]]}