Как мне скопировать объект в Java?
Рассмотрим код ниже:
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
?
24 ответа
Создайте конструктор копирования:
class DummyBean {
private String dummy;
public DummyBean(DummyBean another) {
this.dummy = another.dummy; // you can access
}
}
Каждый объект имеет также метод клонирования, который можно использовать для копирования объекта, но не используйте его. Слишком легко создать класс и использовать неправильный метод клонирования. Если вы собираетесь это сделать, прочитайте хотя бы то, что сказал об этом Джошуа Блох в Effective Java.
Базовый: Копирование объектов в Java.
Давайте предположим, что объект obj1
, который содержит два объекта, содержащихся в Obj1 и содержащем Obj2.
мелкое копирование:
мелкое копирование создает новый instance
того же класса и копирует все поля в новый экземпляр и возвращает его. Класс объекта обеспечивает clone
метод и обеспечивает поддержку мелкого копирования.
Глубокое копирование:
Глубокое копирование происходит, когда объект копируется вместе с объектами, на которые он ссылается. Ниже изображение показывает obj1
после того, как глубокая копия была выполнена на нем. Не только имеет obj1
были скопированы, но содержащиеся в нем объекты также были скопированы. Мы можем использовать Java Object Serialization
сделать глубокую копию. К сожалению, у этого подхода есть некоторые проблемы ( подробные примеры).
Возможные проблемы: clone
сложно реализовать правильно.
Лучше использовать защитное копирование, конструкторы копирования (как ответ @egaga) или статические фабричные методы.
- Если у вас есть объект, который вы знаете, есть публичный
clone()
метод, но вы не знаете тип объекта во время компиляции, то у вас есть проблемы. Java имеет интерфейс под названиемCloneable
, На практике мы должны реализовать этот интерфейс, если мы хотим сделать объектCloneable
,Object.clone
защищен, поэтому мы должны переопределить его публичным методом, чтобы он был доступен. - Другая проблема возникает при попытке глубокого копирования сложного объекта. Предположим, что
clone()
Метод всех переменных объекта-члена также делает глубокое копирование, это слишком рискованно для предположения. Вы должны контролировать код во всех классах.
Например, у http://commons.apache.org/lang/api-2.5/org/apache/commons/lang/SerializationUtils.html будет метод для глубокого клонирования с использованием сериализации ( Source). Если нам нужно клонировать Bean, то в org.apache.commons.beanutils ( Source) есть несколько служебных методов.
cloneBean
Будет клонировать компонент на основе доступных методов получения и установки свойств, даже если сам класс компонента не реализует Cloneable.copyProperties
Скопирует значения свойств из исходного объекта в целевой компонент для всех случаев, когда имена свойств совпадают.
В упаковке import org.apache.commons.lang.SerializationUtils;
есть метод:
SerializationUtils.clone(Object);
Пример:
this.myObjectCloned = SerializationUtils.clone(this.object);
Просто следуйте инструкциям ниже:
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;
}
}
Это действительно просто.
РЕДАКТИРОВАТЬ: включить дочерний объект с помощью рекурсии
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
,
Проверьте эту статью вики о копировании объектов.
Глубокое клонирование - это ваш ответ, который требует реализации 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();
Добавлять Cloneable
и ниже код для вашего класса
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
Использовать этот clonedObject = (YourClass) yourClassObject.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
Это медленно и сломано. Написать конструктор копирования для лучшей производительности ref.
Что-то вроде
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 итераций:
Линия UserAccount clone = gson.fromJson(gson.toJson(aO), UserAccount.class);
занимает 808 мс
Линия UserAccount clone = new UserAccount(aO);
занимает менее 1 мс
Вывод: используйте gson, если ваш босс сумасшедший и вы предпочитаете скорость. Используйте второй конструктор копирования, если вы предпочитаете качество.
Вы также можете использовать плагин генератора кода конструктора копирования в Android Studio.
Вот достойное объяснение clone()
если вам это понадобится...
Используйте утилиту глубокого клонирования:
SomeObjectType copy = new Cloner().deepClone(someObject);
Это позволит глубоко скопировать любой Java-объект, проверьте его на https://github.com/kostaskougios/cloning
Альтернатива методу копирования конструктора egaga. Вероятно, у вас уже есть POJO, поэтому просто добавьте еще один методcopy()
который возвращает копию инициализированного объекта.
class DummyBean {
private String dummyStr;
private int dummyInt;
public DummyBean(String dummyStr, int dummyInt) {
this.dummyStr = dummyStr;
this.dummyInt = dummyInt;
}
public DummyBean copy() {
return new DummyBean(dummyStr, dummyInt);
}
//... Getters & Setters
}
Если у вас уже есть DummyBean
и хотите копию:
DummyBean bean1 = new DummyBean("peet", 2);
DummyBean bean2 = bean1.copy(); // <-- Create copy of bean1
System.out.println("bean1: " + bean1.getDummyStr() + " " + bean1.getDummyInt());
System.out.println("bean2: " + bean2.getDummyStr() + " " + bean2.getDummyInt());
//Change bean1
bean1.setDummyStr("koos");
bean1.setDummyInt(88);
System.out.println("bean1: " + bean1.getDummyStr() + " " + bean1.getDummyInt());
System.out.println("bean2: " + bean2.getDummyStr() + " " + bean2.getDummyInt());
Выход:
bean1: peet 2 bean2: peet 2 bean1: koos 88 bean2: peet 2
Но оба работают хорошо, в конечном счете, решать вам...
Использовать gson
для копирования объекта.
public static <T>T copyObject(Object object){
Gson gson = new Gson();
JsonObject jsonObject = gson.toJsonTree(object).getAsJsonObject();
return gson.fromJson(jsonObject,(Type) object.getClass());
}
Предположим, у меня есть объект person
.Так
Person copyPerson = copyObject(person);
Передайте объект, который вы хотите скопировать, и получите нужный объект:
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
к желаемому объекту.
Удачного кодирования!
Для этого вам нужно каким-то образом клонировать объект. Хотя в Java есть механизм клонирования, не используйте его, если это не нужно. Создайте метод копирования, который работает для вас, а затем выполните:
dumtwo = dum.copy();
Вот еще несколько советов по различным методам выполнения копирования.
Помимо явного копирования, другой подход заключается в том, чтобы сделать объект неизменным (нет set
или другие методы мутатора). Таким образом, вопрос никогда не возникает. Неизменяемость становится более сложной для более крупных объектов, но другой стороной этого является то, что она толкает вас в направлении разделения на когерентные мелкие объекты и композиты.
class DB {
private String dummy;
public DB(DB one) {
this.dummy = one.dummy;
}
}
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();
Вы можете глубоко копировать автоматически с 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));
При этом у вас есть копия без необходимости реализации какого-либо интерфейса клонирования.
Использование функции расширения Kotlin
fun <T : Any?> T.duplicate(): T? {
var copyObject: T? = null
try {
val byteArrayOutputStream = ByteArrayOutputStream()
val objectOutputStream = ObjectOutputStream(byteArrayOutputStream)
objectOutputStream.writeObject(this)
objectOutputStream.flush()
objectOutputStream.close()
byteArrayOutputStream.close()
val byteData = byteArrayOutputStream.toByteArray()
val byteArrayInputStream = ByteArrayInputStream(byteData)
try {
copyObject = ObjectInputStream(byteArrayInputStream).readObject() as T
} catch (e: ClassNotFoundException) {
e.printStackTrace()
}
} catch (e: IOException) {
e.printStackTrace()
}
return copyObject
}
Пример использования
var object = Any()
var duplicateObject = object.duplicate()
Ява
<T extends Object> T copyObject(T sourceObject) {
T copyObject = null;
try {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(sourceObject);
objectOutputStream.flush();
objectOutputStream.close();
byteArrayOutputStream.close();
byte[] byteData = byteArrayOutputStream.toByteArray();
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteData);
try {
copyObject = (T) new ObjectInputStream(byteArrayInputStream).readObject();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
return copyObject;
}
Пример использования
Object object = new Object();
Object duplicateObject = copyObject(object);
Вы можете попробовать реализовать Cloneable
и использовать clone()
Способ; однако, если вы используете метод клонирования, вы должны - по умолчанию - ВСЕГДА переопределять Object
"s public Object 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();