مقدمة
Java Cloning (Clone) هي واحدة من ميزات لغة Java ، ولكن من النادر استخدامها في الممارسة العملية. ولكن في بعض الأحيان يكون الاستنساخ أكثر ملاءمة وكفاءة.
بالنسبة للاستنساخ ، لدى Java بعض القيود:
1. يجب على الفئة المستنسخة تنفيذ الواجهة المستنسخة نفسها للإشارة إلى أن طريقة Object.clone() . الواجهة المستنسخة هي في الواقع واجهة تعريف وليس لها طريقة واجهة.
2. يجب أن تخطى الفئات التي تنفذ الواجهة المستنسخة Object.clone . من المستحيل على كائن استنساخه بعد تنفيذ هذه الواجهة. حتى لو تسمى طريقة الاستنساخ بشكل عكسي ، فلا يوجد ضمان بأنه سينجح.
3. يتم تعريف طريقة الاستنساخ في فئة Java.lang.Object على النحو التالي:
استنساخ الكائن المحمي () يلقي clonenotsupportedException
يخلق وإرجاع نسخة من هذا الكائن. إنه يشير إلى أنها طريقة محمية ، مرئية في نفس الحزمة.
عن طريق الاتفاقية ، يجب الحصول على الكائن الذي تم إرجاعه عن طريق استدعاء super.clone .
مهمة في جافا
في Java ، يتم استخدام المهمة بشكل شائع جدًا ، وهي مهمة بسيطة على النحو التالي
// النوع الأصلي int a = 1 ؛ int b = a ؛ // نوع المرجع سلسلة [] أيام الأسبوع = سلسلة جديدة [5]
في الكود أعلاه.
1. إذا كان هذا هو نوع البيانات الأصلي ، فإن القيمة التي تم تمريرها بواسطة المهمة هي القيمة الحقيقية.
2. إذا كان نوع بيانات مرجعية ، فإن المهمة تمرر المرجع إلى الكائن ، وليس الكائن.
إن فهم الفرق بين أنواع البيانات وأنواع المرجعية يجعل من السهل علينا فهم الاستنساخ.
استنساخ
في Java ، استنساخ هو عملية نسخ كائن موجود إلى ذاكرة إلى كائن آخر هو نفسه. الاستنساخ في Java هو نسخة من المجال.
إذا كنت ترغب في دعم أساليب الاستنساخ في Java ، فأنت بحاجة أولاً إلى تنفيذ الواجهة المستنسخة
استنساخ هو في الواقع غريب بعض الشيء. إنه مختلف عن الواجهة التي نستخدمها غالبًا. لا يحتوي على أي طرق في الداخل ، فهي مجرد واجهة وضع علامة.
رمز المصدر كما يلي
الواجهة العامة استنساخ {}حول استنساخ ، ما ينبغي الانتباه إليه
1. إذا كنت ترغب في دعم استنساخ ، فأنت بحاجة إلى تنفيذ الواجهة المستنسخة
2. إذا لم يتم استدعاء طريقة الاستنساخ إلى الواجهة المستنسخة ، فسيتم طرح clonenotsupportedException.
ثم أعد كتابة طريقة الاستنساخ وتعديلها إلى مستوى الوصول العام
الطبقة الثابتة clonableimimive تنفذ clonable {public int count ؛ الطفل العام Override Public Object Clone () يلقي clonenotsupportedException {return super.clone () ؛ }}استدعاء طريقة استنساخ لنسخ الكائن
clonableimim imp1 = new clonableimim () ؛ imp1.Child = New Child ("Andy") ؛ Try {Object obj = imp1.clone () ؛ clonableimim imp2 = (clonableimim) obj ؛ System.out.println ("Main Imp2.child.name =" + IMP2.CHILD.NAME) ؛} catch (clonenotsupportedException e) {E.PrintStackTrace () ؛}نسخة خفيفة
الاستنساخ المنفذ في الكود أعلاه هو في الواقع نسخة ضحلة.
ما يجب أن تعرفه عن نسخة ضحلة
1. استخدم طريقة الاستنساخ الافتراضية
2. قم بعمل نسخة قيمة من حقل البيانات الأصلي
3. نسخ المراجع فقط لأنواع المرجع
4. التنفيذ السريع والكفاءة العالية
5. لا يمكن فصل البيانات بنسبة 100 ٪.
6. إذا كان الكائن يحتوي فقط على مجال البيانات الأصلي أو مجال الكائن غير القابل للتغيير ، فمن المستحسن استخدام نسخة ضحلة.
فيما يتعلق بعدم القدرة على فصل البيانات ، يمكننا استخدام هذا الرمز للتحقق منه.
clonableimim imp1 = new clonableimim () ؛ imp1.Child = New Child ("Andy") ؛ Try {Object obj = imp1.clone () ؛ clonableimim imp2 = (clonableimim) obj ؛ IMP2.CHILD.NAME = "BOB" ؛ System.out.println ("Main Imp1.Child.name =" + IMP1.CHILD.NAME) ؛} catch (clonenotsupportedException e) {E.PrintStackTrace () ؛} في الكود أعلاه ، استخدمنا طريقة استنساخ IMP1 لاستنساخ IMP2 ، ثم تعديل imp2.child.name إلى BOB ، ثم طباعة imp1.child.name للحصول على النتيجة.
Main Imp1.Child.name = Bob
والسبب هو أن النسخة الضحلة لا تحقق فصلًا بنسبة 100 ٪ للبيانات. يشارك IMP1 و IMP2 نفس كائن الطفل ، لذلك سيؤثر التعديل على الآخر.
نسخة عميقة
يمكن للنسخة العميقة حل مشكلة فصل البيانات بنسبة 100 ٪. فقط قم بإجراء بعض التعديلات على الكود أعلاه.
1. الطفل ينفذ الواجهة المستنسخة.
طبقة الطبقة العامة تنفذ استنساخ {اسم السلسلة العامة ؛ الطفل العام (اسم السلسلة) {this.name = name ؛ } Override Public String ToString () {return "child [name =" + name + "]" ؛ } override كائن محمي clone () يلقي clonenotsupportedException {return super.clone () ؛ }}2. أعد كتابة طريقة الاستنساخ واستدعاء طريقة استنساخ مجال البيانات.
الطبقة الثابتة clonableimimive تنفذ clonable {public int count ؛ الطفل العام Override public Object Clone () يلقي clonenotsupportedException {clonableimimp obj = (clonableimp) super.clone () ؛ OBJ.CHILD = (الطفل) child.clone () ؛ إرجاع OBJ ؛ }} عندما نقوم بتعديل imp2.child.name مرة أخرى ، فلن يؤثر ذلك على قيمة imp1.child.name ، لأن كل من IMP1 و IMP2 لهما كائنات طفلهما الخاصة ، لأن البيانات معزولة بنسبة 100 ٪.
بعض ميزات نسخة عميقة
1. تحتاج إلى تجاوز طريقة الاستنساخ ، ليس فقط استدعاء طريقة الفئة الأصل ، ولكن أيضًا استدعاء طريقة استنساخ السمة.
2. يتم تحقيق فصل بيانات 100 ٪ بين الكائن الأصلي والكائن المستنسخ
3. إذا كان للكائن سمة نوع مرجع ، يوصى باستخدام نسخة عميقة.
4. نسخة عميقة تستغرق وقتًا طويلاً وأقل كفاءة من النسخة الضحلة
لماذا تستخدم الاستنساخ
إنه أمر مهم للغاية وشائع: يحتاج واجهة برمجة التطبيقات إلى توفير مجموعة قائمة ، لكنها لا تريد أن يؤثر تعديل المتصل على تغييراته الخاصة ، لذلك يحتاج إلى استنساخ كائن لتحقيق غرض عزل البيانات.
يجب تجنب العبودية قدر الإمكان
1. عادةً ما يتم تنفيذ الواجهة لإظهار ما يمكن أن تفعله الفصل لعملائها ، في حين أن الاستنساخ هو مجرد واجهة علامة ، كما أنه يغير سلوك الطريقة المحمية باليد في الفئة الفائقة. إنه استخدام غير نمطي للغاية للواجهة ولا يستحق التقليد.
2. وصف Javadoc لاتفاقية طريقة الاستنساخ وطريقة استنساخه الهشة غامضة بعض الشيء ، على النحو التالي: اتفاقية Java SE8
تقوم طريقة Clone بإنشاء نسخة من الكائن وإرجاعها. يعتمد المعنى الدقيق للنسخة على فئة الكائن. المعنى العام هو أنه لأي كائن x ، التعبير
x.clone() != x 为true x.clone().getClass() == x.getClass() يعيد أيضًا صحيحًا ، ولكنه غير مطلوب x.clone().equals(x)
التعبيرات الثانية والثالثة أعلاه يعود بسهولة خاطئة. لذلك ، فإن الشيء الوحيد الذي يمكن أن يضمن صحيحًا دائمًا هو التعبير ، أي أن الكائنين هما كائنات مستقلة.
3. المجال النهائي للكائن المتغير. في طريقة الاستنساخ ، إذا كنا بحاجة إلى نسخ المجال النهائي للكائن المتغير ، فمن المستحيل في الواقع تجميع ومرور بسبب القيود النهائية. لذلك ، من أجل تنفيذ الاستنساخ ، نحتاج إلى التفكير في التخلي عن الكلمة الرئيسية النهائية لمجال الكائن القابل للتغيير.
4. سلامة مؤشر الترابط إذا قررت استخدام فئات آمنة مؤشرات الترابط لتنفيذ الواجهة المستنسخة ، فأنت بحاجة إلى التأكد من مزامنة طريقة استنساخها. لا يتم مزامنة طريقة Object.clone الافتراضي.
بشكل عام ، فإن طريقة الاستنساخ في Java ليست مثالية بالفعل ، لذلك يوصى بتجنب استخدامه قدر الإمكان. فيما يلي بعض البدائل.
نسخ المنشئين
يمكن أيضًا تنفيذ كائنات نسخ باستخدام مُنشئ نسخ.
1. نسخ مُنشئ هو أيضًا نوع من المنشئ
2. يتم قبول معلمة واحدة فقط ، نوع المعلمة هو الفئة الحالية
3. الغرض من ذلك هو إنشاء كائن جديد مع نفس المعلمات مثل
4. ميزة منشئ النسخ عبر طريقة الاستنساخ هي أنها بسيطة وسهلة التنفيذ.
رمز مثال باستخدام منشئ نسخ
سيارة الطبقة العامة {عجلة العجلات ؛ الشركة المصنعة السيارة العامة (عجلة العجلات ، الشركة المصنعة) {this.wheel = wheel ؛ this.manufacturer = الشركة المصنعة ؛ } // Copy Constructor Public Car (Car Car) {this (Car.Wheel ، Car.Manufacturer) ؛ } عجلة الفئة الثابتة العامة {سلسلة العلامة التجارية ؛ }}لاحظ أنه يتم تنفيذ الرمز أعلاه كنسخة ضحلة. إذا كنت ترغب في تنفيذ نسخة عميقة ، يرجى الرجوع إلى الرمز التالي.
// copy Constructorpublic Car (Car Car) {Wheel Wheel = New Wheel () ؛ wheel.brand = car.wheel.brand ؛ this.wheel = عجلة ؛ this.manufacturer = car.manufacturer ؛}لمزيد من الراحة ، يمكننا أيضًا إضافة طريقة ثابتة إلى الفئة أعلاه
السيارة الثابتة العامة NewInstance (سيارة) {إرجاع سيارة جديدة (سيارة) ؛}استخدم مسلسلًا لتحقيق نسخة عميقة
في الواقع ، يمكن تحقيق نسخة عميقة من الكائنات باستخدام التسلسل. الرمز الموجز على النحو التالي
الطبقة العامة deepcopyexample تنفس Serializable {private static final long serialversionuid = 6098694917984051357l ؛ الطفل العام public deepcopyexample copy () {deepcopyexample copy = null ؛ حاول {bytearrayoutputstream baos = new bytearrayoutputstream () ؛ ObjectOutputStream OOS = جديد ObjectOutputStream (BAOS) ؛ OOS.WriteObject (هذا) ؛ bytearrayinputstream bais = new bytearrayinputStream (baos.tobytearray ()) ؛ ObjectInputStream OIS = New ObjectInputStream (BAIS) ؛ copy = (deepCopyExample) ois.readObject () ؛ } catch (ioException e) {E.PrintStackTrace () ؛ } catch (classNotFoundException e) {E.PrintStackTrace () ؛ } نسخة الإرجاع ؛ }}من بينها ، يجب على الطفل تنفيذ الواجهة التسلسلية
تابعة للطفل من الطبقة العامة ، قابلة للتسلسل {Private Static Final Long SerialVersionuid = 6832122780722711261L ؛ اسم السلسلة العامة = "" ؛ الطفل العام (اسم السلسلة) {this.name = name ؛ } Override Public String ToString () {return "child [name =" + name + "]" ؛ }}استخدم أمثلة ورمز الاختبار
DeepCopyexample مثال = جديد deepcopyexample () ؛ example.child = طفل جديد ("مثال") ؛ deepcopyexample copy = example.copy () ؛ if (copy! = null) {copy.child.name = "copied" ؛ System.out.println ("example.child =" + example.child + "؛ copy.child =" + copy.child) ؛} // النتيجة: example.child = child [name = example] ؛ copy.child = child [name = copied]انطلاقًا من نتائج الإخراج ، لا يؤثر تعديل القيمة الفرعية لكائن النسخ على القيمة الطفل للكائن المثال ، أي باستخدام التسلسل لتحقيق نسخة عميقة من الكائن.
لخص
ما سبق هو كل محتويات الاستنساخ في جافا. آمل أن يكون هذا المقال مفيدًا للجميع لتعلم جافا.