Как сделать глубокую копию объекта в Java?



в java немного сложно реализовать функцию глубокой копии объекта. Какие шаги вы предпринимаете, чтобы гарантировать, что исходный объект и клонированный один не имеют ссылки?

967   17  

17 ответов:

безопасный способ-сериализовать объект, а затем десериализовать. Это гарантирует, что все является совершенно новой ссылкой.

вот статья о том, как сделать это эффективно.

предостережения: классы могут переопределять сериализацию таким образом, что новые экземпляры не создано, например, для синглетов. Также это, конечно, не работает, если ваши классы не Сериализуемы.

несколько человек упомянули использование или переопределение Object.clone(). Не делай этого. Object.clone() есть некоторые серьезные проблемы, и ее использование не рекомендуется в большинстве случаев. См. пункт 11, начиная с "Эффективная Java" Джошуа блох для полного ответа. Я считаю, что вы можете безопасно использовать Object.clone() на массивах примитивных типов, но кроме этого вам нужно быть разумным в правильном использовании и переопределении clone.

схемы, которые полагаются на сериализацию (XML или иначе) являются несколько аляповата.

здесь нет простого ответа. Если вы хотите глубоко скопировать объект, вам придется пересечь граф объектов и скопировать каждый дочерний объект явно с помощью конструктора копирования объекта или статического метода фабрики, который в свою очередь глубоко копирует дочерний объект. Неизменяемые (например String s) не нужно копировать. В стороне, вы должны поддерживать неизменность по этой причине.

вы можете сделать глубокую копию с сериализацией без создания файлов.

ваш объект, который вы хотите глубоко скопировать, должен будет implement serializable. Если класс не является окончательным и не может быть изменен, расширить класс и реализовать Serializable.

преобразовать свой класс в поток байтов:

ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(object);
oos.flush();
oos.close();
bos.close();
byte[] byteData = bos.toByteArray();

восстановить класс из потока байтов:

ByteArrayInputStream bais = new ByteArrayInputStream(byteData);
(Object) object = (Object) new ObjectInputStream(bais).readObject();

вы можете сделать глубокий клон на основе сериализации с помощью org.apache.commons.lang3.SerializationUtils.clone(T) в Apache Commons Lang, но будьте осторожны-производительность ужасна.

В общем случае рекомендуется писать собственные методы клонирования для каждого класса объекта в графе объектов, нуждающихся в клонировании.

одним из способов реализации глубокого копирования является добавление конструкторов копирования в каждый связанный класс. Конструктор копирования принимает экземпляр 'this' в качестве единственного аргумента и копирует из него все значения. Довольно небольшая работа, но довольно простая и безопасная.

EDIT: обратите внимание, что вам не нужно использовать методы доступа для чтения полей. Вы можете получить доступ ко всем полям напрямую, потому что исходный экземпляр всегда имеет тот же тип, что и экземпляр с конструктором копирования. Очевидно, но может быть упущенный.

пример:

public class Order {

    private long number;

    public Order() {
    }

    /**
     * Copy constructor
     */
    public Order(Order source) {
        number = source.number;
    }
}


public class Customer {

    private String name;
    private List<Order> orders = new ArrayList<Order>();

    public Customer() {
    }

    /**
     * Copy constructor
     */
    public Customer(Customer source) {
        name = source.name;
        for (Order sourceOrder : source.orders) {
            orders.add(new Order(sourceOrder));
        }
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Edit: обратите внимание, что при использовании конструкторов копирования необходимо знать тип среды выполнения копируемого объекта. При вышеуказанном подходе вы не можете легко скопировать смешанный список (вы можете сделать это с помощью некоторого кода отражения).

Apache commons предлагает быстрый способ глубокого клонирования объекта.

My_Object object2= org.apache.commons.lang.SerializationUtils.clone(object1);

вы можете использовать библиотеку что имеет простой API и выполняет относительно быстрое клонирование с отражением (должно быть быстрее, чем методы сериализации).

Cloner cloner = new Cloner();

MyClass clone = cloner.deepClone(o);
// clone is a deep-clone of o

XStream действительно полезен в таких случаях. Вот простой код для клонирования

private static final XStream XSTREAM = new XStream();
...

Object newObject = XSTREAM.fromXML(XSTREAM.toXML(obj));

один очень простой и простой подход-использовать Jackson JSON для сериализации сложного объекта Java в JSON и считывания его обратно.

http://wiki.fasterxml.com/JacksonInFiveMinutes

использовать XStream(http://x-stream.github.io/). вы даже можете контролировать, какие свойства можно игнорировать с помощью аннотаций или явно указав имя свойства для класса XStream. Кроме того, вам не нужно реализовывать clonable интерфейс.

глубокое копирование может быть сделано только с согласия каждого класса. Если у вас есть контроль над иерархией классов, то вы можете реализовать clonable интерфейс и реализовать Clone метод. В противном случае выполнение глубокой копии невозможно сделать безопасно, потому что объект также может совместно использовать ресурсы, не связанные с данными (например, соединения с базой данных). В целом, однако глубокое копирование считается плохой практикой в среде Java и его следует избегать с помощью соответствующих методов проектирования.

import com.thoughtworks.xstream.XStream;

public class deepCopy {
    private static  XStream xstream = new XStream();

    //serialize with Xstream them deserialize ...
    public static Object deepCopy(Object obj){
        return xstream.fromXML(xstream.toXML(obj));
    }
}

для сложных объектов и когда производительность не является значительным я использую библиотеку json, как gson чтобы сериализовать объект в текст json, затем десериализуйте текст, чтобы получить новый объект.

gson, который основан на отражении будет работать в большинстве случаев, за исключением того, что transient поля не будут скопированы и объекты с круговой ссылкой с причиной StackOverflowError.

public static <T> T Copy(T AnObject, Class<T> ClassInfo)
{
    Gson gson = new GsonBuilder().create();
    String text = gson.toJson(AnObject);
    T newObject = gson.fromJson(text, ClassInfo);
    return newObject;
}
public static void main(String[] args)
{
    String originalObject = "hello";
    String copiedObject = Copy(originalObject, String.class);

}

Я бульдозер для клонирования объектов java, и это здорово,Kryo библиотека является еще одной отличной альтернативой.

на Spring Framework пользователи. Используя класс org.springframework.util.SerializationUtils:

@SuppressWarnings("unchecked")
public static <T extends Serializable> T clone(T object) {
     return (T) SerializationUtils.deserialize(SerializationUtils.serialize(object));
}

1)

public static Object deepClone(Object object) {
   try {
     ByteArrayOutputStream baos = new ByteArrayOutputStream();
     ObjectOutputStream oos = new ObjectOutputStream(baos);
     oos.writeObject(object);
     ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
     ObjectInputStream ois = new ObjectInputStream(bais);
     return ois.readObject();
   }
   catch (Exception e) {
     e.printStackTrace();
     return null;
   }
 }

2)

    // (1) create a MyPerson object named Al
    MyAddress address = new MyAddress("Vishrantwadi ", "Pune", "India");
    MyPerson al = new MyPerson("Al", "Arun", address);

    // (2) make a deep clone of Al
    MyPerson neighbor = (MyPerson)deepClone(al);

здесь ваш myperson и MyAddress класс должен реализовать serilazable интерфейс

BeanUtils делает действительно хорошую работу глубокого клонирования бобов.

BeanUtils.cloneBean(obj);

Comments

    Ничего не найдено.