لقد استخدمنا مفهوم "الكائن" من قبل، لكننا لم نناقش الطريقة المحددة التي يتم بها تخزين الكائنات في الذاكرة. ستؤدي هذه المناقشة إلى المفهوم المهم وهو "مرجع الكائن".
مرجع الكائن
نستمر في استخدام الفئة البشرية المحددة مسبقًا ولدينا فئة اختبار:
انسخ رمز الكود كما يلي:
اختبار الطبقة العامة
{
الفراغ العام الثابت الرئيسي (String[] args)
{
الإنسان aPerson = الإنسان الجديد (160)؛
}
}
com.classHuman
{
/**
* منشئ
*/
الإنسان العام (كثافة العمليات ح)
{
this.height = h;
}
/**
*الملحقات
*/
كثافة العمليات العامة getHeight()
{
إرجاع هذا الارتفاع؛
}
/**
* متحور
*/
نمو الفراغ العام (int h)
{
this.height = this.height + h;
}
ارتفاع كثافة العمليات الخاصة؛
}
يمكن استدعاء الفئات خارجيًا لإنشاء كائنات، مثل ما ورد أعلاه في فئة الاختبار:
انسخ رمز الكود كما يلي:
الإنسان aPerson = الإنسان الجديد (160)؛
يتم إنشاء كائن من فئة الإنسان.
ما ورد أعلاه عبارة بسيطة للغاية، ولكن لدينا الكثير من التفاصيل التي يجب الخوض فيها:
1. انظر أولاً إلى الجانب الأيمن من علامة المساواة. جديد يفتح مساحة للأشياء في الذاكرة. على وجه التحديد، الجديد يفتح مساحة للكائنات الموجودة في كومة الذاكرة. في هذه المساحة، يتم تخزين بيانات وأساليب الكائن.
2. انظر إلى الجانب الأيسر من علامة المساواة. يشير aPerson إلى كائن بشري، وهو ما يسمى مرجع الكائن. في الواقع، aPerson ليس الكائن نفسه، ولكنه يشبه مؤشر الكائن. aPerson موجود في المكدس في الذاكرة.
3. عندما نستخدم علامة المساواة لتعيين قيمة، يتم تعيين عنوان الكائن الذي تم إنشاؤه في الكومة بواسطة جديد على اليمين إلى مرجع الكائن.
تشير الذاكرة هنا إلى مساحة ذاكرة عملية Java الافتراضية بواسطة JVM (Java Virtual Machine). للتعرف على مفاهيم الكومة والمكدس في الذاكرة، يرجى الرجوع إلى Linux من البرنامج إلى العملية.
يمكن قراءة المكدس بشكل أسرع من الكومة، لكن البيانات المخزنة على المكدس محدودة بالنطاق الصالح. في لغة C، عند انتهاء استدعاء دالة، يتم حذف إطار المكدس المقابل، وتختفي المعلمات والمتغيرات التلقائية المخزنة في إطار المكدس. تخضع مكدسة Java أيضًا لنفس القيود. عند انتهاء استدعاء الطريقة، سيتم مسح البيانات المخزنة على المكدس بواسطة الطريقة. في Java، يتم تخزين كافة الكائنات (العادية) في الكومة. لذلك، المعنى الكامل للكلمة الأساسية الجديدة هو إنشاء كائن في الكومة.
يتم تخزين الكائنات ذات الأنواع البدائية، مثل int وdouble، في المكدس. عندما نعلن عن نوع أساسي، ليست هناك حاجة لنوع جديد. بمجرد الإعلان عنها، تقوم Java بتخزين أنواع البيانات البدائية مباشرة على المكدس. ولذلك، فإن اسم المتغير من النوع الأساسي يمثل البيانات نفسها، وليس مرجعًا.
العلاقة بين المراجع والأشياء هي مثل الطائرة الورقية والشخص. فعندما ننظر إلى السماء (المكتوبة في البرنامج)، فإن ما نراه هو طائرة ورقية (مرجع)، أما ما يتوافق مع الطائرة الورقية فهو شخص (جسم):
فصل المراجع والكائنات؛ تشير المراجع إلى الكائنات
على الرغم من أن المراجع والكائنات منفصلة، إلا أن كل وصولنا إلى الكائنات يجب أن يمر عبر "باب" المراجع، مثل الوصول إلى أساليب الكائن من خلال signal.method(). في Java، لا يمكننا تخطي المراجع ولمس الكائنات مباشرةً. على سبيل المثال، إذا كان عضو البيانات للكائن أ هو كائن عادي ب، فإن عضو البيانات أ يحفظ مرجعًا للكائن ب (إذا كان متغير نوع أساسي، فإن عضو البيانات أ يحفظ متغير النوع الأساسي نفسه) .
في Java، تلعب المراجع دور المؤشرات، لكن لا يمكننا تعديل قيمة المؤشر بشكل مباشر، مثل إضافة 1 إلى قيمة المؤشر كما هو الحال في لغة C. لا يمكننا إجراء العمليات على الكائنات إلا من خلال المراجع. يتجنب هذا التصميم العديد من الأخطاء التي يمكن أن تسببها المؤشرات.
مهمة مرجعية
عندما نقوم بتعيين مرجع إلى مرجع آخر، فإننا في الواقع نقوم بنسخ عنوان الكائن. سيشير كلا المرجعين إلى نفس الكائن. على سبيل المثال، dummyPerson=aPerson; سوف يؤدي إلى:
يمكن أن يكون للكائن مراجع متعددة (يمكن لشخص واحد أن يطير عدة طائرات ورقية). عندما يقوم برنامج بتعديل كائن من خلال مرجع واحد، يكون التعديل مرئيًا من خلال مراجع أخرى. يمكننا استخدام فئة الاختبار التالية لاختبار التأثير الفعلي:
انسخ رمز الكود كما يلي:
اختبار الطبقة العامة
{
الفراغ العام الثابت الرئيسي (String[] args)
{
الإنسان aPerson = الإنسان الجديد (160)؛
دمية الإنسان = شخص؛
System.out.println(dummyPerson.getHeight());
aPerson.growHeight(20);
System.out.println(dummyPerson.getHeight());
}
}
ستؤثر تعديلاتنا على aPerson على dummyPerson. يشير هذان المرجعان في الواقع إلى نفس الكائن.
ولذلك، فإن تعيين مرجع إلى مرجع آخر لا يؤدي إلى نسخ الكائن نفسه. يجب أن نجد آليات أخرى لنسخ الأشياء.
جمع القمامة
مع انتهاء استدعاء الطريقة، يتم مسح متغيرات النوع المرجعي والبدائي. نظرًا لأن الكائن موجود في الكومة، فلن يتم مسح الذاكرة التي يشغلها الكائن عند انتهاء استدعاء الأسلوب. قد تمتلئ مساحة العملية بسرعة بالكائنات التي يتم إنشاؤها. تحتوي Java على آلية تجميع البيانات المهملة المضمنة لمسح الكائنات التي لم تعد تُستخدم لاستعادة مساحة الذاكرة.
المبدأ الأساسي لجمع البيانات المهملة هو أنه عندما يكون هناك مرجع يشير إلى كائن، فلن تتم إعادة تدوير الكائن؛ وعندما لا يكون هناك مرجع يشير إلى كائن، فسيتم مسح الكائن. يتم استعادة المساحة التي تشغلها.
يفترض الشكل أعلاه حالة الذاكرة في JVM في لحظة معينة. يحتوي الكائن البشري على ثلاثة مراجع: aPerson وdummyPerson من المكدس، والرئيس، وهو عضو بيانات في كائن آخر. كائن النادي ليس له مرجع. إذا بدأت عملية جمع البيانات المهملة في هذا الوقت، فسيتم إفراغ كائن النادي، وسيتم أيضًا حذف مرجع الكائن البشري (الرئيس) من كائن النادي.
يعد جمع البيانات المهملة آلية مهمة في Java، مما يؤثر بشكل مباشر على كفاءة تشغيل Java. سأدخل في تفاصيل ذلك لاحقا.
تمرير المعلمة
عندما نفصل بين مفهومي المراجع والكائنات، تكون آلية تمرير المعلمات لطرق Java واضحة جدًا في الواقع: تمرير معلمات Java يتم حسب القيمة. أي أنه عندما نمرر معلمة، ستحصل الطريقة على نسخة من المعلمة.
في الواقع، إحدى المعلمات التي نمررها هي متغير من النوع الأساسي والأخرى هي مرجع للكائن.
تعني قيمة التمرير لمتغيرات النوع البدائي أنه يتم نسخ المتغير نفسه وتمريره إلى طريقة Java. لن تؤثر تعديلات المتغيرات بطرق Java على المتغيرات الأصلية.
يعني التمرير بالقيمة أنه تم نسخ عنوان الكائن وتمريره إلى طريقة Java. سيؤثر الوصول إلى طريقة Java بناءً على هذا المرجع على الكائن.
هناك موقف آخر جدير بالذكر هنا: نستخدم new داخل الطريقة لإنشاء كائن وإرجاع مرجع إلى الكائن. إذا تم استلام الإرجاع بواسطة مرجع، نظرًا لأن مرجع الكائن ليس 0، فسيظل الكائن موجودًا ولن يتم تجميع البيانات المهملة.
تلخيص
جديد
مرجع، كائن
شروط جمع القمامة
المعلمات: مرت بالقيمة