Рассмотрим следующий код:
DummyBean dum = new DummyBean();
dum.setDummy("foo");
System.out.println(dum.getDummy()); // prints 'foo'
DummyBean dumtwo = dum;
System.out.println(dumtwo.getDummy()); // prints 'foo'
dum.setDummy("bar");
System.out.println(dumtwo.getDummy()); // prints 'bar' but it should print 'foo'
Итак, я хочу скопировать dum
на dumtwo
и изменить dum
не затрагивая dumtwo
. Но вышеприведенный код не делает этого. Когда я что-то меняю в dum
, то такое же изменение происходит и в dumtwo
.
Я думаю, когда я говорю dumtwo = dum
, Java копирует только ссылку. Итак, есть ли способ создать новую копию dum
и присвоить ей dumtwo
?
Создайте конструктор копирования:
class DummyBean {
private String dummy;
public DummyBean(DummyBean another) {
this.dummy = another.dummy; // you can access
}
}
Каждый объект имеет также метод клонирования, который можно использовать для копирования объекта, но не использовать его. Слишком легко создать класс и сделать неправильный метод клонирования. Если вы собираетесь это сделать, прочитайте, по крайней мере, то, что Джошуа Блох должен сказать об этом в Эффективная Java.
Основные: Копирование объектов на Java.
Предположим, что объект- obj1
, содержащий два объекта, содержит Obj1 и , содержащий Obj2.
мелкое копирование:
мелкое копирование создает новый instance
того же класса и копирует все поля в новый экземпляр и возвращает его. Класс объекта предоставляет метод clone
и обеспечивает поддержку мелкого копирования.
Глубокое копирование:
Глубокая копия возникает, когда объект копируется вместе с объектами, к которым он относится. Ниже изображения отображается obj1
после того, как на нем была выполнена глубокая копия. Скопировано не только obj1
, но и объекты, содержащиеся в нем, были скопированы. Мы можем использовать Java Object Serialization
, чтобы сделать глубокую копию. К сожалению, этот подход также имеет некоторые проблемы (подробные примеры).
Возможные проблемы: clone
сложно реализовать правильно.
Лучше использовать Оборонительное копирование, конструкторы копирования (как @egaga reply) или статические методы factory.
clone()
, но вы не знаете тип объекта во время компиляции, тогда у вас есть проблема. Java имеет интерфейс под названием Cloneable
. На практике мы должны реализовать этот интерфейс, если хотим создать объект Cloneable
. Object.clone
защищен, поэтому мы должны переопределить его общедоступным методом, чтобы он был доступен. clone()
всех переменных-членов-членов также делает глубокую копию, это слишком рискованно из предположения. Вы должны контролировать код во всех классах. Например org.apache.commons.lang.SerializationUtils будет иметь метод для Deep clone с использованием сериализации (Source). Если нам нужно клонировать Bean, тогда в org.apache.commons.beanutils есть несколько методов утилиты (Источник).
cloneBean
будет клонировать Bean на основе доступных свойств getters и seters, даже если сам класс Bean не реализует Cloneable.copyProperties
скопирует значения свойств из источника Bean в пункт назначения Bean для всех случаев, когда имена свойств совпадают.В пакете import org.apache.commons.lang.SerializationUtils;
есть метод:
SerializationUtils.clone(Object);
Пример:
this.myObjectCloned = SerializationUtils.clone(this.object);
Serializable
Просто следуйте ниже:
public class Deletable implements Cloneable{
private String str;
public Deletable(){
}
public void setStr(String str){
this.str = str;
}
public void display(){
System.out.println("The String is "+str);
}
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
и везде, где вы хотите получить другой объект, просто выполните клонирование. например:
Deletable del = new Deletable();
Deletable delTemp = (Deletable ) del.clone(); // this line will return you an independent
// object, the changes made to this object will
// not be reflected to other object
Почему нет ответа на использование Reflection 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;
}
}
Это действительно просто.
EDIT: включить дочерний объект через рекурсию
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(field.get(obj)));
}
}
}
return clone;
}catch(Exception e){
return null;
}
}
Я использую библиотеку Google JSON для сериализации, а затем создаю новый экземпляр сериализованного объекта. Он делает глубокую копию с несколькими ограничениями:
не может быть рекурсивных ссылок
он не будет копировать массивы разрозненных типов
массивы и списки должны быть напечатаны или он не найдет класс для создания экземпляра
вам может потребоваться инкапсуляция строк в класс, который вы объявляете сами
Я также использую этот класс для сохранения пользовательских настроек, окон и необходимости перезагрузки во время выполнения. Он очень прост в использовании и эффективен.
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;
}
}
Да, вы просто делаете ссылку на объект. Вы можете клонировать объект, если он реализует Cloneable
.
Ознакомьтесь с этой статьей wiki о копировании объектов.
Добавьте Cloneable
и ниже код в свой класс
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
Используйте этот clonedObject = (YourClass) yourClassObject.clone();
Вот достойное объяснение clone()
, если вам это нужно...
Это тоже работает. Предполагаемая модель
class UserAccount{
public int id;
public String name;
}
Сначала добавьте compile 'com.google.code.gson:gson:2.8.1'
в ваше приложение> gradle & sync. затем
Gson gson = new Gson();
updateUser = gson.fromJson(gson.toJson(mUser),UserAccount.class);
Вы можете исключить использование поля с помощью transient
ключевого слова после модификатора доступа.
Примечание. Это плохая практика. Также не рекомендуется использовать Cloneable
или JavaSerialization
Он медленный и сломанный. Написать конструктор копирования для лучшей производительности исх.
Что-то вроде
class UserAccount{
public int id;
public String name;
//empty constructor
public UserAccount(){}
//parameterize constructor
public UserAccount(int id, String name) {
this.id = id;
this.name = name;
}
//copy constructor
public UserAccount(UserAccount in){
this(in.id,in.name);
}
}
Испытательная статистика итерации 90000:
Line UserAccount clone = gson.fromJson(gson.toJson(aO), UserAccount.class);
занимает 808 мс
Line UserAccount clone = new UserAccount(aO);
занимает менее 1 мс
Вывод: используйте gson, если ваш босс сумасшедший, и вы предпочитаете скорость. Используйте второй конструктор копий, если вы предпочитаете качество.
Вы также можете использовать плагин генерации кода конструктора кода в Android Studio.
Глубокое клонирование - это ваш ответ, который требует реализации интерфейса 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();
cloned.setDummy(cloned.getDummy());
// the above is applicable in case of primitive member types,
// however, in case of non primitive types
// cloned.setNonPrimitiveType(cloned.getNonPrimitiveType().clone());
return cloned;
}
}
Вы назовете это так
DummyBean dumtwo = dum.clone();
dummy
, String
, является неизменной, вам не нужно копировать ее
Чтобы сделать это, вы должны каким-то образом клонировать объект. Хотя Java имеет механизм клонирования, не используйте его, если вам это не нужно. Создайте метод копирования, который выполняет копирование для вас, а затем выполните:
dumtwo = dum.copy();
Здесь есть еще несколько советов по различным методам выполнения копии.
Используйте утилиту глубокого клонирования:
SomeObjectType copy = new Cloner().deepClone(someObject);
Это будет глубоко скопировать любой Java-объект, проверьте его на https://github.com/kostaskougios/cloning
Помимо явного копирования, другой подход заключается в том, чтобы сделать объект неизменным (no set
или другие методы мутатора). Таким образом, вопрос никогда не возникает. Неизменность становится более сложной с более крупными объектами, но другая сторона заключается в том, что она толкает вас в направлении расщепления на когерентные мелкие объекты и композиты.
class DB {
private String dummy;
public DB(DB one) {
this.dummy = one.dummy;
}
}
Передайте объект, который вы хотите скопировать, и получите объект, который вы хотите,
private Object copyObject(Object objSource) {
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(objSource);
oos.flush();
oos.close();
bos.close();
byte[] byteData = bos.toByteArray();
ByteArrayInputStream bais = new ByteArrayInputStream(byteData);
try {
objDest = new ObjectInputStream(bais).readObject();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
return objDest;
}
Теперь проанализируйте объект objDest с объектом, находящимся в поиске.
Счастливое кодирование
Вы можете выполнить глубокую копию с помощью XStream, http://x-stream.github.io/:
XStream - простая библиотека для сериализации объектов в XML и обратно еще раз.
Добавьте его в свой проект (при использовании maven)
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.3.1</version>
</dependency>
Тогда
DummyBean dum = new DummyBean();
dum.setDummy("foo");
DummyBean dumCopy = (DummyBean) XSTREAM.fromXML(XSTREAM.toXML(dum));
При этом у вас есть копия без необходимости реализовывать какой-либо интерфейс клонирования.
java.beans.XMLEncoder
для стандартного Java API, который также сериализуется в XML (хотя и не для целей глубокого копирования).
Вы можете попробовать реализовать Cloneable
и использовать метод clone()
; однако, если вы используете метод clone, вы должны - по стандарту - ВСЕГДА переопределять метод Object
public Object clone()
.
public class MyClass implements Cloneable {
private boolean myField= false;
// and other fields or objects
public MyClass (){}
@Override
public MyClass clone() throws CloneNotSupportedException {
try
{
MyClass clonedMyClass = (MyClass)super.clone();
// if you have custom object, then you need create a new one in here
return clonedMyClass ;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return new MyClass();
}
}
}
и в вашем коде:
MyClass myClass = new MyClass();
// do some work with this object
MyClass clonedMyClass = myClass.clone();
Если вы можете добавить аннотацию к исходному файлу, то обработчик аннотации или генератор кода, например этот может быть б.
import net.zerobuilder.BeanBuilder
@BeanBuilder
public class DummyBean {
// bean stuff
}
Будет создан класс DummyBeanBuilders
, у которого есть статический метод dummyBeanUpdater
для создания неглубоких копий, так же, как вы делали бы это вручную.
DummyBean bean = new DummyBean();
// Call some setters ...
// Now make a copy
DummyBean copy = DummyBeanBuilders.dummyBeanUpdater(bean).done();