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

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

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

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

Сделайте это самостоятельно:

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

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

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

- медленное выполнение

Использование рамки клонирования:

Используйте фреймворк, который сделает это за вас, например:
commons-lang SerializationUtils
Библиотека глубокого клонирования Java
Dozer
Kryo

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

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

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

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

Спасибо.

Ответ 1

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

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

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

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

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

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

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

Ответ 2

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

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

  • Используйте конструктор factory вместо конструктора:

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

         public Yum(Yum yum);
    

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

Ответ 3

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

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

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

Ответ 4

Используйте XStream toXML/fromXML в памяти. Чрезвычайно быстрая и долгое время существует и идет крепко. Объекты не обязательно должны быть Serializable, и у вас нет рефлекса использования (хотя 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));

Ответ 5

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

Ответ 6

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

Ответ 7

Зависит.

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

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

Ответ 8

Для сложных объектов и при невысокой производительности я использую 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);

}