Derin bir nesne kopyalama işlevi uygulamak biraz zor. Orijinal nesnenin ve klonlanan nesnenin referans paylaşmamasını sağlamak için hangi adımları attınız?
Derin bir nesne kopyalama işlevi uygulamak biraz zor. Orijinal nesnenin ve klonlanan nesnenin referans paylaşmamasını sağlamak için hangi adımları attınız?
Yanıtlar:
Güvenli bir yol, nesneyi serileştirmek, ardından serisini kaldırmaktır. Bu, her şeyin yepyeni bir referans olmasını sağlar.
İşte bunun nasıl verimli bir şekilde yapılacağı hakkında bir makale .
Uyarılar: sınıflar yeni örneklerini öyle ki seri geçersiz kılmak için Mümkün değil singletons için örneğin yarattı. Ayrıca, dersleriniz Serileştirilebilir değilse, bu elbette işe yaramaz.
Birkaç kişi kullandığını veya geçersiz kıldığını belirtti Object.clone()
. Yapma. Object.clone()
bazı büyük sorunları vardır ve kullanımı çoğu durumda önerilmez. Eksiksiz bir cevap için lütfen Joshua Bloch'un " Etkili Java " ndan Madde 11'e bakınız . Object.clone()
İlkel tip dizilerde güvenle kullanabileceğinize inanıyorum , ancak bunun dışında klonu düzgün bir şekilde kullanma ve geçersiz kılma konusunda dikkatli olmanız gerekiyor.
Serileştirmeye (XML veya başka türlü) dayanan şemalar kludgy'dir.
Burada kolay bir cevap yok. Bir nesneyi derinden kopyalamak istiyorsanız, nesne grafiğini geçmeniz ve her alt nesneyi, nesnenin kopya yapıcısı ya da alt nesneyi derin kopyalayan statik bir fabrika yöntemiyle açıkça kopyalamanız gerekecektir. Dokunulmazların (örn. String
) Kopyalanması gerekmez. Bir yana, bu nedenle değişmezliği tercih etmelisiniz.
Dosya oluşturmadan serileştirme ile derin bir kopya oluşturabilirsiniz.
Derin kopyalamak istediğiniz nesneye ihtiyacınız olacak implement serializable
. Sınıf son değilse veya değiştirilemezse, sınıfı genişletin ve serileştirilebilir uygulayın.
Sınıfınızı bir bayt akışına dönüştürün:
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(object);
oos.flush();
oos.close();
bos.close();
byte[] byteData = bos.toByteArray();
Sınıfınızı bir bayt akışından geri yükleyin:
ByteArrayInputStream bais = new ByteArrayInputStream(byteData);
(Object) object = (Object) new ObjectInputStream(bais).readObject();
instance
bu durumda?
org.apache.commons.lang3.SerializationUtils.clone(T)
Apache Commons Lang'da kullanarak serileştirmeye dayalı derin bir klon yapabilirsiniz , ancak dikkatli olun - performans uçsuz bucaksız.
Genel olarak, klonlamaya ihtiyaç duyan nesne grafiğinde bir nesnenin her sınıfı için kendi klon yöntemlerinizi yazmak en iyi uygulamadır.
org.apache.commons.lang.SerializationUtils
Derin kopyayı uygulamanın bir yolu, ilişkili her sınıfa kopya yapıcıları eklemektir. Bir kopya oluşturucu tek argümanı olarak "this" in bir örneğini alır ve ondan tüm değerleri kopyalar. Oldukça fazla iş, ama oldukça basit ve güvenli.
EDIT: alanları okumak için erişimci yöntemlerini kullanmanız gerekmediğini unutmayın. Tüm örneklere doğrudan erişebilirsiniz, çünkü kaynak örnek her zaman kopya oluşturucudaki örnekle aynı türdedir. Açık ama göz ardı edilebilir.
Misal:
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;
}
}
Düzenleme: Kopya oluşturucuları kullanırken kopyaladığınız nesnenin çalışma zamanı türünü bilmeniz gerektiğini unutmayın. Yukarıdaki yaklaşımla, karışık bir listeyi kolayca kopyalayamazsınız (bazı yansıma koduyla yapabilirsiniz).
Toyota
, kodunuz Car
hedef listesine bir koyar . Uygun klonlama genellikle sınıfın sözleşmesi kendi sınıfının yeni bir nesnesini döndüreceğini bildiren sanal bir fabrika yöntemi sağlamasını gerektirir; kopya yapıcısının kendisi protected
, yalnızca kesin türü kopyalanan nesnenin nesnesiyle eşleşen nesneler oluşturmak için kullanılmasını sağlamak olmalıdır ).
Sen edebilir bir kütüphane kullanmak basit bir API vardır ve nispeten hızlı yansıma ile klonlama gerçekleştirdiği (daha hızlı seri yöntemlerine göre daha olmalıdır).
Cloner cloner = new Cloner();
MyClass clone = cloner.deepClone(o);
// clone is a deep-clone of o
Apache commons, bir nesneyi derin klonlamak için hızlı bir yol sunar.
My_Object object2= org.apache.commons.lang.SerializationUtils.clone(object1);
XStream bu gibi durumlarda gerçekten yararlıdır. İşte klonlama yapmak için basit bir kod
private static final XStream XSTREAM = new XStream();
...
Object newObject = XSTREAM.fromXML(XSTREAM.toXML(obj));
Çok kolay ve basit bir yaklaşım, karmaşık Java Nesnesini JSON'a serileştirmek ve tekrar okumak için Jackson JSON kullanmaktır.
İçin Bahar Çerçeve kullanıcıları. Sınıfı kullanma org.springframework.util.SerializationUtils
:
@SuppressWarnings("unchecked")
public static <T extends Serializable> T clone(T object) {
return (T) SerializationUtils.deserialize(SerializationUtils.serialize(object));
}
Karmaşık nesneler için ve performans önemli olmadığında, json metnine nesneyi serileştirmek için gson gibi bir json kütüphanesi kullanırım , ardından yeni nesneyi almak için metnin serileştirilmesini sağlayın .
yansımaya dayalı gson çoğu durumda çalışır, ancak transient
alanlar kopyalanmayacak ve sebepli dairesel referanslı nesneler olacaktır 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);
}
XStream kullanın ( http://x-stream.github.io/ ). Ek açıklamalarla veya özellik adını XStream sınıfına açıkça belirterek hangi özellikleri göz ardı edebileceğinizi bile kontrol edebilirsiniz. Dahası, klonlanabilir arayüz uygulamanıza gerek yoktur.
Derin kopyalama yalnızca her sınıfın rızasıyla yapılabilir. Sınıf hiyerarşisi üzerinde denetiminiz varsa, klonlanabilir arabirimi uygulayabilir ve Klon yöntemini uygulayabilirsiniz. Aksi takdirde, nesne veri olmayan kaynakları da (örneğin, veritabanı bağlantıları) paylaşıyor olabileceğinden, derin bir kopya yapmak güvenli bir şekilde yapılamaz. Bununla birlikte, genel olarak, derin kopyalama Java ortamında kötü uygulama olarak kabul edilir ve uygun tasarım uygulamaları ile bundan kaçınılmalıdır.
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));
}
}
BeanUtils derin klonlama fasulye gerçekten iyi bir iş yapar.
BeanUtils.cloneBean(obj);
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);
Burada MyPerson ve MyAddress sınıfınız serilazable arabirimi uygulamalıdır
Nesneyi serileştirmek ve serisini kaldırmak için Jackson'ı kullanma. Bu uygulama, nesnenin Serializable sınıfını uygulamasını gerektirmez.
<T> T clone(T object, Class<T> clazzType) throws IOException {
final ObjectMapper objMapper = new ObjectMapper();
String jsonStr= objMapper.writeValueAsString(object);
return objMapper.readValue(jsonStr, clazzType);
}