如何在Java中复制对象?

How do I copy an object in Java?

考虑下面的代码:

1
2
3
4
5
6
7
8
9
DummyBean dum = new DummyBean();
dum.setDummy("foo");
System.out.println(dum.getDummy()); // prints 'foo'

DummyBean dumtwo = dum;
System.out.println(dumtwo.getDummy()); // prints 'foo'

dum.setDummy("bar");
System.out.println(dumtwo.getDummy()); // prints 'bar' but it should print 'foo'

所以,我想复制到dumtwodumdumdumtwo和变化没有影响。但在这样做,是不规范的。当我在路上的东西dum变化,该变化是一dumtwo照样发生。

我想,当我说dumtwo = dumJava的复制,仅参考。所以,有任何的方式来创建新的副本和assign dumtwodum它吗?


创建复制构造函数:

1
2
3
4
5
6
7
class DummyBean {
  private String dummy;

  public DummyBean(DummyBean another) {
    this.dummy = another.dummy; // you can access  
  }
}

每个对象都有一个克隆方法,可以用来复制对象,但不要使用它。创建类和执行不正确的克隆方法太容易了。如果你要这么做,至少读一下Joshua Bloch在有效Java中所说的话。


Basic:Java中的对象复制。

让我们假设一个对象-obj1,它包含两个对象,containedobj1和containedobj2。< BR>enter image description here

浅复制:
浅复制创建同一类的新instance,并将所有字段复制到新实例并返回该实例。对象类提供了一个clone方法,并提供了对浅复制的支持。
enter image description here

深度复制:
当对象与其引用的对象一起复制时,将发生深度复制。下图显示了对其执行深度复制后的obj1。不仅复制了obj1,而且还复制了其中包含的对象。我们可以用Java Object Serialization进行深度复制。不幸的是,这种方法也有一些问题(详细示例)。< BR>enter image description here

可能的问题:
clone很难正确实施。
最好使用防御性复制、复制构造函数(如@egaga reply)或静态工厂方法。

  • 如果你有一个对象,你知道它有一个公共的clone()方法,但是你在编译时不知道对象的类型,那么你就有问题了。Java有一个称为EDCOX1的接口8。在实践中,如果我们想要生成一个对象Cloneable,就应该实现这个接口。Object.clone是受保护的,因此我们必须用公共方法覆盖它,以便它可以被访问。
  • 当我们尝试对复杂对象进行深度复制时,会出现另一个问题。假设所有成员对象变量的clone()方法也做了深度复制,这是一个假设的风险太大。必须控制所有类中的代码。
  • 例如,org.apache.commons.lang.serializationutils将具有使用序列化(source)进行深度克隆的方法。如果我们需要克隆bean,那么在org.apache.commons.beanutils(源代码)中有两种实用方法。

    • cloneBean将基于可用的属性getter和setter克隆bean,即使bean类本身不实现可克隆。
    • 对于所有属性名称相同的情况,copyProperties都会将属性值从源bean复制到目标bean。


    import org.apache.commons.lang.SerializationUtils;包中,有一种方法:

    1
    SerializationUtils.clone(Object);

    例子:

    1
    this.myObjectCloned = SerializationUtils.clone(this.object);


    如下所示:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    public class Deletable implements Cloneable{

        private String str;
        public Deletable(){
        }
        public void setStr(String str){
            this.str = str;
        }
        public void display(){
            System.out.println("The String is"+str);
        }
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
    }

    无论您想在哪里获得另一个对象,只需执行克隆即可。例如:

    1
    2
    3
    4
    Deletable del = new Deletable();
    Deletable delTemp = (Deletable ) del.clone(); // this line will return you an independent
                                     // object, the changes made to this object will
                                     // not be reflected to other object


    为什么使用反射API没有答案?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    private static Object cloneObject(Object obj){
            try{
                Object clone = obj.getClass().newInstance();
                for (Field field : obj.getClass().getDeclaredFields()) {
                    field.setAccessible(true);
                    field.set(clone, field.get(obj));
                }
                return clone;
            }catch(Exception e){
                return null;
            }
        }

    这真的很简单。

    编辑:通过递归包含子对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    private static Object cloneObject(Object obj){
            try{
                Object clone = obj.getClass().newInstance();
                for (Field field : obj.getClass().getDeclaredFields()) {
                    field.setAccessible(true);
                    if(field.get(obj) == null || Modifier.isFinal(field.getModifiers())){
                        continue;
                    }
                    if(field.getType().isPrimitive() || field.getType().equals(String.class)
                            || field.getType().getSuperclass().equals(Number.class)
                            || field.getType().equals(Boolean.class)){
                        field.set(clone, field.get(obj));
                    }else{
                        Object childObj = field.get(obj);
                        if(childObj == obj){
                            field.set(clone, clone);
                        }else{
                            field.set(clone, cloneObject(field.get(obj)));
                        }
                    }
                }
                return clone;
            }catch(Exception e){
                return null;
            }
        }


    我使用Google的JSON库将其序列化,然后创建序列化对象的新实例。它可以进行深度复制,但有一些限制:

    • 不能有任何递归引用

    • 它不会复制不同类型的数组

    • 数组和列表应该是类型化的,否则它找不到要实例化的类

    • 您可能需要在声明自己的类中封装字符串。

    我还使用这个类保存用户首选项、窗口以及运行时不需要重新加载的内容。它非常容易使用和有效。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    import com.google.gson.*;

    public class SerialUtils {

    //___________________________________________________________________________________

    public static String serializeObject(Object o) {
        Gson gson = new Gson();
        String serializedObject = gson.toJson(o);
        return serializedObject;
    }
    //___________________________________________________________________________________

    public static Object unserializeObject(String s, Object o){
        Gson gson = new Gson();
        Object object = gson.fromJson(s, o.getClass());
        return object;
    }
           //___________________________________________________________________________________
    public static Object cloneObject(Object o){
        String s = serializeObject(o);
        Object object = unserializeObject(s,o);
        return object;
    }
    }


    是的,您只是在引用对象。如果对象实现了Cloneable,则可以克隆该对象。

    看看这个关于复制对象的wiki文章。

    请参阅:对象复制


    对。你需要深度复制你的对象。


    Cloneable及以下代码添加到类中

    1
    2
    3
    public Object clone() throws CloneNotSupportedException {
            return super.clone();
        }

    用这个clonedObject = (YourClass) yourClassObject.clone();


    这里有一个很好的关于clone()的解释,如果你最终需要它…

    这里:克隆(Java方法)


    这也行。假设模型

    1
    2
    3
    4
    class UserAccount{
       public int id;
       public String name;
    }

    第一次添加compile 'com.google.code.gson:gson:2.8.1'到您的应用程序>Gradle&Sync。然后

    1
    2
    Gson gson = new Gson();
    updateUser = gson.fromJson(gson.toJson(mUser),UserAccount.class);

    通过在访问修饰符后使用transient关键字,可以排除使用字段。

    注意:这是不好的做法。也不建议使用CloneableJavaSerialization,因为它很慢而且很坏。为获得最佳性能引用而编写复制构造函数。

    类似的东西

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    class UserAccount{
            public int id;
            public String name;
            //empty constructor
            public UserAccount(){}
            //parameterize constructor
            public UserAccount(int id, String name) {
                this.id = id;
                this.name = name;
            }

            //copy constructor
            public UserAccount(UserAccount in){
                this(in.id,in.name);
            }
        }

    90000次迭代的测试状态:
    UserAccount clone = gson.fromJson(gson.toJson(aO), UserAccount.class);线808ms

    UserAccount clone = new UserAccount(aO);线用时小于1ms

    结论:如果你的老板疯了,你更喜欢速度,那就使用GSON。如果您喜欢质量,请使用第二个副本构造函数。

    您也可以在Android Studio中使用复制构造函数代码生成器插件。


    使用深度克隆实用程序:

    1
    SomeObjectType copy = new Cloner().deepClone(someObject);

    这将深度复制任何Java对象,在HTTPS://GITHUBCOM/KOSTASKOGIOSOS/克隆中进行检查


    要做到这一点,您必须以某种方式克隆对象。虽然Java有一个克隆机制,但是如果你不需要的话,就不要使用它。创建一个复制方法,该方法为您完成复制工作,然后执行以下操作:

    1
    dumtwo = dum.copy();

    以下是关于完成副本的不同技术的更多建议。


    深度克隆是您的答案,它需要实现Cloneable接口并重写clone()方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    public class DummyBean implements Cloneable {

       private String dummy;

       public void setDummy(String dummy) {
          this.dummy = dummy;
       }

       public String getDummy() {
          return dummy;
       }

       @Override
       public Object clone() throws CloneNotSupportedException {
          DummyBean cloned = (DummyBean)super.clone();
          cloned.setDummy(cloned.getDummy());
          // the above is applicable in case of primitive member types,
          // however, in case of non primitive types
          // cloned.setNonPrimitiveType(cloned.getNonPrimitiveType().clone());
          return cloned;
       }
    }

    你可以这样称呼它DummyBean dumtwo = dum.clone();


    除了显式复制之外,另一种方法是使对象不可变(没有set或其他mutator方法)。这样问题就永远不会出现。对于较大的对象来说,不变性变得更加困难,但另一方面,它会将你推向分裂为连贯的小对象和复合物的方向。


    1
    2
    3
    4
    5
    6
    7
    class DB {
      private String dummy;

      public DB(DB one) {
        this.dummy = one.dummy;
      }
    }

    传递要复制的对象并获取所需的对象:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    private Object copyObject(Object objSource) {
            try {
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                ObjectOutputStream oos = new ObjectOutputStream(bos);
                oos.writeObject(objSource);
                oos.flush();
                oos.close();
                bos.close();
                byte[] byteData = bos.toByteArray();
                ByteArrayInputStream bais = new ByteArrayInputStream(byteData);
                try {
                    objDest = new ObjectInputStream(bais).readObject();
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            return objDest;

        }

    现在将objDest解析为所需的对象。

    快乐编码!


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    public class MyClass implements Cloneable {

    private boolean myField= false;
    // and other fields or objects

    public MyClass (){}

    @Override
    public MyClass clone() throws CloneNotSupportedException {
       try
       {
           MyClass clonedMyClass = (MyClass)super.clone();
           // if you have custom object, then you need create a new one in here
           return clonedMyClass ;
       } catch (CloneNotSupportedException e) {
           e.printStackTrace();
           return new MyClass();
       }

      }
    }

    在你的代码中:

    1
    2
    3
    MyClass myClass = new MyClass();
    // do some work with this object
    MyClass clonedMyClass = myClass.clone();


    您可以使用x stream从http://x-stream.github.io/自动进行深度复制:

    XStream is a simple library to serialize objects to XML and back
    again.

    将其添加到项目中(如果使用maven)

    1
    2
    3
    4
    5
    <dependency>
        <groupId>com.thoughtworks.xstream</groupId>
        xstream</artifactId>
        <version>1.3.1</version>                
    </dependency>

    然后

    1
    2
    3
    DummyBean dum = new DummyBean();
    dum.setDummy("foo");
    DummyBean dumCopy = (DummyBean) XSTREAM.fromXML(XSTREAM.toXML(dum));

    有了这个,您就有了一个副本,不需要实现任何克隆接口。


    您可以尝试实现Cloneable并使用clone()方法;但是,如果您使用clone方法,则应按标准始终覆盖Objectpublic Object clone()方法。


    如果可以将注释添加到源文件中,则可以使用这样的注释处理器或代码生成器。

    1
    2
    3
    4
    5
    6
    import net.zerobuilder.BeanBuilder

    @BeanBuilder
    public class DummyBean {
      // bean stuff
    }

    将生成一个类DummyBeanBuilders,它有一个静态方法dummyBeanUpdater来创建浅副本,与手工创建的方法相同。

    1
    2
    3
    4
    DummyBean bean = new DummyBean();
    // Call some setters ...
    // Now make a copy
    DummyBean copy = DummyBeanBuilders.dummyBeanUpdater(bean).done();