Sometimes you need to clone objects, and sometimes you can't
use their clone method, and sometimes serialization provides an
alternative. Here's an explanation of when you might need this
exotic and expensive technique, and how you can use it.

When do you need to clone?

The commonest time that you need to clone an object is when it
is a parameter or return value of one of your public methods.
If it is a parameter that you save somewhere, then you don't
want the caller to be able to modify it later. So you save a
copy of the object. Likewise, if you are returning an object
that is part of your class's internal state, you need to return
a copy instead so that callers can't accidentally or
deliberately change that internal state.

A second case when you might want to clone is when you need to
modify an object, but you don't know who else might have a
reference to it. So you make a copy and modify that.

[In both cases, if the object in question is of one of your
classes, you should ask yourself if it couldn't be
href="http://en.wikipedia.org/wiki/Immutable">immutable
instead. Immutability has many advantages regarding
performance, security, and thread-safety.]

Can't I just use Object.clone()?

Happily, java.lang.Object defines a
href="http://java.sun.com/javase/6/docs/api/java/lang/Object.html#clone()">clone()
method whose intent is exactly to produce a copy of the object
on which it is called. Unhappily, this method, which dates from
the very earliest days of the Java platform, has some design
flaws.

The first problem is that the method is protected. The idea,
presumably, is that subclasses have to explicitly agree to be
cloneable by overriding this protected method with a public
method. The various Collections classes all do this, for
example. (
href="http://java.sun.com/javase/6/docs/api/java/util/ArrayList.html#clone()">ArrayList,

href="http://java.sun.com/javase/6/docs/api/java/util/TreeSet.html#clone()">TreeSet,

href="http://java.sun.com/javase/6/docs/api/java/util/IdentityHashMap.html#clone()">IdentityHashMap,
etc.) The subclass also has to implement
href="http://java.sun.com/javase/6/docs/api/java/lang/Cloneable.html">Cloneable
for the default cloning mechanism in Object.clone() to work.

If you have an object that you know has a public clone()
method, but you don't know the type of the object at compile-time,
you are stuck. Say x is declared as an Object. You can't just
call x.clone(), because Object.clone() is protected. If Cloneable
defined a public clone() method, then you could use ((Cloneable)
x).clone(). But it doesn't. So you either have to enumerate all the classes that you think x could be...

Object copy;
if(x instanceofArrayList)
    copy =((ArrayList<?>) x).clone();
elseif(x instanceofIdentityHashMap)
    copy =((IdentityHashMap<?,?>) x).clone();
else
    ...

...or you have to resort to reflection...

Object copy;
try{
    Method clone = x.getClass().getMethod("clone");
    copy = clone.invoke(x);
}catch(Exception e){
    ...what?...
}

Both solutions are pretty nasty.

A second potential problem is that the default behaviour of
Object.clone() is to make a shallow copy of the object.
Most system classes that provide a clone() method do this too.
A shallow copy means that the copy object itself is different,
but if the original object referenced other objects, the copy
will reference those same objects, and not a copy of them. For
example, what does the following code print?

    HashMap<String,List<String>>cities=newHashMap<String,List<String>>();
    cities.put("France",Arrays.asList("Paris","Grenoble"));
    HashMap<String,List<String>>citiesClone=(HashMap) cities.clone();
    citiesClone.get("France").set(0,"Dublin");
    System.out.println(cities.get("France"));

Of course, it prints "[Dublin, Grenoble]". The original
cities object has been modified through its clone,
because the clone operation does not clone the list in each
entry.

Cloning through serialization

One solution to these problems is to clone using serialization.
Usually, serialization is used to send objects off somewhere (into
a file or over the network) so that somebody else can reconstruct
them later. But you can abuse it to reconstruct the object
yourself immediately. If the object is serializable at all, then
the reconstruction should be a faithful copy. In normal uses of
serialization, the original object is nowhere near; it could be on
the other side of the world at the far end of a network
connection. So you can be sure that changing the copy will have
no effect on the original.

Before going any further, I should caution that this technique
is not to be used lightly. First of all, serialization is
hugely expensive. It could easily be a hundred times more
expensive than the clone() method. Secondly, not all objects
are serializable
. Thirdly, making a class serializable is
tricky
and not all classes can be relied on to get it right.
(You can assume that system classes are correct, though.)

Class-loading subtleties

When an object is being deserialized, the platform has to be
able to find its class in order to construct an instance of that
class. Imagine that you're deserializing an object you received
over the network. If it's a com.example.Foo, the
serialization framework is going to have to find that class
somehow. How does it do it?

The answer is that it uses the ClassLoader of the code that is
doing the deserialization. So if I define a class
SerialClone that serializes and deserializes an
object, then by default the class of that object, and the
classes of other objects it references, need to be known to the
ClassLoader of SerialClone.

In simple cases, this will always be true. Every class of
interest is on the classpath, including SerialClone
and the class of any object it might be asked to copy.

In more complicated environments, with several ClassLoaders,
this default behaviour is not necessarily what you want. In a
web server, for example, typically every web app has its own
ClassLoader. If the web server wanted to serial-clone an object
it got from a web app, it would need to reconstruct the object
using the web app's ClassLoader. How could it do that?

The answer is that it can use class annotations. RMI
uses these to write information into the serial stream that
tells the remote partner where it can find the classes of
objects that are being sent. The information is the appropriate
URL to download the class from.

We don't need anything nearly as complicated as that. We
already have the class locally. We know the class when we're
serializing. We just need to figure out how to recover it when
deserializing.

The first time any class is referenced from an object being serialized, ObjectOutputStream calls its annotateClass method. Correspondingly, the first time a
class is referenced from an object being deserialized,
ObjectInputStream calls its
href="http://java.sun.com/javase/6/docs/api/java/io/ObjectInputStream.html#resolveClass(java.io.ObjectStreamClass)">
resolveClass method.

So we can create subclasses of ObjectOutputStream and
ObjectInputStream that override these methods. Our
annotateClass method will simply record the class in a list; it
won't actually write anything into the stream. The resolveClass
is called at the same point in deserialization as annotateClass
is called in serialization. So resolveClass can simply consume
the classes one by one from the list where annotateClass
recorded them.

Deep modifications in the cloned object

Another method that you can override in ObjectOutputStream is

href="http://java.sun.com/javase/6/docs/api/java/io/ObjectOutputStream.html#replaceObject(java.lang.Object)">replaceObject.
This allows you to replace one object with another. For
example, suppose you wanted to change the string "foo" into the
string "bar" everywhere it occurs within an object. Your
ObjectOutputStream subclass might look like this:

classMyObjectOutputStreamextendsObjectOutputStream{
    MyObjectOutputStream(OutputStreamout)throwsIOException{     super(out);
    enableReplaceObject(true);
    }     protectedObject replaceObject(Object obj)throwsIOException{
    if(obj.equals("foo"))
        return"bar";
    else
        returnsuper.replaceObject(obj);
    }
   
    ...
}

This will change "foo" into "bar" even if it occurs deep inside
the object being serialized, for example in any of the Strings in
a Map>. Don't forget
to call enableReplaceObject(true)
or nothing will
happen!

Of course the ability to make arbitrary changes to object
contents is potentially very dangerous. Proceed with caution.
For this reason, unprivileged code cannot override replaceObject
in this way; if there is a SecurityManager then you must have
href="http://java.sun.com/javase/6/docs/api/java/io/SerializablePermission.html">
SerializablePermission("enableSubstitution").

The code

Here's a basic class that clones using serialization. Once
again, only use this as a last resort, for all the reasons
mentioned above.

Usage is copy =SerialClone.clone(object).

package serialclone;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InvalidClassException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamClass;
import java.io.OutputStream;
import java.util.LinkedList;
import java.util.Queue; publicclassSerialClone{
    publicstatic<T> T clone(T x){
try{
    return cloneX(x);
}catch(IOException e){
    thrownewIllegalArgumentException(e);
}catch(ClassNotFoundException e){
    thrownewIllegalArgumentException(e);
}
    }     privatestatic<T> T cloneX(T x)throwsIOException,ClassNotFoundException{
ByteArrayOutputStream bout =newByteArrayOutputStream();
CloneOutput cout =newCloneOutput(bout);
cout.writeObject(x);
byte[] bytes = bout.toByteArray(); ByteArrayInputStream bin =newByteArrayInputStream(bytes);
CloneInput cin =newCloneInput(bin, cout); @SuppressWarnings("unchecked")  // thanks to Bas de Bakker for the tip!
T clone =(T) cin.readObject();
return clone;
    }     privatestaticclassCloneOutputextendsObjectOutputStream{
Queue<Class<?>> classQueue =newLinkedList<Class<?>>(); CloneOutput(OutputStreamout)throwsIOException{
    super(out);
} @Override
protectedvoid annotateClass(Class<?> c){
    classQueue.add(c);
} @Override
protectedvoid annotateProxyClass(Class<?> c){
    classQueue.add(c);
}
    }     privatestaticclassCloneInputextendsObjectInputStream{
privatefinalCloneOutput output; CloneInput(InputStreamin,CloneOutput output)throwsIOException{
    super(in);
    this.output = output;
}     @Override
protectedClass<?> resolveClass(ObjectStreamClass osc)
throwsIOException,ClassNotFoundException{
    Class<?> c = output.classQueue.poll();
    String expected = osc.getName();
    String found =(c ==null)?null: c.getName();
    if(!expected.equals(found)){
thrownewInvalidClassException("Classes desynchronized: "+
"found "+ found +" when expecting "+ expected);
    }
    return c;
}     @Override
    protectedClass<?> resolveProxyClass(String[] interfaceNames)
throwsIOException,ClassNotFoundException{
        return output.classQueue.poll();
    }
    }
} From: https://weblogs.java.net/blog/emcmanus/archive/2007/04/cloning_java_ob.html

Cloning Java objects using serialization的更多相关文章

  1. Java序列化(Serialization)

    关于Java的序列化的文章在网上已经够多了,在这里写关于Java序列化的文章是对自己关于这方面的的一种总结,结合以前的开发经验与网上的资料,写了这篇文章,对自己是有着巩固记忆的作用,也希望能够对大家有 ...

  2. Effective Java 78 Consider serialization proxies instead of serialized instances

    Serialization proxy pattern private static nested class (has a single constructor whose parameter ty ...

  3. Unity学习笔记 - Assets, Objects and Serialization

    Assets和Objects Asset是存储在硬盘上的文件,保存在Unity项目的Assets文件夹内.比如:纹理贴图.材质和FBX都是Assets.一些Assets以Unity原生格式保存数据,例 ...

  4. learning java Objects.requireNonNull 当传入参数为null时,该方法返回参数本身

    System.out.println(Objects.hashCode(obj)); System.out.println(Objects.toString(obj)); System.out.pri ...

  5. java的Serialization 机制

    基本使用方法               Serialization是指把类或者基本的数据类型持久化(persistence)到数据流(Stream)中,包括文件.字节流.网络数据流.         ...

  6. Objects First with Java 读书笔记 (1)

    umm...这学期被发了助教Java的任务,为了避免误人子弟从零开始现学.课是英语教学,就不逐字翻译了,方便记. 参考书目:Objects First with Java - A Practical ...

  7. Java性能提示(全)

    http://www.onjava.com/pub/a/onjava/2001/05/30/optimization.htmlComparing the performance of LinkedLi ...

  8. 115 Java Interview Questions and Answers – The ULTIMATE List--reference

    In this tutorial we will discuss about different types of questions that can be used in a Java inter ...

  9. Java资源大全中文版(Awesome最新版)

    Awesome系列的Java资源整理.awesome-java 就是akullpp发起维护的Java资源列表,内容包括:构建工具.数据库.框架.模板.安全.代码分析.日志.第三方库.书籍.Java 站 ...

随机推荐

  1. Android.Hack.02_Animations

    #01# TextView 和 ImageView TextView和Imageview切换卡顿,为了实现更好的切换,可以用动画来实现,系统自带的TextViewSwitcher 和ImageView ...

  2. Javascript 链式运动框架——逐行分析代码,让你轻松了解运动的原理

    所谓链式运动,就是一环扣一环.我们的很多运动实际上来说指的就是分阶段的,第一个阶段动完,下个阶段开始动. 这个链式运动框架就是用来处理这些问题的. 我们先来看下之前的运动框架,以下是Javascrip ...

  3. c语言字符串翻转系列

    2013-10-25 最近碰到一道笔试题,是关于字符串翻转的.题目是:将一段英文翻转,但保留单词拼写,如给定字符串str="I am a student",返回为"stu ...

  4. lex 和 yacc 的区别与联系

    lex负责词法解析,而yacc负责语法解析,其实说白了就是lex负责根据指定的正则表达式,将输入的字符串匹配成一个一个的token,同时允许用户将当前匹配到的字符串进行处理,并且允许返回一个标识当前t ...

  5. [转]C 语言指针的使用

    第一章 指针的概念 指针是一个特殊的变量,它里面存储的数值被解释成为内存里的一个地址. 要搞清一个指针需要搞清指针的四方面的内容:指针的类型,指针所指向的 类型,指针的值或者叫指针所指向的内存区,还有 ...

  6. 常用在线工具及API网址总结

    1.小图标在线查找 https://www.iconfinder.com/ 2.在线做图,Flowchart流程图,BPMN图,Org组织结构图等 http://www.processon.com/ ...

  7. C#的DLL注册为COM,Delphi来调用

    非常实用的东西!过去知道这个方法的话可以解决多少问题啊 首先建立一个C#的DLL工程,写一个类 //Test.csnamespace Test...{public class MyTest...{pu ...

  8. 本地拦截genymotion或者Android模拟器的网络请求

    我们在主机上面运行了Burp或者fiddler,那么代理已经监听在本机的8080端口了. 那么我们需要在模拟器中进行如下设置: 1.在设置中,长按当前连接的wifi网络,弹出如下: 2. 点击修改网络 ...

  9. Linux进程间通信——使用信号量

    这篇文章将讲述别一种进程间通信的机制——信号量.注意请不要把它与之前所说的信号混淆起来,信号与信号量是不同的两种事物.有关信号的更多内容,可以阅读我的另一篇文章:Linux进程间通信——使用信号.下面 ...

  10. HDU 5700 区间交(线段树)

    [题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=5700 [题目大意] 给出一个长度为n的数列和m个区间,现在求k个区间,使得他们的区间交内的数列项和 ...