كيف يمكنك عمل نسخة عميقة من كائن في جافا؟

StackOverflow https://stackoverflow.com/questions/64036

  •  09-06-2019
  •  | 
  •  

سؤال

في جافا، من الصعب بعض الشيء تنفيذ وظيفة نسخ الكائن العميق.ما هي الخطوات التي تتخذها للتأكد من عدم مشاركة الكائن الأصلي والكائن المستنسخ في أي مرجع؟

هل كانت مفيدة؟

المحلول

الطريقة الآمنة هي إجراء تسلسل للكائن ثم إلغاء التسلسل.وهذا يضمن أن كل شيء هو مرجع جديد تمامًا.

وهنا مقال حول كيفية القيام بذلك بكفاءة.

تحفظات:من الممكن أن تتجاوز الفئات التسلسل بحيث تكون المثيلات الجديدة كذلك لا تم إنشاؤه، على سبيل المثال.للمفردات.كما أن هذا بالطبع لا يعمل إذا لم تكن فصولك قابلة للتسلسل.

نصائح أخرى

لقد ذكر عدد قليل من الأشخاص الاستخدام أو التجاوز Object.clone().لا تفعل ذلك. Object.clone() لديه بعض المشاكل الرئيسية، ولا ينصح باستخدامه في معظم الحالات.الرجاء مراجعة البند 11 من "جافا فعالة"بقلم جوشوا بلوخ للحصول على إجابة كاملة.أعتقد أنه يمكنك استخدامها بأمان Object.clone() على صفائف النوع البدائي، ولكن بصرف النظر عن ذلك، يجب أن تكون حكيمًا بشأن استخدام النسخ وتجاوزه بشكل صحيح.

المخططات التي تعتمد على التسلسل (XML أو غير ذلك) غير متقنة.

لا توجد إجابة سهلة هنا.إذا كنت تريد إجراء نسخ عميق لكائن ما، فسيتعين عليك اجتياز الرسم البياني للكائن ونسخ كل كائن فرعي بشكل صريح عبر مُنشئ نسخ الكائن أو طريقة المصنع الثابتة التي بدورها تقوم بنسخ الكائن الفرعي بعمق.غير قابلة للتغيير (على سبيل المثال. Stringق) لا تحتاج إلى نسخ.وبغض النظر عن ذلك، يجب عليك تفضيل الثبات لهذا السبب.

يمكنك عمل نسخة عميقة بالتسلسل دون إنشاء ملفات.

سوف يحتاج الكائن الذي ترغب في نسخه بعمق إلى ذلك 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();

يمكنك إجراء استنساخ عميق قائم على التسلسل باستخدام org.apache.commons.lang3.SerializationUtils.clone(T) في Apache Commons Lang، ولكن كن حذرًا، فالأداء سيئ جدًا.

بشكل عام، من الأفضل كتابة أساليب الاستنساخ الخاصة بك لكل فئة من الكائنات في الرسم البياني للكائنات التي تحتاج إلى الاستنساخ.

إحدى طرق تنفيذ النسخة العميقة هي إضافة مُنشئات النسخ إلى كل فئة مرتبطة.يأخذ منشئ النسخ مثيل "هذا" كوسيطة واحدة وينسخ جميع القيم منه.هناك بعض الأعمال، ولكنها واضحة جدًا وآمنة.

يحرر:لاحظ أنك لا تحتاج إلى استخدام أساليب الوصول لقراءة الحقول.يمكنك الوصول إلى جميع الحقول مباشرة لأن المثيل المصدر يكون دائمًا من نفس نوع المثيل الذي يحتوي على مُنشئ النسخة.واضح ولكن قد يتم التغاضي عنه.

مثال:

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;
    }
}

يحرر:لاحظ أنه عند استخدام مُنشئات النسخ، فأنت بحاجة إلى معرفة نوع وقت تشغيل الكائن الذي تقوم بنسخه.باستخدام الطريقة المذكورة أعلاه، لا يمكنك نسخ قائمة مختلطة بسهولة (قد تتمكن من القيام بذلك باستخدام بعض رموز الانعكاس).

توفر مشاعات Apache طريقة سريعة لاستنساخ الكائن بشكل عميق.

My_Object object2= org.apache.commons.lang.SerializationUtils.clone(object1);

أنت تستطيع استخدم مكتبة يحتوي على واجهة برمجة تطبيقات بسيطة، ويقوم بإجراء استنساخ سريع نسبيًا مع الانعكاس (يجب أن يكون أسرع من طرق التسلسل).

Cloner cloner = new Cloner();

MyClass clone = cloner.deepClone(o);
// clone is a deep-clone of o

يعد XStream مفيدًا حقًا في مثل هذه الحالات.هنا رمز بسيط للقيام بالاستنساخ

private static final XStream XSTREAM = new XStream();
...

Object newObject = XSTREAM.fromXML(XSTREAM.toXML(obj));

أحد الأساليب السهلة والبسيطة للغاية هو استخدام Jackson JSON لإجراء تسلسل لكائنات Java المعقدة إلى JSON وقراءتها مرة أخرى.

http://wiki.fasterxml.com/JacksonInFiveMinutes

استخدم XStream(http://x-stream.github.io/).يمكنك أيضًا التحكم في الخصائص التي يمكنك تجاهلها من خلال التعليقات التوضيحية أو تحديد اسم الخاصية بشكل صريح لفئة XStream.علاوة على ذلك، لا تحتاج إلى تنفيذ واجهة قابلة للاستنساخ.

ل إطار الربيع المستخدمين.باستخدام الطبقة org.springframework.util.SerializationUtils:

@SuppressWarnings("unchecked")
public static <T extends Serializable> T clone(T object) {
     return (T) SerializationUtils.deserialize(SerializationUtils.serialize(object));
}

لا يمكن إجراء النسخ العميق إلا بموافقة كل فئة.إذا كان لديك التحكم في التسلسل الهرمي للفئة، فيمكنك تنفيذ الواجهة القابلة للاستنساخ وتنفيذ طريقة الاستنساخ.بخلاف ذلك، من المستحيل إجراء نسخة عميقة بأمان لأن الكائن قد يشارك أيضًا موارد غير البيانات (على سبيل المثال.اتصالات قاعدة البيانات).بشكل عام، يعتبر النسخ العميق ممارسة سيئة في بيئة Java ويجب تجنبه من خلال ممارسات التصميم المناسبة.

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));
    }
}

بالنسبة للكائنات المعقدة وعندما لا يكون الأداء مهمًا، أستخدم مكتبة json، مثل com.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);
}

إستعملت البلدوزر لاستنساخ كائنات جافا وهو أمر رائع في ذلك، كريو المكتبة هي بديل رائع آخر.

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 الخاصة بك تنفيذ واجهة قابلة للتسلسل

BeanUtils يقوم بعمل جيد حقًا في استنساخ الفاصوليا العميقة.

BeanUtils.cloneBean(obj);
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top