Java 浅拷贝和深拷贝
一看就懂的,java深拷贝浅拷贝
1、直接赋值
/* 建立类 */
class Resume {
private String name; //姓名
private String sex; //性别
private int age; //年龄
private String experience; //工作经历 public Resume(String name, String sex, int age) {
this.name = name;
this.sex = sex;
this.age = age;
} public void setAge(int age) {
this.age = age;
}
public int getAge() {
return age;
} public void setExperience(String experience) {
this.experience = experience;
}
public String getExperience() {
return experience;
} public void displayResume() {
System.out.println("姓名:"+name+" 性别:"+sex+" 年龄:"+age);
System.out.println("工作经历:"+experience);
}
} public class MainClass {
public static void main(String[] args) {
Resume zhangsan = new Resume("zhangsan","男",24);
zhangsan.setExperience("2009-2013就读于家里蹲大学,精通JAVA,C,C++,C#等代码复制");
zhangsan.displayResume();
Resume zhangsan1 = zhangsan;
zhangsan1.setExperience("2009-2013就读于家里蹲大学,精通JAVA,C,C++,C#等");
zhangsan.displayResume();
zhangsan1.displayResume();
}
}
程序运行结果
姓名:zhangsan 性别:男 年龄:24
工作经历:2009-2013就读于家里蹲大学,精通JAVA,C,C++,C#等代码复制
姓名:zhangsan 性别:男 年龄:24
工作经历:2009-2013就读于家里蹲大学,精通JAVA,C,C++,C#等
姓名:zhangsan 性别:男 年龄:24
工作经历:2009-2013就读于家里蹲大学,精通JAVA,C,C++,C#等
在本程序中,生成了一份zhangsan的简历。之后又复制了一份简历zhangsan1,可见zhangsan1中工作经历发生变化时,zhangsan的工作经历也发生了变化。
2、浅拷贝
/* 建立类,实现Clone方法 */
class Resume implements Cloneable{
private String name; //姓名
private String sex; //性别
private int age; //年龄
private String experience; //工作经历 public Resume(String name, String sex, int age) {
this.name = name;
this.sex = sex;
this.age = age;
} public void setAge(int age) {
this.age = age;
}
public int getAge() {
return age;
} public void setExperience(String experience) {
this.experience = experience;
}
public String getExperience() {
return experience;
} public void displayResume() {
System.out.println("姓名:"+name+" 性别:"+sex+" 年龄:"+age);
System.out.println("工作经历:"+experience);
} public Object clone() {
try {
return (Resume)super.clone();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
} public class MainClass {
public static void main(String[] args) {
Resume zhangsan = new Resume("zhangsan","男",24);
zhangsan.setExperience("2009-2013就读于家里蹲大学,精通JAVA,C,C++,C#等代码拷贝和粘贴");
zhangsan.displayResume();
Resume zhangsan1 = (Resume)zhangsan.clone();
zhangsan1.setAge(23);
zhangsan1.displayResume();
Resume zhangsan2 = (Resume)zhangsan.clone();
zhangsan2.setExperience("2009-2013就读于家里蹲大学,精通JAVA,C,C++,C#等代码");
zhangsan2.displayResume();
zhangsan.displayResume();
}
}
程序运行结果
姓名:zhangsan 性别:男 年龄:24
工作经历:2009-2013就读于家里蹲大学,精通JAVA,C,C++,C#等代码拷贝和粘贴
姓名:zhangsan 性别:男 年龄:23
工作经历:2009-2013就读于家里蹲大学,精通JAVA,C,C++,C#等代码拷贝和粘贴
姓名:zhangsan 性别:男 年龄:24
工作经历:2009-2013就读于家里蹲大学,精通JAVA,C,C++,C#等代码
姓名:zhangsan 性别:男 年龄:24
工作经历:2009-2013就读于家里蹲大学,精通JAVA,C,C++,C#等代码拷贝和粘贴
class Experience {
private String educationBackground;
private String skills;
public void setExperience(String educationBackground, String skills) {
// TODO Auto-generated constructor stub
this.educationBackground = educationBackground;
this.skills = skills;
}
public String toString() {
return educationBackground + skills;
}
}
/* 建立类,实现Clone方法 */
class Resume implements Cloneable{
private String name; //姓名
private String sex; //性别
private int age; //年龄
private Experience experience; //工作经历
public Resume(String name, String sex, int age) {
this.name = name;
this.sex = sex;
this.age = age;
this.experience = new Experience();
}
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return age;
}
public Experience getExperience() {
return experience;
}
public void setExperience(String educationBackground, String skills) {
experience.setExperience(educationBackground, skills);
}
public void displayResume() {
System.out.println("姓名:"+name+" 性别:"+sex+" 年龄:"+age);
System.out.println("工作经历:"+experience.toString());
}
public Object clone() {
try {
return (Resume)super.clone();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
public class MainClass {
public static void main(String[] args) {
Resume zhangsan = new Resume("zhangsan","男",24);
zhangsan.setExperience("2009-2013就读于家里蹲大学","精通JAVA,C,C++,C#等代码拷贝和粘贴");
zhangsan.displayResume();
Resume zhangsan2 = (Resume)zhangsan.clone();
zhangsan2.setExperience("2009-2013就读于家里蹲大学","精通JAVA,C,C++,C#等");
zhangsan2.displayResume();
zhangsan.displayResume();
zhangsan2.displayResume();
}
}
程序运行结果:
姓名:zhangsan 性别:男 年龄:24
工作经历:2009-2013就读于家里蹲大学精通JAVA,C,C++,C#等代码拷贝和粘贴
姓名:zhangsan 性别:男 年龄:24
工作经历:2009-2013就读于家里蹲大学精通JAVA,C,C++,C#等
姓名:zhangsan 性别:男 年龄:24
工作经历:2009-2013就读于家里蹲大学精通JAVA,C,C++,C#等
姓名:zhangsan 性别:男 年龄:24
工作经历:2009-2013就读于家里蹲大学精通JAVA,C,C++,C#等
我们看一下上面两段程序差异在哪儿,第一段程序的工作经历是作为Resume类的一个普通的成员变量,也就是值属性。而后面一段程序中,工作经历Experience是一个类。结合上面程序的运行结果,我们再来理解“如果该字段是引用类型的话,则复制引用但不复制引用的对象。因此,原始对象及其副本引用同一个对象。”其实也就是说,zhangsan和zhangsan2里面的Experience类指向的是同一个对象嘛!那不管是zhangsan里面的Experience变化,还是zhangsan2里面的Experience变化都会影响另外一个啊。
3、深拷贝
其实出现问题的关键就在于clone()方法上,我们知道该clone()方法是使用Object类的clone()方法,但是该方法存在一个缺陷,它并不会将对象的所有属性全部拷贝过来,而是有选择性的拷贝,基本规则如下:
1、 基本类型
如果变量是基本很类型,则拷贝其值,比如int、float等。
2、 对象
如果变量是一个实例对象,则拷贝其地址引用,也就是说此时新对象与原来对象是公用该实例变量。
3、 String字符串
若变量为String字符串,则拷贝其地址引用。但是在修改时,它会从字符串池中重新生成一个新的字符串,原有紫都城对象保持不变。
基于上面上面的规则,我们很容易发现问题的所在,他们三者公用一个对象,张三修改了该邮件内容,则李四和王五也会修改,所以才会出现上面的情况。对于这种情况我们还是可以解决的,只需要在clone()方法里面新建一个对象,然后张三引用该对象即可:
rotected Person clone() {
Person person = null;
try {
person = (Person) super.clone();
person.setEmail(new Email(person.getEmail().getObject(),person.getEmail().getContent()));
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return person;
}
所以:浅拷贝只是Java提供的一种简单的拷贝机制,不便于直接使用。
对于上面的解决方案还是存在一个问题,若我们系统中存在大量的对象是通过拷贝生成的,如果我们每一个类都写一个clone()方法,并将还需要进行深拷贝,新建大量的对象,这个工程是非常大的,这里我们可以利用序列化来实现对象的拷贝。
如何利用序列化来完成对象的拷贝呢?在内存中通过字节流的拷贝是比较容易实现的。把母对象写入到一个字节流中,再从字节流中将其读出来,这样就可以创建一个新的对象了,并且该新对象与母对象之间并不存在引用共享的问题,真正实现对象的深拷贝。
public class CloneUtils {
@SuppressWarnings("unchecked")
public static <T extends Serializable> T clone(T obj){
T cloneObj = null;
try {
//写入字节流
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream obs = new ObjectOutputStream(out);
obs.writeObject(obj);
obs.close();
//分配内存,写入原始对象,生成新对象
ByteArrayInputStream ios = new ByteArrayInputStream(out.toByteArray());
ObjectInputStream ois = new ObjectInputStream(ios);
//返回生成的新对象
cloneObj = (T) ois.readObject();
ois.close();
} catch (Exception e) {
e.printStackTrace();
}
return cloneObj;
}
}
使用该工具类的对象必须要实现Serializable接口,否则是没有办法实现克隆的。
public class Person implements Serializable{
private static final long serialVersionUID = 2631590509760908280L;
..................
//去除clone()方法
}
public class Email implements Serializable{
private static final long serialVersionUID = 1267293988171991494L;
....................
}
所以使用该工具类的对象只要实现Serializable接口就可实现对象的克隆,无须继承Cloneable接口实现clone()方法。
public class Client {
public static void main(String[] args) {
//写封邮件
Email email = new Email("请参加会议","请与今天12:30到二会议室参加会议...");
Person person1 = new Person("张三",email);
Person person2 = CloneUtils.clone(person1);
person2.setName("李四");
Person person3 = CloneUtils.clone(person1);
person3.setName("王五");
person1.getEmail().setContent("请与今天12:00到二会议室参加会议...");
System.out.println(person1.getName() + "的邮件内容是:" + person1.getEmail().getContent());
System.out.println(person2.getName() + "的邮件内容是:" + person2.getEmail().getContent());
System.out.println(person3.getName() + "的邮件内容是:" + person3.getEmail().getContent());
}
}
-------------------
Output:
张三的邮件内容是:请与今天12:00到二会议室参加会议...
李四的邮件内容是:请与今天12:30到二会议室参加会议...
王五的邮件内容是:请与今天12:30到二会议室参加会议...
Java 浅拷贝和深拷贝的更多相关文章
- Java 浅拷贝、深拷贝,你知多少?
这是今天我们在技术群里面讨论的一个知识点,讨论的相当激烈,由于对这一块使用的比较少,所以对这一块多少有些盲区.这篇文章总结了所讨论的内容,希望这篇文章对你有所帮助. 在 Java 开发中,对象拷贝或者 ...
- Java 浅拷贝和深拷贝的理解和实现方式
Java中的对象拷贝(Object Copy)指的是将一个对象的所有属性(成员变量)拷贝到另一个有着相同类类型的对象中去.举例说明:比如,对象A和对象B都属于类S,具有属性a和b.那么对对象A进行拷贝 ...
- java 浅拷贝和深拷贝 对象克隆clone
分一下几点讨论: 为什么要克隆? 如何实现克隆 浅克隆和深克隆 解决多层克隆问题 总结 一:为什么要克隆? 大家先思考一个问题,为什么需要克隆对象?直接new一个对象不行吗? 答案是:克隆的对象可能包 ...
- 浅谈java浅拷贝和深拷贝
前言:深拷贝和浅拷贝的区别是什么? 浅拷贝:被复制的对象的所有变量都含有原来对象相同的值,而所有的对其他对象的引用仍然指向原来的对象.换言之, 浅拷贝仅仅复制所考虑的对象,而不复制它所引用的对象.深拷 ...
- 【转】Java 浅拷贝和深拷贝的理解和实现方式
Java中的对象拷贝(Object Copy)指的是将一个对象的所有属性(成员变量)拷贝到另一个有着相同类类型的对象中去.举例说明:比如,对象A和对象B都属于类S,具有属性a和b.那么对对象A进行拷贝 ...
- Java 浅拷贝,深拷贝
从Java 强引用.软引用,弱引用http://blog.csdn.net/jltxgcy/article/details/35558465一文中,我们看到把一个对象赋值给另一个对象,本质上 ...
- java浅拷贝和深拷贝(基础也是很重要的)
对象的copy你兴许只是懵懂,或者是并没在意,来了解下吧. 对于的github基础代码https://github.com/chywx/JavaSE 最近学习c++,跟java很是相像,在慕课网学习c ...
- Java浅拷贝与深拷贝(思维导图)
图1 拷贝思维导图(点击查看图片) 1,拷贝 有两个相同属性的对象A和B,A拥有初始化值,将其值拷贝到B中,使得B拥有与A“相同”数据的属性!注意这里的相同我有加双引号! 相同可能表示这么几个意思:① ...
- java浅拷贝和深拷贝
转:http://blog.csdn.net/u014727260/article/details/55003402 实现clone的2点: 1,clone方法是Object类的一个方法,所以任何一个 ...
随机推荐
- History of program (language).
1 编程语言的发展历程及未来趋势 1.1 编程语言的发展 · 1946 Plankalkul Konrad Zuse,一位德国工程师,他躲藏在巴伐利亚附近的阿尔卑斯山上时,独立开发了Plank ...
- 在ASP.NET Core中使用brotli压缩
Brotli是一种全新的数据格式,可以提供比Zopfli高20-26%的压缩比.据谷歌研究,Brotli压缩速度同zlib的Deflate实现大致相同,而在Canterbury语料库上的压缩密度比LZ ...
- [Swift]LeetCode132. 分割回文串 II | Palindrome Partitioning II
Given a string s, partition s such that every substring of the partition is a palindrome. Return the ...
- [Swift]LeetCode553. 最优除法 | Optimal Division
Given a list of positive integers, the adjacent integers will perform the float division. For exampl ...
- [Swift]LeetCode775. 全局倒置与局部倒置 | Global and Local Inversions
We have some permutation Aof [0, 1, ..., N - 1], where N is the length of A. The number of (global) ...
- [Swift]LeetCode869. 重新排序得到 2 的幂 | Reordered Power of 2
Starting with a positive integer N, we reorder the digits in any order (including the original order ...
- iOS学习——#define、const、typedef的区别
在iOS开发中经常遇到一些字段和类型的定义,例如配置生产和测试不同环境的参数等,这时候经常用到#define.const以及typedef.那么它们之间有什么区别呢?我们接下来一个一个具体了解下. 一 ...
- Javascript 链式操作以及流程控制
春节过后,感觉过年吃的油腻的食品转化的脂肪都长到 脑子去了. 根本转不动啊 上班第一天 实在是写不动代码了, 顺手打开多天为看的 收件箱,查看查看邮件,看看春节期间 风云变幻的前端圈又有哪些大事发生. ...
- 知识小罐头03(idea+maven+部署war包到tomcat 上)
自学的的小伙伴第一就要用maven!自学的的小伙伴第一就要用maven!自学的的小伙伴第一就要用maven! 重要的事说三遍!maven本质上,其实就是一种目录的格式,没有什么特别的地方!而且,你可以 ...
- ThreadPoolExecutor系列三——ThreadPoolExecutor 源码解析
ThreadPoolExecutor 源码解析 本文系作者原创,转载请注明出处:http://www.cnblogs.com/further-further-further/p/7681826.htm ...