JAVA-面向对象之对象拷贝
Java 中的数据类型分为基本数据类型和引用数据类型。对于这两种数据类型,在进行赋值操作、用作方法参数或返回值时,会有值传递和引用(地址)传递的差别。
Map对象
测试01-等号赋值:
@Test
public void TestCopy() {
Map<String, Object> beginByMap = new HashMap<>();
beginByMap.put("key01", "Value01");
Map<String, Object> endByMap = beginByMap;
endByMap.put("key02", "Value02");
System.out.println("beginByMap:" + beginByMap);
System.out.println("endByMap:" + endByMap);
}
对象复制或者说拷贝在等号下的引用类型是浅拷贝:{拷贝分为浅拷贝和深度拷贝}
可以看出 对象的 = 号赋值时 是浅复制,两个对象实际引用的是同一个地址,所以不管谁改变了,都会变。。
有时候我们又需要两个对象相等但是互相操作不干扰
浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址 ,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。
深拷贝,在拷贝引用类型成员变量时,为引用类型的数据成员另辟了一个独立的内存空间,实现真正内容上的拷贝。
(1) 对于基本数据类型的成员对象,因为基础数据类型是值传递的,所以是直接将属性值赋值给新的对象。基础类型的拷贝,其中一个对象修改该值,不会影响另外一个(和浅拷贝一样)。
(2) 对于引用类型,比如数组或者类对象,深拷贝会新建一个对象空间,然后拷贝里面的内容,所以它们指向了不同的内存空间。改变其中一个,不会对另外一个也产生影响。
(3) 对于有多层对象的,每个对象都需要实现 Cloneable 并重写 clone() 方法,进而实现了对象的串行层层拷贝。
(4) 深拷贝相比于浅拷贝速度较慢并且花销较大。
我这里JDK1.8
结果是:不管使用MAP的PUTALLH还是HashMap的PutAll都是属于深度拷贝
但是网上一部分人说的是HashMap才是深度拷贝,而MAp的不是深度拷贝,这个有待考察
@Test
public void TestCopy() {
Map<String, Object> beginByMap = new HashMap<>();
beginByMap.put("key01", "ADC");
//使用浅拷贝
Map<String, Object> endByMap = new HashMap<>();
endByMap.putAll(beginByMap);
endByMap.put("key02", "ABC");
endByMap.put("key03", "ADA");
endByMap.remove("key01");
System.out.println("beginByMap:" + beginByMap);
System.out.println("endByMap:" + endByMap);
HashMap<String, Object> b = new HashMap<>();
b.putAll(beginByMap);
b.put("key02", "ADA");
b.put("key03", "ADA");
b.remove("key01");
System.out.println(b);
System.out.println(beginByMap);
}
List对象
几种常见的浅拷贝:
1.直接用“=”赋值
2.遍历循环复制
3.使用List实现类的构造方法
4.使用list.addAll()方法
list的深拷贝
LIST<泛型> 泛型一定是Object类型,,实现cloneable接口并且重写clone方法。
clone方法:就是深度拷贝更多的如下
public class MyTest {
@Test
public void TestCopy() throws Exception {
List<Person> list1 = new ArrayList<Person>();
list1.add(new Person("aaa"));
list1.add(new Person("bbb"));
// 第一种方法:通过构造函数来实现,它里面调用了Arrays.copyOf(),copyOf又调用了System.arraycopy
List<Person> list2 = new ArrayList<Person>(list1);
list2.get(0).setName("ccc");
System.out.println("list1 ==>" + list1);
System.out.println("list2 ==>" + list2);
/** 此时list1 和list2 输出相同,说明是引用*/
// 第二种方法:addAll方法,它调用了System.arraycopy
// System.arraycopy:源数据-->目的数据
// Arrays.copyOf():源数据及长度,数据类型,它里面调用了System.arraycopy
List<Person> list3 = new ArrayList<Person>();
list3.addAll(list1);
list3.get(0).setName("ddd");
System.out.println("list1 ==>" + list1);
System.out.println("list3 ==>" + list3);
/** 此时list1 和list3 输出相同,说明是引用*/
// 第三种方法:实现clone来实现
List<Person> list4 = new ArrayList<Person>();
for (int i = 0; i < list1.size(); i++) {
list4.add(list1.get(i).clone());
}
list4.get(0).setName("eee");
System.out.println("list1 ==>" + list1);
System.out.println("list4 ==>" + list4);
/** 此处clone了,但要注意实现了cloneable接口只能对原始类型和String进行clone,如果是引用类型,则要进行深clone,复制内容或者对象再次clone*/
// 第四种方法:通过序列化来实现
List<Person> list5 = null;
OutputStream bo = new ByteArrayOutputStream();
ObjectOutputStream oo = new ObjectOutputStream(bo);
oo.writeObject(list1);
InputStream bi = new ByteArrayInputStream(((ByteArrayOutputStream) bo).toByteArray());
ObjectInputStream oi = new ObjectInputStream(bi);
list5 = (List<Person>) oi.readObject();
list5.get(0).setName("ggg");
System.out.println("list1 ==>" + list1);
System.out.println("list5 ==>" + list5);
/** 通过这种方法也能实现深拷贝*/
// 第五种方法: Collection.copy,它的实现过程是通过遍历然后set进入
List<Person> list6 = new ArrayList<Person>(Arrays.asList(new Person[list1.size()]));
Collections.copy(list6, list1);
list6.get(0).setName("hhh");
System.out.println("list1 ==>" + list1);
System.out.println("list6 ==>" + list6);
/** list1 和list6 输出结果是相同的*/
}
}
class Person implements Cloneable, Serializable {
private String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return this.name;
}
@Override
protected Person clone() throws CloneNotSupportedException {
return (Person) super.clone();
}
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);
return (List<T>)in.readObject();
}
}
JAVA-面向对象之对象拷贝的更多相关文章
- Java面向对象 其他对象
Java面向对象 其他对象 知识概要: (1)可变参数 (2)静态导入 (3)System (4)Runtime (5)Date Calendar (6)Math 本 ...
- Java面向对象----String对象的声明和创建
String a="abcd" 相等 String b="abcd" String a=new String("abcd") 不等于 ...
- Java面向对象_对象数组
今天学习了对象数组,写点东西总结一下.废话不多说,啥是对象数组呢? 对象数组的概念是这么讲的,对象数组就是数组里的每个元素都是类的对象,赋值时先定义对象,然后将对象直接赋给数组. 举个例子,使用对象数 ...
- Java面向对象_对象内存分析—值传递和引用传递
对象内存分析,我一直学的比较模糊,今天抽空整理一下,希望能理清. 先说一下,内存分析从何而来,这对于我们这些刚接触java的人来说都比较模糊,就从new关键字说起吧. new关键字表示创建一个对象或者 ...
- 128、Java面向对象之对象的比较
01.代码如下: package TIANPAN; class Book { private String title; private double price; public Book(Strin ...
- 127、Java面向对象之对象的比较
01.代码如下: package TIANPAN; class Book { private String title; private double price; public Book(Strin ...
- Java面向对象_对象一一对应关系和this关键字
一.打个比方,一个人有一个身份证号,一个身份证号对应一个人.一个英雄对应一把武器,一把武器对应一个英雄.生活中很多对象都存在一一对应关系,那么一一对应关系在代码中是如何实现的呢?举个例子,英雄和武器一 ...
- java面向对象 - 匿名对象
一.匿名对象 1. 创建的对象,没有显示的赋给一个变量名,即为匿名对象. 2. 匿名对象只能调用一次 二.匿名对象使用 class Phone { private int price; public ...
- 通过与C++程序对比,彻底搞清楚JAVA的对象拷贝
目录 一.背景 二.JAVA对象拷贝的实现 2.1 浅拷贝 2.2 深拷贝的实现方法一 2.3 深拷贝的实现方法二 2.3.1 C++拷贝构造函数 2.3.2 C++源码 2.3.3 JAVA通过拷贝 ...
- Java面向对象(一) 类和对象
一.软件开发进化史 摘自<从零开始学架构> 机器语言(1940年) 最早的软件开发使用的是“机器语言”,直接使用二进制码0和1来表示机器可以识别的指令和数据. 汇编语言(20世纪40年代) ...
随机推荐
- Java Socket底层实现浅析
最近在学Java的socket编程,发现Java可以很简单的通过socketAPI实现网络通信,但是我一直有个疑问,Java的socket的底层是怎么实现的? 如果没记错的话Java的底层是C和C++ ...
- MySQL建表语句生成Golang代码
1. 背景 对于后台开发新的需求时,一般会先进行各种表的设计,写各个表的建表语句 然后根据建立的表,写对应的model代码.基础的增删改查代码(基础的增删改查服务可以划入DAO(Data Access ...
- 如何干涉MySQL优化器使用hash join
GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源. GreatSQL是MySQL的国产分支版本,使用上与MySQL一致. 前言 实验 总结 前言 数据库的优化器相当于人类的大 ...
- 升级Windows 2003域控制器到Windows 2012 R2
由于Windows 2003包括R2的扩展支持在今年7月14日就会过期.如果在扩展周期结束之前没有和微软签订昂贵服务协议,那么系统将得不到任何补丁和技术支持. 我这里准备了两台测试用的机器做这个实验. ...
- Kubernetes 安全
RBAC 权限控制 对资源对象的操作都是通过 APIServer 进行的,那么集群是怎样知道我们的请求就是合法的请求呢?这个就需要了解 Kubernetes 中另外一个非常重要的知识点了:RBAC(基 ...
- 第四章:Django表单
一.HTML表单概述 Django开发的是动态Web服务,而非单纯提供静态页面.动态服务的本质在于和用户进行互动,接收用户的输入,根据输入的不同,返回不同的内容给用户.返回数据是我们服务器后端做的,而 ...
- cAdvisor容器监控规则
其他说明参考host主机监控规则:https://www.cnblogs.com/sanduzxcvbnm/p/13589848.html 在prometheus主程序目录下的rules目录下新建do ...
- My life of Honker Security Commando
红客突击队 && 红客突击分队 红客突击队,于2019年,由队长k龙联合国内多位顶尖高校研究生牵头成立.其团队从成立至今多次参加国际网络安全竞赛并取得良好成绩,积累了丰富的竞赛经验.团 ...
- Docker容器获取宿主机信息
最近在做产品授权的东西,开始宿主机为Window,程序获取机器硬件信息相对简单些,后来部署时发现各种各样的的环境问题,所有后来改用dokcer部署,docker方式获取宿主机信息时花了些时间,特此记录 ...
- 关于Linux中使用bc命令计算圆周率
Linux系统中,我们可以安装bc计算器用来计算pi值(π圆周率) 在玩的同时,这可以从某些方面反映出一个CPU的运算能力,在命令最前加上time即可 如果系统中没有bc命令,需要先进行安装:yum ...