基于序列化存取实现java对象深度克隆的方法详解
程序员文章站
2023-12-17 14:04:40
我们知道,在java中,将一个非原型类型类型的对象引用,赋值给另一个对象的引用之后,这两个引用就指向了同一个对象,如:复制代码 代码如下:public class deep...
我们知道,在java中,将一个非原型类型类型的对象引用,赋值给另一个对象的引用之后,这两个引用就指向了同一个对象,如:
public class deepclonetest {
private class clonetest {
private long mylong = new long(1);
}
public static void main(string args[]) {
new deepclonetest().test();
}
public void test() {
clonetest ct1 = new clonetest();
clonetest ct2 = ct1;
// to see if ct1 and ct2 are one same reference.
system.out.println("ct1: " + ct1);
system.out.println("ct2: " + ct2);
// if ct1 and ct2 point to one same object, then ct1.mylong == ct2.mylong.
system.out.println("ct1.mylong: " + ct1.mylong);
system.out.println("ct2.mylong: " + ct2.mylong);
// we change ct2's mylong
ct2.mylong = 2l;
// to see whether ct1's mylong was changed.
system.out.println("ct1.mylong: " + ct1.mylong);
system.out.println("ct2.mylong: " + ct2.mylong);
}
}
out put:
ct1: deepclonetest$clonetest@c17164
ct2: deepclonetest$clonetest@c17164
ct1.mylong: 1
ct2.mylong: 1
ct1.mylong: 2
ct2.mylong: 2
这个很easy,估计学java的都知道(不知道的是学java的么?)。
在内存中,对象的引用存放在栈中,对象的数据,存放在堆中,栈中的引用指向了堆中的对象。这里就是两个栈中的引用,指向了堆中的同一个对象,所以,当改变了 ct2 的 mylong,可以看到,ct1 的 mylong 值也随之改变,如果用图来表示,就很容易理解了:
大多时候,我们会用 java 语言的这一特性做我们想做的事情,比如,将对象的引用作为入参传入一个方法中,在方法中,对引用所指对象做相应修改。但有时,我们希望构造出一个和已经存在的对象具有完全相同的内容,但引用不同的对象,为此,可以这样做:
public class deepclonetest{
// must implements cloneable.
private class clonetest implements cloneable{
private object o = new object();
public clonetest clone() {
clonetest ct = null;
try {
ct = (clonetest)super.clone();
} catch (clonenotsupportedexception e) {
e.printstacktrace();
}
return ct;
}
}
public static void main(string args[]) {
new deepclonetest().test();
}
public void test() {
clonetest ct1 = new clonetest();
clonetest ct2 = ct1.clone();
// to see if ct1 and ct2 are one same reference.
system.out.println("ct1: " + ct1);
system.out.println("ct2: " + ct2);
// whether ct1.o == ct2.o ? yes
system.out.println("ct1.o " + ct1.o);
system.out.println("ct1.o " + ct1.o);
}
}
out put:
ct1: deepclonetest$clonetest@c17164
ct2: deepclonetest$clonetest@1fb8ee3
ct1.o java.lang.object@61de33
ct1.o java.lang.object@61de33
从输出可以看出:ct1 和 ct2 确实是两个不同的引用,所以我们想当然的认为,ct1.o 和 ct2.o 也是两个不同的对象了,但从输出可以看出并非如此!ct1.o 和 ct2.o 是同一个对象!原因在于,虽然用到了克隆,但上面只是浅度克隆,用图形来表示:
看到上面的 o 了么?其实是两个对象共享的。这就相当于,你本来有一个羊圈1,里面有一只羊,然后你又弄了一个羊圈2,在不将羊从羊圈1里牵出来的情况下,将羊也圈在了羊圈2中,你以为你有两条羊了,其实呢?大家都知道。
这就是浅度克隆的结果:如果你想让两个对象具有独立的 o,就必须再对 o 做克隆操作。可能有些人认为这没有什么,做就做呗,但想过没有,如果不止一个 o, 还有很多很多的类似 o 的东东,你都逐一去做克隆吗?显然是不太现实的。
一种解决方法是:将对象先序列化存储到流中,然后再从留中读出对象,这样就可以保证读取出来的数据和之前的对象,里面的值完全相同,就像是一个完全的拷贝。
import java.io.bytearrayinputstream;
import java.io.bytearrayoutputstream;
import java.io.ioexception;
import java.io.objectinputstream;
import java.io.objectoutputstream;
import java.io.serializable;
public class deepclonetest {
// must implements cloneable.
private class clonetest implements serializable{
private static final long serialversionuid = 1l;
private object o = new object();
public clonetest deepclone() {
clonetest ct = null;
try {
bytearrayoutputstream baos = new bytearrayoutputstream();
objectoutputstream oos = new objectoutputstream(baos);
oos.writeobject(this);
bytearrayinputstream bais = new bytearrayinputstream(baos.tobytearray());
objectinputstream ois= new objectinputstream(bais);
ct = (clonetest)ois.readobject();
} catch (ioexception e) {
e.printstacktrace();
} catch (classnotfoundexception e) {
e.printstacktrace();
}
return ct;
}
}
public static void main(string args[]) {
new deepclonetest().test();
}
public void test() {
clonetest ct1 = new clonetest();
clonetest ct2 = ct1.deepclone();
// to see if ct1 and ct2 are one same reference.
system.out.println("ct1: " + ct1);
system.out.println("ct2: " + ct2);
// whether ct1.o == ct2.o ? no
system.out.println("ct1.o " + ct1.o);
system.out.println("ct1.o " + ct1.o);
}
}
这个时候,内存中的数据就是这样的了:
复制代码 代码如下:
public class deepclonetest {
private class clonetest {
private long mylong = new long(1);
}
public static void main(string args[]) {
new deepclonetest().test();
}
public void test() {
clonetest ct1 = new clonetest();
clonetest ct2 = ct1;
// to see if ct1 and ct2 are one same reference.
system.out.println("ct1: " + ct1);
system.out.println("ct2: " + ct2);
// if ct1 and ct2 point to one same object, then ct1.mylong == ct2.mylong.
system.out.println("ct1.mylong: " + ct1.mylong);
system.out.println("ct2.mylong: " + ct2.mylong);
// we change ct2's mylong
ct2.mylong = 2l;
// to see whether ct1's mylong was changed.
system.out.println("ct1.mylong: " + ct1.mylong);
system.out.println("ct2.mylong: " + ct2.mylong);
}
}
out put:
ct1: deepclonetest$clonetest@c17164
ct2: deepclonetest$clonetest@c17164
ct1.mylong: 1
ct2.mylong: 1
ct1.mylong: 2
ct2.mylong: 2
这个很easy,估计学java的都知道(不知道的是学java的么?)。
在内存中,对象的引用存放在栈中,对象的数据,存放在堆中,栈中的引用指向了堆中的对象。这里就是两个栈中的引用,指向了堆中的同一个对象,所以,当改变了 ct2 的 mylong,可以看到,ct1 的 mylong 值也随之改变,如果用图来表示,就很容易理解了:
大多时候,我们会用 java 语言的这一特性做我们想做的事情,比如,将对象的引用作为入参传入一个方法中,在方法中,对引用所指对象做相应修改。但有时,我们希望构造出一个和已经存在的对象具有完全相同的内容,但引用不同的对象,为此,可以这样做:
复制代码 代码如下:
public class deepclonetest{
// must implements cloneable.
private class clonetest implements cloneable{
private object o = new object();
public clonetest clone() {
clonetest ct = null;
try {
ct = (clonetest)super.clone();
} catch (clonenotsupportedexception e) {
e.printstacktrace();
}
return ct;
}
}
public static void main(string args[]) {
new deepclonetest().test();
}
public void test() {
clonetest ct1 = new clonetest();
clonetest ct2 = ct1.clone();
// to see if ct1 and ct2 are one same reference.
system.out.println("ct1: " + ct1);
system.out.println("ct2: " + ct2);
// whether ct1.o == ct2.o ? yes
system.out.println("ct1.o " + ct1.o);
system.out.println("ct1.o " + ct1.o);
}
}
out put:
ct1: deepclonetest$clonetest@c17164
ct2: deepclonetest$clonetest@1fb8ee3
ct1.o java.lang.object@61de33
ct1.o java.lang.object@61de33
从输出可以看出:ct1 和 ct2 确实是两个不同的引用,所以我们想当然的认为,ct1.o 和 ct2.o 也是两个不同的对象了,但从输出可以看出并非如此!ct1.o 和 ct2.o 是同一个对象!原因在于,虽然用到了克隆,但上面只是浅度克隆,用图形来表示:
看到上面的 o 了么?其实是两个对象共享的。这就相当于,你本来有一个羊圈1,里面有一只羊,然后你又弄了一个羊圈2,在不将羊从羊圈1里牵出来的情况下,将羊也圈在了羊圈2中,你以为你有两条羊了,其实呢?大家都知道。
这就是浅度克隆的结果:如果你想让两个对象具有独立的 o,就必须再对 o 做克隆操作。可能有些人认为这没有什么,做就做呗,但想过没有,如果不止一个 o, 还有很多很多的类似 o 的东东,你都逐一去做克隆吗?显然是不太现实的。
一种解决方法是:将对象先序列化存储到流中,然后再从留中读出对象,这样就可以保证读取出来的数据和之前的对象,里面的值完全相同,就像是一个完全的拷贝。
复制代码 代码如下:
import java.io.bytearrayinputstream;
import java.io.bytearrayoutputstream;
import java.io.ioexception;
import java.io.objectinputstream;
import java.io.objectoutputstream;
import java.io.serializable;
public class deepclonetest {
// must implements cloneable.
private class clonetest implements serializable{
private static final long serialversionuid = 1l;
private object o = new object();
public clonetest deepclone() {
clonetest ct = null;
try {
bytearrayoutputstream baos = new bytearrayoutputstream();
objectoutputstream oos = new objectoutputstream(baos);
oos.writeobject(this);
bytearrayinputstream bais = new bytearrayinputstream(baos.tobytearray());
objectinputstream ois= new objectinputstream(bais);
ct = (clonetest)ois.readobject();
} catch (ioexception e) {
e.printstacktrace();
} catch (classnotfoundexception e) {
e.printstacktrace();
}
return ct;
}
}
public static void main(string args[]) {
new deepclonetest().test();
}
public void test() {
clonetest ct1 = new clonetest();
clonetest ct2 = ct1.deepclone();
// to see if ct1 and ct2 are one same reference.
system.out.println("ct1: " + ct1);
system.out.println("ct2: " + ct2);
// whether ct1.o == ct2.o ? no
system.out.println("ct1.o " + ct1.o);
system.out.println("ct1.o " + ct1.o);
}
}
这个时候,内存中的数据就是这样的了:
克隆任务完成。