سنقوم بتحليلها من البداية إلى النهاية: كيف يتم استدعاء المُنشئين، وكيف وبأي ترتيب تتم تهيئة الحقول (بما في ذلك الحقول الثابتة)، وما إلى ذلك. لقد تطرقنا سابقًا إلى بعض النقاط التي تمت مناقشتها في المقالة، حتى تتمكن من إلقاء نظرة سريعة عليها المواد على منشئي الطبقة الأساسية
. أولاً، دعونا نتذكر كيفية إنشاء الكائن. تتذكر جيدًا كيف تبدو هذه العملية من وجهة نظر المطور: يقوم بإنشاء فصل، ويكتب new، وكل شيء جاهز :) هنا سنتحدث عما يحدث داخل الكمبيوتر وجهاز Java عندما نكتب، على سبيل المثال:
Cat cat = new Cat(); لقد تحدثنا عن هذا من قبل، ولكن فقط في حالة سنذكرك:
- أولا، يتم تخصيص الذاكرة لتخزين الكائن.
- بعد ذلك، تقوم آلة Java بإنشاء مرجع للكائن (في حالتنا، المرجع هو Cat cat).
- وأخيرا، تتم تهيئة المتغيرات ويتم استدعاء المنشئ (سننظر في هذه العملية بمزيد من التفصيل).
لا ينبغي أن تثير هاتان النقطتان الأوليتان أي أسئلة خاصة. يعد تخصيص الذاكرة عملية بسيطة، وهناك نتيجتان محتملتان فقط: إما أن تكون هناك ذاكرة أو لا توجد :) وإنشاء رابط ليس بالأمر غير المعتاد. لكن النقطة الثالثة تمثل مجموعة كاملة من العمليات التي يتم تنفيذها بترتيب صارم. أنا لست من محبي تكثيف الاختبارات، لكن عليك أن تفهم هذه العملية جيدًا وتحتاج إلى حفظ تسلسل العمليات هذا . عندما تحدثنا عن عملية إنشاء الكائنات في الدروس السابقة، لم تكن تعرف أي شيء عن الميراث بعد، لذلك كان شرح بعض الأشياء مشكلة. الآن أنت تعرف الكثير ويمكننا أخيرًا النظر في هذا السؤال بالكامل :) إذن النقطة الثالثة تقول " أخيرًا، تتم تهيئة المتغيرات ويتم استدعاء المنشئ. " ولكن بأي ترتيب يحدث كل هذا؟ للحصول على فهم أفضل، دعونا ننشئ فصلين بسيطين للغاية — أحد الوالدين والطفل:
public class Vehicle {
public static int vehicleCounter = 0;
private String description = "Vehicle";
public Vehicle() {
}
public String getDescription() {
return description;
}
}
public class Truck extends Vehicle {
private static int truckCounter = 0;
private int yearOfManufacture;
private String model;
private int maxSpeed;
public Truck(int yearOfManufacture, String model, int maxSpeed) {
this.yearOfManufacture = yearOfManufacture;
this.model = model;
this.maxSpeed = maxSpeed;
Vehicle.vehicleCounter++;
truckCounter++;
}
} الفصل Truckعبارة عن تطبيق لشاحنة تحتوي على حقول تمثل عامها وطرازها وسرعتها القصوى. الآن نريد إنشاء كائن واحد من هذا القبيل:
public class Main {
public static void main(String[] args) throws IOException {
Truck truck = new Truck(2017, "Scania S 500 4x2", 220);
}
} بالنسبة لجهاز جافا، ستبدو العملية كما يلي:
-
أول ما يحدث هو تهيئة المتغيرات الثابتة للفئة
Vehicle. نعم، قلت الطبقةVehicle، وليسTruck. تتم تهيئة المتغيرات الثابتة قبل استدعاء المنشئات، ويبدأ ذلك في الفئة الأصل. دعونا نحاول التحقق من ذلك. قمنا بتعيينvehicleCounterالحقل فيVehicleالفصل على 10 وحاول عرضه في كل منVehicleالمنشئينTruck.public class Vehicle { public static int vehicleCounter = 10; private String description = "Vehicle"; public Vehicle() { System.out.println(vehicleCounter); } public String getDescription() { return description; } } public class Truck extends Vehicle { private static int truckCount = 0; private int yearOfManufacture; private String model; private int maxSpeed; public Truck(int yearOfManufacture, String model, int maxSpeed) { System.out.println(vehicleCounter); this.yearOfManufacture = yearOfManufacture; this.model = model; this.maxSpeed = maxSpeed; Vehicle.vehicleCounter++; truckCount++; } }لقد وضعنا عبارة println عمدًا في بداية المُنشئ
Truckللتأكد من أن حقول الشاحنة لم تتم تهيئتها بعد عندvehicleCounterعرضها.وهذه هي النتيجة:
10 10 -
بعد تهيئة المتغيرات الثابتة للفئة الأصلية، تتم تهيئة المتغيرات الثابتة للفئة الفرعية. في حالتنا، هذا هو
truckCounterمجال الطبقةTruck.لنقم بتجربة أخرى حيث سنحاول عرض قيمة
truckCounterداخلTruckالمُنشئ قبل تهيئة الحقول الأخرى:public class Truck extends Vehicle { private static int truckCounter = 10; private int yearOfManufacture; private String model; private int maxSpeed; public Truck(int yearOfManufacture, String model, int maxSpeed) { System.out.println(truckCounter); this.yearOfManufacture = yearOfManufacture; this.model = model; this.maxSpeed = maxSpeed; Vehicle.vehicleCounter++; truckCounter++; } }كما ترون، تم بالفعل تعيين القيمة 10 للمتغير الثابت لدينا عندما
Truckيبدأ المنشئ. -
لم يحن الوقت بعد للبناة! تستمر التهيئة المتغيرة. تتم تهيئة المتغيرات غير الثابتة للفئة الأصلية ثالثًا. كما ترون، فإن الميراث يعقد بشكل كبير عملية إنشاء كائن، ولكن لا يوجد شيء يمكنك القيام به حيال ذلك: عليك فقط حفظ بعض الأشياء في البرمجة :)
كتجربة، يمكننا تعيين بعض القيمة الأولية للمتغير
descriptionفيVehicleالفئة، ثم تغييرها في المنشئ.public class Vehicle { public static int vehicleCounter = 10; private String description = "Initial value of the description field"; public Vehicle() { System.out.println(description); description = "Vehicle"; System.out.println(description); } public String getDescription() { return description; } }لنقم بتشغيل
main()طريقتنا لإنشاء شاحنة:public class Main { public static void main(String[] args) throws IOException { Truck truck = new Truck(2017, "Scania S 500 4x2", 220); } }نحصل على النتيجة التالية:
Initial value of the description field Vehicleوهذا يثبت أنه عندما
Vehicleيبدأ المنشئ،descriptionتم بالفعل تعيين قيمة للحقل. -
أخيرا، حان الوقت للبناة! بتعبير أدق، حان الوقت لمنشئ الطبقة الأساسية. يتم استدعاؤه في الخطوة الرابعة من عملية إنشاء الكائن.
وهذا أيضًا سهل التحقق إلى حد ما. دعونا نحاول إخراج سطرين إلى وحدة التحكم: أحدهما داخل
Vehicleمُنشئ الفئة الأساسية، والثاني داخلTruckالمُنشئ. يجب أن نقتنع بأن السطر الموجود بالداخلVehicleيظهر أولاً:public Vehicle() { System.out.println("Hello from the Vehicle constructor!"); } public Truck(int yearOfManufacture, String model, int maxSpeed) { System.out.println("Hello from the Truck constructor!"); this.yearOfManufacture = yearOfManufacture; this.model = model; this.maxSpeed = maxSpeed; Vehicle.vehicleCounter++; truckCounter++; }سنقوم بتشغيل طريقتنا
main()وننظر إلى النتيجة:Hello from the Vehicle constructor! Hello from the Truck constructor!ممتاز. وهذا يعني أننا لسنا مخطئين :) دعونا نمضي قدما.
-
حان الوقت الآن لتهيئة الحقول غير الثابتة للفصل الفرعي، أي فصلنا
Truck. لا تتم تهيئة الحقول الموجودة مباشرة داخل الفصل الذي يتم إنشاء مثيل له حتى الخطوة الخامسة! مفاجئ، ولكنه حقيقي :) مرة أخرى، سنجري فحصًا بسيطًا - تمامًا كما هو الحال مع الفئة الأصلية: سنضع بعض القيمة الأولية للمتغيرmaxSpeedوفيTruckالمنشئ سنتحقق من أنه تم تعيين القيمة قبل بدء المنشئ:public class Truck extends Vehicle { private static int truckCounter = 10; private int yearOfManufacture; private String model; private int maxSpeed = 150; public Truck(int yearOfManufacture, String model, int maxSpeed) { System.out.println("Initial value of maxSpeed = " + this.maxSpeed); this.yearOfManufacture = yearOfManufacture; this.model = model; this.maxSpeed = maxSpeed; Vehicle.vehicleCounter++; truckCounter++; } }إخراج وحدة التحكم:
Initial value of maxSpeed = 150كما ترون، عندما
Truckيبدأ المنشئ،maxSpeedيساوي بالفعل 150! -
Truckيتم استدعاء منشئ فئة الطفل.وفقط عند هذه النقطة، أخيرًا، سيتم استدعاء منشئ الفئة التي نقوم بإنشاء مثيل لها!
فقط في الخطوة السادسة سيتم تخصيص القيم التي نمررها إلى شاحنتنا للحقول.
كما ترون، "إنشاء" شاحنة، أي عملية إنشاء الكائن، ليس بالأمر السهل. ولكن يبدو أننا قمنا بتقسيمها إلى أجزاء أصغر :)
لماذا من المهم جدًا فهم هذه العملية جيدًا؟ تخيل كيف يمكن أن تكون نتائج إنشاء كائن عادي غير متوقعة إذا كنت لا تعرف بالضبط ما كان يحدث "تحت الغطاء" :) الآن حان وقت العودة إلى الدورة التدريبية وإكمال بعض المهام! حظا سعيدا ونراكم قريبا! :)
GO TO FULL VERSION