克隆和反序列化都会破坏单例模式
浅克隆:克隆的时候是直接将内存中的二进制复制一份,克隆不会调用构造器。实现浅克隆需要是先实现Cloneable接口,其实实现Cloneable接口就像是实现序列化接口一样,仅仅是一个标识的作用。不过此时我们需要重写clone()方法。
浅克隆的时候,如果被克隆的对象的某个属性还是一个对象,那么克隆出来的对象的这个属性和被克隆的对象的这个属性将指向同一块地址,所以在修改克隆出来的对象的这个属性时,被克隆对象该属性的值也会被修改。但是克隆对象和被克隆对象的地址是不相同的。
如下图所示:
深克隆:我们可以继续上面的方法,然后把其中为对象的那个属性,也进行克隆,这样就实现了深度克隆,但是当属性的层级比较多时,这样执行起来就会很麻烦,所以使用序列化进行克隆。方法:实现Cloneable和Serializable接口,重写clone()方法。
不成熟代码:
@Override
protected Object clone() throws CloneNotSupportedException {
try {
//先将对象序列化
OutputStream out = new FileOutputStream("F://a.txt");
ObjectOutputStream oos = new ObjectOutputStream(out);
oos.writeObject(this);//序列化时,对象中属性的层级关系会自动进行处理。
oos.close();
//再将对象反序列化
InputStream in = new FileInputStream("f://a.txt");
ObjectInputStream ois = new ObjectInputStream(in);
Object clone = ois.readObject();
ois.close();
return clone;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
上述代码中使用到了盘符,和电脑的操作系统相关联,不能体现java跨平台的特性。所以代码改进如下:使用字节输入流。
@Override
protected Object clone() throws CloneNotSupportedException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
ObjectOutputStream oos = new ObjectOutputStream(out);
oos.writeObject(this);
oos.close();
//从内存中读取数据
byte[] bb = out.toByteArray();
ByteArrayInputStream in = new ByteArrayInputStream(bb);
ObjectInputStream ois = new ObjectInputStream(in);
Object clone = ois.readObject();
return clone;
} catch (Exception e) {
throw new RuntimeException(e);
}
}