В java немного сложно реализовать функцию копирования глубоких объектов. Какие шаги вы предпримете для обеспечения того, чтобы исходный объект и клонированный объект не имели ссылки?
Как сделать глубокую копию объекта в Java?
Ответ 1
Безопасным способом является сериализация объекта, а затем десериализация. Это гарантирует, что все станет новой ссылкой.
Вот статья о том, как это сделать эффективно.
Предостережения: возможно, классы переопределяют сериализацию, так что новые экземпляры не создаются, например. для однотонных. Кроме того, это, конечно, не работает, если ваши классы не являются Serializable.
Ответ 2
Несколько человек упоминали об использовании или переопределении Object.clone()
. Не делай этого. Object.clone()
имеет некоторые серьезные проблемы, и его использование не рекомендуется в большинстве случаев. Пожалуйста, см. Пункт 11 из " Эффективной Явы " Джошуа Блоха для полного ответа. Я полагаю, что вы можете безопасно использовать Object.clone()
в массивах примитивных типов, но помимо этого вы должны быть осторожны с правильным использованием и переопределением клона.
Схемы, которые полагаются на сериализацию (XML или иным образом), являются хитрыми.
Здесь нет простого ответа. Если вы хотите глубоко копировать объект, вам придется пройти по графу объектов и явно скопировать каждый дочерний объект с помощью конструктора копирования объекта или статического метода фабрики, который, в свою очередь, копирует дочерний объект. Неизменяемые (например, String
) не должны быть скопированы. Кроме того, по этой причине вы должны поддерживать неизменность.
Ответ 3
Вы можете сделать глубокую копию с сериализацией без создания файлов.
Ваш объект, который вам нужен для глубокой копии, должен быть implement 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();
Ответ 4
Вы можете сделать глубокий клон на основе сериализации, используя org.apache.commons.lang3.SerializationUtils.clone(T)
в Apache Commons Lang, но будьте осторожны - производительность ужасна.
Как правило, рекомендуется писать собственные методы клонирования для каждого класса объекта в графе объектов, нуждающихся в клонировании.
Ответ 5
Один из способов реализации глубокой копии - добавить конструкторы копирования в каждый связанный класс. Конструктор копирования принимает экземпляр '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;
}
}
Изменить: Обратите внимание, что при использовании конструкторов копирования вам нужно знать тип среды выполнения объекта, который вы копируете. С помощью вышеуказанного подхода вы не можете легко скопировать смешанный список (вы можете сделать это с помощью некоторого кода отражения).
Ответ 6
Вы можете использовать библиотеку, которая имеет простой API и выполняет относительно быстрое клонирование с отражением (должно быть быстрее, чем методы сериализации).
Cloner cloner = new Cloner();
MyClass clone = cloner.deepClone(o);
// clone is a deep-clone of o
Ответ 7
Apache commons предлагает быстрый способ глубокого клонирования объекта.
My_Object object2= org.apache.commons.lang.SerializationUtils.clone(object1);
Ответ 8
XStream действительно полезен в таких случаях. Вот простой код для клонирования
private static final XStream XSTREAM = new XStream();
...
Object newObject = XSTREAM.fromXML(XSTREAM.toXML(obj));
Ответ 9
Один очень простой и простой подход - использовать Jackson JSON для сериализации сложного Java-объекта для JSON и чтения его.
Ответ 10
Используйте XStream (http://x-stream.github.io/). Вы можете даже контролировать, какие свойства можно игнорировать с помощью аннотаций или явно указывать имя свойства для класса XStream. Кроме того, вам не нужно реализовывать клонируемый интерфейс.
Ответ 11
Глубокое копирование может выполняться только с согласия каждого класса. Если у вас есть контроль над иерархией классов, вы можете реализовать клонируемый интерфейс и реализовать метод Clone. В противном случае сделать глубокую копию невозможно, так как объект может также совместно использовать ресурсы без данных (например, соединения с базой данных). В целом, однако, глубокое копирование считается плохой практикой в среде Java, и его следует избегать с помощью соответствующих методов проектирования.
Ответ 12
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));
}
}
Ответ 13
Для сложных объектов и когда производительность невелика, я использую библиотеку 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);
}
Ответ 14
Для пользователей Spring Framework. Использование класса org.springframework.util.SerializationUtils
:
@SuppressWarnings("unchecked")
public static <T extends Serializable> T clone(T object) {
return (T) SerializationUtils.deserialize(SerializationUtils.serialize(object));
}
Ответ 15
Я использовал Dozer для клонирования java-объектов, и это здорово, Kryo - еще одна отличная альтернатива.
Ответ 16
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 интерфейс
Ответ 17
BeanUtils делает действительно хорошую работу глубокое клонирование beans.
BeanUtils.cloneBean(obj);