Java: рекомендуемое решение для глубокого клонирования / копирования экземпляра

Мне интересно, есть ли рекомендуемый способ сделать глубокий клон / копию экземпляра в java.

У меня есть 3 решения в уме, но я могу пропустить некоторые, и я хотел бы иметь ваше мнение

edit: включите bohzo propositon и уточните вопрос: это больше о глубоком клонировании, чем о мелком клонировании.

Сделай сам:

Закодируйте свойства clone by hand после свойств и убедитесь, что изменяемые экземпляры клонированы тоже.
pro:
- контроль за тем, что будет выполнено
- быстрое исполнение
плюсы:
- нудно писать и поддерживать
- ошибка склонна (ошибка копирования / вставки, отсутствующее свойство, переназначенное изменяемое свойство)

использовать отражение:

с помощью собственных инструментов отражения или с помощью внешнего помощника (например, jakarta common-beans) легко написать общий метод копирования, который будет выполнять работу в одном линия.
pro:
- легко писать
- никакого обслуживания!--9-->плюсы:
- меньше контроля над тем, что происходит
- ошибка, склонная к изменяемому объекту, если инструмент отражения не клонирует подобъекты слишком
- медленное выполнение

используйте clone framework:

используйте фреймворк, который сделает это за вас, например:
commons-Lang SerializationUtils
Глубокое Клонирование Java Библиотека
бульдозер
Kryo

pro:
- то же, что отражение!--9--> - больше контроля над тем, что будет точно клонироваться.
плюсы:
- каждый изменяемый экземпляр полностью клонирован, даже в конце иерархии
- может быть очень медленно выполнить

используйте инструментарий байт-кода для записи клона во время выполнения

javassit, как bcel или cglib может использоваться для создания выделенного клона так же быстро, как одна рука. Кто-то знает lib, использующий один из этих инструментов для этой цели ?

Что я пропустил здесь ?
Какой из них вы бы порекомендовали ?

спасибо.

8 ответов


для глубокого клонирования (клонирует всю иерархию объектов):

  • commons-Lang SerializationUtils - использование сериализации-если все классы находятся под вашим контролем, и вы можете принудительно реализовать Serializable.

  • Библиотека Глубокого Клонирования Java - использование отражения-в случаях, когда классы или объекты, которые вы хотите клонировать, находятся вне вашего контроля (сторонняя библиотека), и вы не можете заставить их реализовать Serializable, или в случаях, когда вы не хотите реализовать Serializable.

для мелкого клонирования (клоны только свойства первого уровня):

  • commons-beanutils BeanUtils - в большинстве случаев.

  • Весна BeanUtils - если вы уже используете spring и, следовательно, имеете эту утилиту на пути к классам.

Я намеренно опустил опцию "Сделай сам" - API выше обеспечить хороший контроль над тем, что и что не клонировать (например, с помощью transient или String[] ignoreProperties), поэтому изобретение колеса не является предпочтительным.


книга Джошуа Блоха имеет целую главу под названием "Пункт 10: Переопределить Клон Разумно" в котором он переходит к тому, почему переопределение клона по большей части является плохой идеей, потому что спецификация Java для него создает много проблем.

он предлагает несколько альтернатив:

  • используйте шаблон фабрики вместо конструктора:

         public static Yum newInstance(Yum yum);
    
  • использовать конструктор копирования:

         public Yum(Yum yum);
    

все классы коллекции в Java поддерживают конструктор копирования (например, new ArrayList (l);)


начиная с версии 2.07 Kryo поддерживает мелкое/глубокое клонирование:

Kryo kryo = new Kryo();
SomeClass someObject = ...
SomeClass copy1 = kryo.copy(someObject);
SomeClass copy2 = kryo.copyShallow(someObject);

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


используйте XStream toXML / fromXML в памяти. Чрезвычайно быстрый и был вокруг в течение длительного времени и идет сильным. Объекты не должны быть Сериализуемыми, и у вас нет отражения использования (хотя XStream делает). XStream может различать переменные, указывающие на один и тот же объект, и не случайно создавать две полные копии экземпляра. За эти годы было разработано множество подобных деталей. Я использовал его в течение нескольких лет, и это пойти. Это так же просто в использовании, как вы можете воображать.

new XStream().toXML(myObj)

или

new XStream().fromXML(myXML)

клонировать,

new XStream().fromXML(new XStream().toXML(myObj))

более лаконично:

XStream x = new XStream();
Object myClone = x.fromXML(x.toXML(myObj));

зависит.

для скорости используйте DIY. Для пуленепробиваемого используйте отражение.

кстати, сериализация не совпадает с refl, так как некоторые объекты могут предоставлять переопределенные методы сериализации (readObject/writeObject), и они могут быть багги


Я бы рекомендовал способ DIY, который в сочетании с хорошим методом hashCode() и equals() должен быть легко проверен в модульном тесте.


Я бы предложил переопределить объект.clone(), вызов супер.clone () сначала и чем вызов ref = ref.clone () для всех ссылок, которые вы хотите глубоко скопировать. Это более или менее Сделай сам подход, но требует немного меньше кодирования.


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

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

public static <ObjectType> ObjectType Copy(ObjectType AnObject, Class<ObjectType> ClassInfo)
{
    Gson gson = new GsonBuilder().create();
    String text = gson.toJson(AnObject);
    ObjectType newObject = gson.fromJson(text, ClassInfo);
    return newObject;
}
public static void main(String[] args)
{
    MyObject anObject ...
    MyObject copyObject = Copy(o, MyObject.class);

}