赋值、浅复制和深复制解析 以及get/set应用

参考视频
参考文章
参考demo

  1. 首先将会用到的类及方法贴出来(实体类省去了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. . 一些基本知识
    (1). 基本类型是按值传递的,每个基本类型都是独立的
    (2).不可变量虽然是按照引用传递的,但是在改变值时,就会在内存中创建新值,并指向新值
    (3).所以从应用角度来说,可以把基本类型和不可变量看成独立的个体
    (4).引用类型(必须用new创建)是按照地址传递的,且修改是在原基础上修改,不会创建新值
    (5). X = new X(abc) 是在内存中重新开辟新的空间abc,然后X重新指向该内存,和不可变量的改变值的方式同理

    String str = "233";
    Computer computer = new Computer();
    
  2. 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]]}

  1. 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方法影响到拷贝双方的值。

下面介绍各种拷贝等级以及实现方式

  1. 数据的拷贝操作一般有三种方式:赋值操作(等号拷贝)、浅复制操作(常见工具类拷贝)和深复制操作(自写工具类拷贝),然后对于不同操作下,不同数据的表现为:赋值,浅复制和深复制。关系表如下:
操作说明表现说明
赋值操作=号拷贝赋值以任何方式改变拷贝一方的值,另一方也会跟着改变
浅复制操作常见工具类拷贝浅复制以某种方式可以独立的改变拷贝一方的值,另一方不受影响(下面会讲)
深复制操作自定义工具类拷贝深复制拷贝一方的值随意变化,另一方都不会受到影响

拷贝情况概览

符号含义符号含义符号含义
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. 最浅层的拷贝→赋值
    (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. . 再深一层的拷贝→浅复制
    (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. 最深层的拷贝→深复制
    

(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]]}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

PH = 7

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值