Как скопировать объект в Java?
Проблема, с которой я столкнулся, связана с копированием объектов в Java. Рассмотрим следующий код:
DummyBean dum = new DummyBean();
dum.setDummy("foo");
System.out.println(dum.getDummy()); // выводит 'foo'
DummyBean dumtwo = dum;
System.out.println(dumtwo.getDummy()); // выводит 'foo'
dum.setDummy("bar");
System.out.println(dumtwo.getDummy()); // выводит 'bar', хотя должно выводить 'foo'
Я создал объект dum
и установил его значение на "foo". Затем я присвоил dum
объекту dumtwo
. Интересно, что при изменении значения в dum
на "bar", dumtwo
также отображает это новое значение, вместо того чтобы сохранить "foo".
Я подозреваю, что когда я выполняю dumtwo = dum
, Java копирует только ссылку на объект, а не сам объект. Могу ли я создать полноценную копию объекта dum
и присвоить ее dumtwo
, чтобы изменения в dum
не влияли на dumtwo
? Как это правильно реализовать?
5 ответ(ов)
В пакете import org.apache.commons.lang.SerializationUtils;
есть метод:
SerializationUtils.clone(Object);
Этот метод используется для клонирования объектов. Например, вы можете использовать его следующим образом:
this.myObjectCloned = SerializationUtils.clone(this.object);
Этот код создает глубокую копию объекта this.object
и сохраняет её в this.myObjectCloned
. Это особенно полезно, когда вам нужно создать отдельную копию объекта, чтобы избежать нежелательных изменений исходного объекта. Однако стоит отметить, что для успешного клонирования объект должен реализовывать интерфейс Serializable
.
Чтобы получить независимую копию объекта, можно использовать метод clone
, как показано в следующем примере кода:
public class Deletable implements Cloneable {
private String str;
public Deletable() {
}
public void setStr(String str) {
this.str = str;
}
public void display() {
System.out.println("Строка: " + str);
}
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
Когда вам нужно создать другой объект, просто выполните клонирование. Например:
Deletable del = new Deletable();
Deletable delTemp = (Deletable) del.clone(); // эта строка вернет вам независимый
// объект, изменения, внесенные в этот объект,
// не отобразятся на другом объекте
Таким образом, вы сможете безопасно изменять delTemp
, не влияя на оригинальный объект del
. Не забудьте обработать возможное исключение CloneNotSupportedException
, поскольку метод clone
может его вызывать.
Проблема отсутствия ответов по использованию API рефлексии может заключаться в том, что многие разработчики предпочитают более простые и безопасные подходы для клонирования объектов. Тем не менее, я хочу предложить простой способ клонирования объекта с использованием рефлексии.
Вот пример метода, который создает клон объекта:
private static Object cloneObject(Object obj) {
try {
Object clone = obj.getClass().newInstance();
for (Field field : obj.getClass().getDeclaredFields()) {
field.setAccessible(true);
field.set(clone, field.get(obj));
}
return clone;
} catch (Exception e) {
return null;
}
}
Этот метод достаточно просто реализован. Однако, чтобы правильно клонировать объекты с ссылочными типами (например, объекты, которые могут содержать ссылки на другие объекты), можем использовать рекурсию. Вот улучшенная версия, которая включает клонирование вложенных объектов:
private static Object cloneObject(Object obj) {
try {
Object clone = obj.getClass().newInstance();
for (Field field : obj.getClass().getDeclaredFields()) {
field.setAccessible(true);
if (field.get(obj) == null || Modifier.isFinal(field.getModifiers())) {
continue;
}
if (field.getType().isPrimitive() || field.getType().equals(String.class)
|| field.getType().getSuperclass().equals(Number.class)
|| field.getType().equals(Boolean.class)) {
field.set(clone, field.get(obj));
} else {
Object childObj = field.get(obj);
if (childObj == obj) {
field.set(clone, clone);
} else {
field.set(clone, cloneObject(childObj));
}
}
}
return clone;
} catch (Exception e) {
return null;
}
}
Этот метод обрабатывает вложенные объекты и предотвращает бесконечные циклы, когда объект ссылается на самого себя. Обратите внимание, что подход с использованием рефлексии может иметь свои недостатки, такие как снижение производительности и возможные проблемы с безопасностью, поэтому следует использовать его с осторожностью.
Если вы используете библиотеку JSON от Google для сериализации объектов и создания их новых экземпляров, будьте внимательны к некоторым ограничениям, связанным с глубоким копированием:
- Не допускаются рекурсивные ссылки.
- Не будут скопированы массивы с различными типами.
- Массивы и списки должны иметь указанный тип, иначе не удастся найти класс для создания экземпляра.
- Строки могут потребовать обрамления в класс, который вы сами объявите.
Также я использую этот класс для сохранения пользовательских настроек, окон и прочего, что необходимо восстановить в процессе выполнения программы. Это очень удобно и эффективно.
Вот пример кода:
import com.google.gson.*;
public class SerialUtils {
//___________________________________________________________________________________
public static String serializeObject(Object o) {
Gson gson = new Gson();
String serializedObject = gson.toJson(o);
return serializedObject;
}
//___________________________________________________________________________________
public static Object unserializeObject(String s, Object o) {
Gson gson = new Gson();
Object object = gson.fromJson(s, o.getClass());
return object;
}
//___________________________________________________________________________________
public static Object cloneObject(Object o) {
String s = serializeObject(o);
Object object = unserializeObject(s, o);
return object;
}
}
С помощью этого класса вы сможете легко сериализовать и десериализовать объекты, а также осуществить глубокое копирование с учетом указанных ограничений.
Для выполнения глубокого клонирования в Java вам нужно реализовать интерфейс Cloneable
и переопределить метод clone()
. Вот пример, как это можно сделать:
public class DummyBean implements Cloneable {
private String dummy;
public void setDummy(String dummy) {
this.dummy = dummy;
}
public String getDummy() {
return dummy;
}
@Override
public Object clone() throws CloneNotSupportedException {
DummyBean cloned = (DummyBean) super.clone();
// TODO: скопируйте изменяемые состояния здесь, чтобы клон не мог изменить внутренние данные оригинала
return cloned;
}
}
Таким образом, вы можете вызвать клонирование следующим образом:
DummyBean dumtwo = (DummyBean) dum.clone();
Важно помнить, что если ваш класс содержит изменяемые объекты (например, коллекции или пользовательские объекты), вам нужно будет явно создать их копии в методе clone()
, чтобы избежать нежелательных изменений в оригинале при изменении клона.
Как передать объект из одной активности в другую на Android
Как клонировать список, чтобы он не изменялся неожиданно после присваивания?
Инициализация ArrayList в одну строчку
Что значит "Не удалось найти или загрузить основной класс"?
Проверка существования вложенного ключа объекта JavaScript