Java对象的深复制与浅复制
来源:互联网 发布:手机t淘宝 编辑:程序博客网 时间:2024/06/02 17:57
深复制与浅复制:
浅复制(shallow clone):被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。换言之,浅复制仅仅复制所考虑的对象,而不复制它所引用的对象。
深复制(deep clone):
被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深复制把要复制的对象所引用的对象都复制了一遍。
所有的基本(primitive)类型数据,无论是浅克隆还是深克隆,都会进行原值克隆。毕竟他们都不是对象,不是存储在堆中。注意:基本数据类型并不包括他们对应的包装类。
下面是一个实现深复制的例子:
创建Employer类,实现Cloneable接口:
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(); }}
创建Employee类,实现Cloneable接口,并改写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; }}
这样,在客户端拷贝的两个Employee对象的Employer就互不影响了,但是由于String对象不能调用clone方法,所以String类型的属性并没有被克隆,所以这种克隆方法还是有问题的,想真正实现深克隆,必须使用序列化的方式:
Employer employer = new Employer();employer.setUsername("Thinking in Java");Employee employee = new Employee();employee.setUsername("Bruce Eckel");employee.setEmployer(employer);// employee2由employee深复制得到Employee employee2 = (Employee) employee.clone();//// 这样两个employee各自保存了两个employeremployee2.getEmployer().setUsername("Bruce Eckel");System.out.println(employee.getEmployer().getUsername());System.out.println(employee2.getEmployer().getUsername());System.out.println(employee.getUsername());System.out.println(employee2.getUsername());System.out.println(employee.getUsername() == employee2.getUsername());输出:Thinking in JavaBruce EckelBruce EckelBruce Eckeltrue
使用序列化来进行对象的深复制:
序列化即是把对象写到流里面的过程;反序列化即是把对象从流中读取出来的过程。写在流里的是对象的一个拷贝,而原来的对象仍然在JVM里面。以下是实现过程描述:
前提是对象以及对象内部所有用到的对象都是可序列化的,否则就需要考虑把那些不可序列化的对象标记为transient,从而把它排除到复制范围之外。然后使对象实现Serializable接口。把对象写入到一个流里(不用依赖于文件,直接暂存在内存中即可),在从流里读取出来,便得到了一个深复制的对象。
下面是使用序列化实现深复制的例子:
创建Employer2类实现序列化接口:
class Employer2 implements Serializable{ private static final long serialVersionUID = 1L; private String name; public String getName() { return name; } public void setName(String name) { this.name = name; }}
创建Employee2类实现序列化接口,并通过序列化编写深复制的方法:
class Employee2 implements Serializable{ private static final long serialVersionUID = 3969438177161438988L; private String name; private Employer2 employer; public String getName() { return name; } public void setName(String name) { this.name = name; } public Employer2 getEmployer() { return employer; } public void setEmployer(Employer2 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(); }}
在main方法中使用序列化深复制对象:
public static void main(String[] args) throws IOException, ClassNotFoundException {Employer2 employer = new Employer2();employer.setName("Thinking in Java");Employee2 employee = new Employee2();employee.setName("Bruce Eckel");employee.setEmployer(employer);// 通过深复制创建employee2Employee2 employee2 = (Employee2) employee.deepCopy();employee2.getEmployer().setName("Jason");System.out.println(employee.getEmployer().getName());System.out.println(employee2.getEmployer().getName());System.out.println(employee.getName());System.out.println(employee2.getName());System.out.println(employee.getName() == employee2.getName());}
输出:
Thinking in JavaJasonBruce EckelBruce Eckelfalse
也可以写一个工具类来实现对象的深复制:
public class BeanUtil {@SuppressWarnings("unchecked")public static <T> T cloneTo(T src) throws RuntimeException {ByteArrayOutputStream memoryBuffer = new ByteArrayOutputStream();ObjectOutputStream out = null;ObjectInputStream in = null;T dist = null;try {out = new ObjectOutputStream(memoryBuffer);out.writeObject(src);out.flush();in = new ObjectInputStream(new ByteArrayInputStream(memoryBuffer.toByteArray()));dist = (T) in.readObject();} catch (Exception e) {throw new RuntimeException(e);} finally {if (out != null)try {out.close();out = null;} catch (IOException e) {throw new RuntimeException(e);}if (in != null)try {in.close();in = null;} catch (IOException e) {throw new RuntimeException(e);}}return dist;}}
测试:
Employer2 employer = new Employer2();employer.setName("Thinking in Java");Employee2 employee = new Employee2();employee.setName("Bruce Eckel");employee.setEmployer(employer);// 通过深复制创建employee2Employee2 employee2 = (Employee2) BeanUtil.cloneTo(employee);employee2.getEmployer().setName("Bruce Eckel");System.out.println(employee.getEmployer().getName());System.out.println(employee2.getEmployer().getName());System.out.println(employee.getName());System.out.println(employee2.getName());System.out.println(employee.getName() == employee2.getName());
输出:
Thinking in JavaBruce EckelBruce EckelBruce Eckelfalse
关于Serializable接口的类中的serialVersionUID:
serialVersionUID是long类型的。在Eclipse中有两种生成方式:默认的是1L:
private static final long serialVersionUID = 1L;另外一个则是根据类名、接口名、成员方法以及属性等生成一个64位的哈希字段:
private static final long serialVersionUID = 3969438177161438988L;
serialVersionUID主要是为了解决对象反序列化的兼容性问题。
如果没有提供serialVersionUID,对象序列化后存到硬盘上之后,再增加或减少类的filed。这样,当反序列化时,就会出现Exception,造成不兼容问题。但当serialVersionUID相同时,它就会将不一样的field以type的缺省值反序列化。这样就可以避开不兼容问题了。
Marker Interface:
标识接口,没有定义任何的方法,如Cloneable和Serializable接口。参考:
Java基础笔记 – 对象的深复制与浅复制 实现Cloneable接口实现深复制 序列化实现深复制
Java对象克隆(Clone)及Cloneable接口、Serializable接口的深入探讨
0 0
- Java对象深复制与浅复制
- Java对象的深复制与浅复制
- 对象的浅复制与深复制
- java对象的复制,浅复制、深复制
- Java对象的浅层复制与深层复制
- 对象浅复制与深复制
- PHP对象的浅复制与深复制
- Python对象的深复制与浅复制
- PHP对象的深复制与浅复制
- 面向对象的深复制与浅复制
- Java对象深复制、浅复制
- Java对象的深复制和浅复制
- Java对象的深复制和浅复制
- Java对象的深复制和浅复制
- Java对象的深复制和浅复制
- Java对象的深复制和浅复制
- 细说java对象的浅复制和深复制
- Java对象的深复制和浅复制
- leetcode Reverse Linked List
- 【jsp/servlet】jsp数据交互(二)
- MPEGTS码率控制的研究
- Linux时间子系统之七:定时器的应用--msleep(),hrtimer_nanosleep()
- About java8
- Java对象的深复制与浅复制
- Linux时间子系统之八:动态时钟框架(CONFIG_NO_HZ、tickless)
- 洛谷 P1113 杂务
- JS获取浏览器中的各种宽高值
- 异常——UnsupportedOperationException的使用
- MySQL日期函数大全
- 中心桥电站调速器发货
- 365. Water and Jug Problem
- ANDROID STUDIO系列教程三快捷键