Java: рекомендуемое решение для глубокого клонирования / копирования экземпляра - PullRequest
171 голосов
/ 28 января 2010

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

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

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

Сделай сам:

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

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

С вашими собственными инструментами отражения или с внешнимпомощник (например, Jakarta Common-Beans) легко написать общий метод копирования, который будет выполнять работу в одну строку.
pro:
- легко писать
- не требует обслуживания
минусы:
- минус продолжениеЧто происходит
- ошибка подвержена изменчивому объекту, если инструмент отражения тоже не клонирует подобъекты
- медленное выполнение

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

Использовать платформу, которая делаетэто для вас, например:
Commons-lang SerializationUtils
Java Deep Cloning Library
Dozer
Kryo

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

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

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

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

Спасибо.

Ответы [ 8 ]

148 голосов
/ 28 января 2010

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

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

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

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

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

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

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

35 голосов
/ 29 января 2010

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

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

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

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

         public Yum(Yum yum);
    

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

9 голосов
/ 22 ноября 2013

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

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

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

5 голосов
/ 28 июля 2015

Используйте 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));
2 голосов
/ 28 января 2010

Зависит.

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

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

1 голос
/ 29 ноября 2016

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

}
1 голос
/ 28 января 2010

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

1 голос
/ 28 января 2010

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

...