深拷贝vs浅拷贝
来源:互联网 发布:淘宝的电脑主机靠谱吗 编辑:程序博客网 时间:2024/05/16 18:09
- 浅拷贝(shallow clone):拷贝的对象的成员中的基本类型(8大基本类型,见Java之基本类型和引用类型、引用传递和值传递、“==”和equals())与原来的的值相同,但是物理地址与原来不同,及拷贝成员与原成员“equals”为True而“==”为False。但是对象类型的成员则不同,我们知道对象名是引用,而拷贝成员和原成员引用地址相同。
- 深拷贝(deep clone):深拷贝对基本类型的成员与浅拷贝相同,对对象类型的成员与深拷贝不同。它首相将对象类型的成员所引用的对象拷贝到新的地址,然后将该新的对象的的引用赋值给对应的对象类型的成员。
1.String是引用传递
public class stringTest {public static void changeStr(String str){str = str+" has been changed";此str与之前的str只是名字不同,但是值相同,指向地址也相同;但是string类不可改变,所以此str只能指向新的一块内存了,而之前的str仍指向原来的内存。System.out.println(str);//str has been changed}public static void changeData(StringBuffer strBuf) { strBuf = strBuf.append(" has been changed"); System.out.println(strBuf);//Hello has been changed}public static void main(String[] args) {String str = "myStr";//new String("myStr") //此str指向内存地址AchangeStr(str);System.out.println(str);StringBuffer sb = new StringBuffer("Hello "); changeData(sb); System.out.println(sb); //输出是 //myStr has been changed //myStr //Hello has been changed //Hello has been changed}}
String与StringBuffer的区别
简单地说,就是一个变量和常量的关系。StringBuffer对象的内容可以修改;而String对象一旦产生后就不可以被修改,重新赋值其实是两个对象。
StringBuffer的内部实现方式和String不同,StringBuffer在进行字符串处理时,不生成新的对象,在内存使用上要优于String类。所以在实际使用时,如果经常需要对一个字符串进行修改,例如插入、删除等操作,使用StringBuffer要更加适合一些。
String:在String类中没有用来改变已有字符串中的某个字符的方法,由于不能改变一个java字符串中的某个单独字符,所以在JDK文档中称String类的对象是不可改变的。然而,不可改变的字符串具有一个很大的优点:编译器可以把字符串设为共享的。
StringBuffer:StringBuffer类属于一种辅助类,可预先分配指定长度的内存块建立一个字符串缓冲区。这样使用StringBuffer类的append方法追加字符 比 String使用 + 操作符添加字符 到 一个已经存在的字符串后面有效率得多。因为使用 + 操作符每一次将字符添加到一个字符串中去时,字符串对象都需要寻找一个新的内存空间来容纳更大的字符串,这无凝是一个非常消耗时间的操作。添加多个字符也就意味着要一次又一次的对字符串重新分配内存。使用StringBuffer类就避免了这个问题。
StringBuffer是线程安全的,在多线程程序中也可以很方便的进行使用,但是程序的执行效率相对来说就要稍微慢一些
简单地说,就是一个变量和常量的关系。StringBuffer对象的内容可以修改;而String对象一旦产生后就不可以被修改,重新赋值其实是两个对象。
StringBuffer的内部实现方式和String不同,StringBuffer在进行字符串处理时,不生成新的对象,在内存使用上要优于String类。所以在实际使用时,如果经常需要对一个字符串进行修改,例如插入、删除等操作,使用StringBuffer要更加适合一些。
String:在String类中没有用来改变已有字符串中的某个字符的方法,由于不能改变一个java字符串中的某个单独字符,所以在JDK文档中称String类的对象是不可改变的。然而,不可改变的字符串具有一个很大的优点:编译器可以把字符串设为共享的。
StringBuffer:StringBuffer类属于一种辅助类,可预先分配指定长度的内存块建立一个字符串缓冲区。这样使用StringBuffer类的append方法追加字符 比 String使用 + 操作符添加字符 到 一个已经存在的字符串后面有效率得多。因为使用 + 操作符每一次将字符添加到一个字符串中去时,字符串对象都需要寻找一个新的内存空间来容纳更大的字符串,这无凝是一个非常消耗时间的操作。添加多个字符也就意味着要一次又一次的对字符串重新分配内存。使用StringBuffer类就避免了这个问题。
StringBuffer是线程安全的,在多线程程序中也可以很方便的进行使用,但是程序的执行效率相对来说就要稍微慢一些
这也解释了下面是一个List的例子的输出为什么是这样的。可以看到当加入的元素是字符串时,将字符串拷贝到List里,所以List里的元素与原来的字符串不相等;当加入的元素是一个自自定义的类对象时,加入的是对象名,由于对象名是对象引用,故实际上加入List的是对象的引用。
public static void main(String[] args) { List<String> myList = new ArrayList<String>(); String b = "aa"; myList.add(b); b = "cc"; //下面的两个输出说明List列表添加字符串元素时是吧字符串的内容复制到List列表中,而不是字符串的引用 System.out.println(myList.get(0));//"aa" System.out.println(myList.get(0)==b);//false class Person { //String name; int age; } List<Person> myList2 = new ArrayList<Person>(); Person p = new Person(); //p.name = "name_1"; p.age = 10; myList2.add(p); //p.name = "name_2"; p.age = 0; //System.out.println(myList2.get(0).name);//name_2,说明List列表的元素是原Person对象的同一个引用 System.out.println(myList2.get(0).age);//0 System.out.println(myList2.get(0)==p);//true,List列表添加Person对象时,添加的这是该对象的引用 List<String> oldList = new ArrayList<String>(); oldList.add("0"); oldList.add("2"); oldList.add("3"); List<String> newList = new ArrayList<String>(oldList);//复制了所有的字符串元素 newList.set(1, "1+2"); System.out.println(oldList.get(1));//2 System.out.println(newList.get(1));//1+2 }
2.实现Cloneable接口实现深拷贝
首先介绍一下类之间的关系。如下图所示,有两个类Employee和Employer,其中Employee类有一个成员是对象类型Employer。深拷贝Employee的一个对象,就是同时将这两个相关联的对象拷贝。设深拷贝得到的新对象为EmployeeObj2和EmployerObj2,原对象时EmployeeObj和EmployerObj,则对EmployeeObj2.employer(即EmployerObj2)的修改操作不会影响EmployeeObj.employer(即EmployerObj),反过来也成立,也就是拷贝对象和原对象完全脱离关系。
通过实现Cloneable接口实现深拷贝的Java代码如下:
public class Employ {class Employer implements Cloneable{ private String username; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } @Override public Object clone() throws CloneNotSupportedException { return super.clone(); }}class Employee implements Cloneable{ private String username; private Employer employer; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public Employer getEmployer() { return employer; } public void setEmployer(Employer employer) { this.employer = employer; } @Override public Object clone() throws CloneNotSupportedException { //克隆Employee对象并手动的进一步克隆Employee对象中包含的Employer对象 Employee employee = (Employee)super.clone(); employee.setEmployer((Employer) employee.getEmployer().clone()); return employee; }}public static void main(String[] args) throws CloneNotSupportedException {Employ employ = new Employ(); Employer employer = employ.new Employer(); employer.setUsername("Jim"); Employee employee = employ.new Employee(); employee.setUsername("Linda"); employee.setEmployer(employer); //employee2由employee深复制得到 Employee employee2 = (Employee) employee.clone(); //这样两个employee各自保存了两个employer employee2.getEmployer().setUsername("Jack"); System.out.println(employee.getEmployer().getUsername()); System.out.println(employee2.getEmployer().getUsername());}}如果此对象的类不能实现接口 Cloneable,则会抛出 CloneNotSupportedException。故要覆盖clone方法必须实现Cloneable接口。
3.序列化实现深拷贝
Java 序列化技术可以使你将一个对象的状态写入一个Byte 流里,并且可以从其它地方把该Byte 流里的数据读出来,重新构造一个相同的对象。
对象序列化包括如下步骤:
(a)创建一个对象输出流,它可以包装一个其他类型的目标输出流,如文件输出流;
(b)通过对象输出流的writeObject()方法写对象。
对象反序列化的步骤如下:
(a)创建一个对象输入流,它可以包装一个其他类型的源输入流,如文件输入流;
(b)通过对象输入流的readObject()方法读取对象。
(a)创建一个对象输出流,它可以包装一个其他类型的目标输出流,如文件输出流;
(b)通过对象输出流的writeObject()方法写对象。
对象反序列化的步骤如下:
(a)创建一个对象输入流,它可以包装一个其他类型的源输入流,如文件输入流;
(b)通过对象输入流的readObject()方法读取对象。
public class EmploySeriable {static class Employer implements Serializable{private static final long serialVersionUID = 1L; private String name; public String getName() { return name; } public void setName(String name) { this.name = name; }}static class Employee implements Serializable{ /** * */private static final long serialVersionUID = 1L; private String name; private Employer employer; public String getName() { return name; } public void setName(String name) { this.name = name; } public Employer getEmployer() { return employer; } public void setEmployer(Employer employer) { this.employer = employer; } /** * 实现深复制的方法 */ public Object deepCopy() throws IOException, ClassNotFoundException{ //字节数组输出流,暂存到内存中 ByteArrayOutputStream bos = new ByteArrayOutputStream(); //序列化 ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(this); ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); //反序列化 return ois.readObject(); }}public static void main(String[] args) throws IOException, ClassNotFoundException { Employer employer = new Employer(); employer.setName("Linda"); Employee employee = new Employee(); employee.setName("jim"); employee.setEmployer(employer); //通过深复制创建employee2 Employee employee2 = (Employee) employee.deepCopy(); employee2.getEmployer().setName("jack"); System.out.println(employee.getEmployer().getName());//Linda System.out.println(employee2.getEmployer().getName());//jack}}
4.附带一个字符串的性质总结
<span style="font-size:14px;font-weight: normal;">public class javaString {private static void test01(){String s0 = "kvill";String s1 = "kvill";String s2 = "kv" + "ill";System.out.println(s0 == s1);// trueSystem.out.println(s0 == s2);// true}private static void test02(){String s0 = "kvill";String s1 = new String("kvill");String s2 = "kv" + new String("ill");System.out.println(s0 == s1);// falseSystem.out.println(s0 == s2);// falseSystem.out.println(s1 == s2);// false}private static void test03(){String s0 = "kvill";String s1 = new String("kvill");String s2 = new String("kvill");System.out.println(s0 == s1);// falses1.intern();s2 = s2.intern();System.out.println(s0 == s1);// falseSystem.out.println(s0 == s1.intern());// trueSystem.out.println(s0 == s2);// true}private static void test04(){String s1 = new String("kvill");String s2 = s1.intern();String s3 = "kvill";System.out.println(s1 == s1.intern());// falseSystem.out.println(s1 + " " + s2);// kvill kvillSystem.out.println(s2 == s1.intern());// trueSystem.out.println(s2 == s3);// true}private static void test05(){String s0 = "kvill";String s1 = new String("kvill");String s2 = s1;System.out.println(s0.equals(s1));// trueSystem.out.println(s0 == s1);// falseSystem.out.println(s2.equals(s1));// trueSystem.out.println(s2 == s1);// true}private static void test06(){String str = "kv" + "ill" + " " + "ans";StringBuffer strBuf = new StringBuffer();strBuf.append("kv").append("ill").append(" ").append("ans");System.out.println(str + " : " + strBuf);}public static void main(String args[]){test01();test02();test03();test04();test05();test06();}}</span>
- 对象的深复制与浅复制 实现Cloneable接口实现深复制 序列化实现深复制
- http://www.itzhai.com/java-based-notebook-the-object-of-deep-and-shallow-copy-copy-copy-implement-the-cloneable-interface-serializing-deep-deep-copy.html#Object的clone方法的说明:
一篇好文 - Java如何复制对象
http://blog.csdn.net/tounaobun/article/details/8491392
原因是浅复制只是复制了addr变量的引用,并没有真正的开辟另一块空间,将值复制后再将引用返回给新对象。所以,为了达到真正的复制对象,而不是纯粹引用复制。我们需要将Address类可复制化,并且修改clone方法。
首先,如果此对象的类不能实现接口 Cloneable,则会抛出 CloneNotSupportedException。故要覆盖clone方法必须实现Cloneable接口。
标识接口,没有定义任何的方法,如Cloneable和Serializable接口。 - 一些不靠谱的java.util.List深复制方法
http://will-turner.iteye.com/blog/1478194 - 如何DEEP COPY(深拷貝) ARRAYLIST
http://www.ntex.tw/wordpress/538.html - 精简深拷贝ArrayList实例
http://gghhgame51333.blog.51cto.com/138362/289383 - Java序列化和克隆
http://developer.51cto.com/art/201103/247236.htm
1 0
- 浅拷贝 vs 深拷贝
- 深拷贝vs浅拷贝
- C++之深拷贝VS浅拷贝
- 【JS】深拷贝 vs 浅拷贝
- java浅拷贝VS深拷贝
- 拷贝构造函数(深拷贝vs浅拷贝)
- 浅拷贝和深拷贝(shallow copy VS deep copy )
- Python —— 深拷贝 VS 浅拷贝
- 深拷贝&&浅拷贝
- 深拷贝||浅拷贝
- 浅拷贝,深拷贝
- 浅拷贝,深拷贝
- 深拷贝,浅拷贝
- 浅拷贝 深拷贝
- 浅拷贝.深拷贝
- 浅拷贝 深拷贝
- 深拷贝,浅拷贝
- 深拷贝、浅拷贝
- nyoj-85-有趣的数
- 走过2014,迈向2015
- thinkphp 中 session 跨域 问题
- Codeforces Round #289 (Div. 2, ACM ICPC Rules)
- 【大街推荐】给明年依然年轻的我们:欲望、外界、标签、时间、人生目标、现实、后悔、和经历
- 深拷贝vs浅拷贝
- 当数据结构遇到编程语言——数组
- 国外奶茶店装修图效果图大全小奶茶店装修图风格设计
- NTU-Coursera机器学习:多類別分类和非线性转换
- c++ 模板的一些
- LeetCode:Remove Duplicates from Sorted Array II
- OpenGL基础图形编程
- eclipse导出jar文件再将它转换成exe可执行文件详解
- [暖手][学习阶段-各路杂题][HDU-1163]Eddy's digital Roots