JavaRush /مدونة جافا /Random-AR /البناء في جاوة

البناء في جاوة

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

ما هو المُنشئ في Java ولماذا هو مطلوب؟

دعونا ننظر إلى مثالين.
public class Car {

   String model;
   int maxSpeed;

   public static void main(String[] args) {

       Car bugatti = new Car();
       bugatti.model = "Bugatti Veyron";
       bugatti.maxSpeed = 407;

   }
}
لقد صنعنا سيارتنا وحددنا طرازها وسرعتها القصوى. ومع ذلك، في المشروع الحقيقي، من الواضح أن كائن السيارة سيحتوي على أكثر من حقلين. وعلى سبيل المثال 16 حقلاً!
public class Car {

   String model;//model
   int maxSpeed;//max speed
   int wheels;// disk width
   double engineVolume;//engine capacity
   String color;//color
   int yearOfIssue;//year of issue
   String ownerFirstName;//Owner's name
   String ownerLastName;//owner's last name
   long price;//price
   boolean isNew;//new or not
   int placesInTheSalon;//number of seats in the cabin
   String salonMaterial;// interior material
   boolean insurance;//is it insured
   String manufacturerCountry;//manufacturer country
   int trunkVolume;// trunk volume
   int accelerationTo100km;//acceleration to 100 km/h in seconds


   public static void main(String[] args) {
       Car bugatti = new Car();

       bugatti.color = "blue";
       bugatti.accelerationTo100km = 3;
       bugatti.engineVolume = 6.3;
       bugatti.manufacturerCountry = "Italy";
       bugatti.ownerFirstName = "Amigo";
       bugatti.yearOfIssue = 2016;
       bugatti.insurance = true;
       bugatti.price = 2000000;
       bugatti.isNew = false;
       bugatti.placesInTheSalon = 2;
       bugatti.maxSpeed = 407;
       bugatti.model = "Bugatti Veyron";

   }

}
لقد أنشأنا كائن سيارة جديدًا . مشكلة واحدة: لدينا 16 حقلاً، ولكننا قمنا بتهيئة 12 حقلاً فقط ! حاول الآن استخدام الكود للعثور على تلك التي نسيناها! ليس بهذه السهولة، أليس كذلك؟ في مثل هذه الحالة، يمكن للمبرمج أن يرتكب خطأ بسهولة ويتخطى تهيئة بعض الحقول. ونتيجة لذلك، سيصبح سلوك البرنامج خاطئًا:
public class Car {

   String model;//model
   int maxSpeed;//max speed
   int wheels;// disk width
   double engineVolume;//engine capacity
   String color;//color
   int yearOfIssue;//year of issue
   String ownerFirstName;//Owner's name
   String ownerLastName;//owner's last name
   long price;//price
   boolean isNew;//new or not
   int placesInTheSalon;//number of seats in the cabin
   String salonMaterial;// interior material
   boolean insurance;//is it insured
   String manufacturerCountry;//manufacturer country
   int trunkVolume;// trunk volume
   int accelerationTo100km;//acceleration to 100 km/h in seconds


   public static void main(String[] args) {
       Car bugatti = new Car();

       bugatti.color = "blue";
       bugatti.accelerationTo100km = 3;
       bugatti.engineVolume = 6.3;
       bugatti.manufacturerCountry = "Italy";
       bugatti.ownerFirstName = "Amigo";
       bugatti.yearOfIssue = 2016;
       bugatti.insurance = true;
       bugatti.price = 2000000;
       bugatti.isNew = false;
       bugatti.placesInTheSalon = 2;
       bugatti.maxSpeed = 407;
       bugatti.model = "Bugatti Veyron";

       System.out.println("Model Bugatti Veyron. Engine size - " + bugatti.engineVolume + ", trunk - " + bugatti.trunkVolume + ", salon is made of" + bugatti.salonMaterial +
       ", disc width - " + bugatti.wheels + ". Was acquired in 2018 by Mr. " + bugatti.ownerLastName);

   }

}
إخراج وحدة التحكم:
موديل بوجاتي فيرون. إزاحة المحرك - 6.3، صندوق السيارة - 0، الجزء الداخلي مصنوع من null، عرض الحافة - 0. تم شراؤه في عام 2018 من قبل السيد null
من الواضح أن المشتري الذي دفع 2 مليون دولار لشراء سيارة، لن يحب أن يطلق عليه "السيد نول"! ولكن على محمل الجد، في النهاية، انتهى برنامجنا بكائن تم إنشاؤه بشكل غير صحيح - سيارة بعرض حافة 0 (أي بدون إطارات على الإطلاق)، وصندوق مفقود، ومقصورة داخلية مصنوعة من مادة غير معروفة، وحتى مملوكة لشخص غير معروف . ولا يسع المرء إلا أن يتخيل كيف يمكن أن يحدث مثل هذا الخطأ أثناء تشغيل البرنامج! نحن بحاجة إلى تجنب مثل هذه المواقف بطريقة أو بأخرى. نحتاج أن يكون لبرنامجنا قيود: عند إنشاء كائن مركبة جديد، على سبيل المثال، يجب دائمًا تحديد الطراز والسرعة القصوى له. وإلا، فلا تسمح بإنشاء الكائن. وظائف المنشئ تتعامل بسهولة مع هذه المهمة. لقد حصلوا على اسمهم لسبب ما. يقوم المنشئ بإنشاء نوع من "الهيكل العظمي" للفئة، والتي يجب أن يتوافق معها كل كائن جديد في الفئة. للراحة، دعنا نعود إلى نسخة أبسط من فئة السيارة مع حقلين. نظرًا لمتطلباتنا، سيبدو مُنشئ فئة السيارة كما يلي:
public Car(String model, int maxSpeed) {
   this.model = model;
   this.maxSpeed = maxSpeed;
}
ويبدو إنشاء كائن الآن كما يلي:
public static void main(String[] args) {
   Car bugatti = new Car("Bugatti Veyron", 407);
}
انتبهكيف يتم إنشاء المنشئ. وهي تشبه الطريقة العادية، ولكنها لا تحتوي على نوع إرجاع. في هذه الحالة، تتم الإشارة إلى اسم الفئة في المُنشئ أيضًا بحرف كبير. في حالتنا - سيارة . بالإضافة إلى ذلك، يستخدم المُنشئ الكلمة الأساسية الجديدة لك this . "هذا" في اللغة الإنجليزية يعني "هذا، هذا". تشير هذه الكلمة إلى كائن معين. الكود في المنشئ:
public Car(String model, int maxSpeed) {
   this.model = model;
   this.maxSpeed = maxSpeed;
}
يمكن ترجمتها بشكل حرفي تقريبًا: " نموذج لهذا الجهاز (الذي نقوم بإنشائه الآن) = وسيطة النموذج المحددة في المنشئ. maxSpeed ​​​​لهذا الجهاز (الذي نقوم بإنشائه) = وسيطة maxSpeed ​​​​، والتي تم تحديده في المنشئ." هذا ما حدث:
public class Car {

   String model;
   int maxSpeed;

   public Car(String model, int maxSpeed) {
       this.model = model;
       this.maxSpeed = maxSpeed;
   }

   public static void main(String[] args) {
       Car bugatti = new Car("Bugatti Veyron", 407);
       System.out.println(bugatti.model);
       System.out.println(bugatti.maxSpeed);
   }

}
إخراج وحدة التحكم:
بوجاتي فيرون 407
قام المنشئ بتعيين القيم المطلوبة بنجاح. ربما لاحظت أن المنشئ يشبه إلى حد كبير الطريقة العادية! هذا هو الحال: المُنشئ هو طريقة، فقط محدد قليلاً :) تمامًا كما هو الحال في الطريقة، قمنا بتمرير المعلمات إلى المُنشئ الخاص بنا. وكما هو الحال مع استدعاء التابع، فإن استدعاء المنشئ لن ينجح إذا لم تحدده:
public class Car {

   String model;
   int maxSpeed;

   public Car(String model, int maxSpeed) {
       this.model = model;
       this.maxSpeed = maxSpeed;
   }

   public static void main(String[] args) {
       Car bugatti = new Car(); //error!
   }

}
كما ترون، قام المصمم بما كنا نحاول تحقيقه. الآن لا يمكنك إنشاء سيارة بدون سرعة أو بدون نموذج! أوجه التشابه بين المنشئين والأساليب لا تنتهي عند هذا الحد. تمامًا مثل الأساليب، يمكن تحميل المُنشئين بشكل زائد. تخيل أن لديك قطتين في المنزل. لقد أخذت أحدهما كقطة صغيرة، وأحضرت الثاني من الشارع إلى المنزل وهو شخص بالغ ولا تعرف عمره بالضبط. هذا يعني أن برنامجنا يجب أن يكون قادرًا على إنشاء قطط من نوعين - باسم وعمر القطة الأولى، واسم فقط - للقط الثاني. للقيام بذلك، سنقوم بتحميل المنشئ بشكل زائد:
public class Cat {

   String name;
   int age;

   //for the first cat
   public Cat(String name, int age) {
       this.name = name;
       this.age = age;
   }

   //for the second cat
   public Cat(String name) {
       this.name = name;
   }

   public static void main(String[] args) {

       Cat barsik = new Cat("Barsik", 5);
       Cat streetCatNamedBob = new Cat("Bob");
   }

}
إلى المُنشئ الأصلي الذي يحتوي على معلمات "الاسم" و"العمر"، أضفنا معلمًا آخر، بالاسم فقط. لقد قمنا بتحميل الأساليب بنفس الطريقة في الدروس السابقة. يمكننا الآن إنشاء كلا الإصدارين من القطط بنجاح :) لماذا هناك حاجة للمصممين؟  - 2هل تتذكر أننا قلنا في بداية المحاضرة أنك استخدمت المُنشئات بالفعل، لكنك لم تلاحظ ذلك؟ هذا صحيح. الحقيقة هي أن كل فئة في Java لديها ما يسمى بالمنشئ الافتراضي. لا يحتوي على أية وسائط، ولكنه يتم تشغيله في كل مرة يتم فيها إنشاء أي كائن من أي فئة.
public class Cat {

   public static void main(String[] args) {

       Cat barsik = new Cat(); //this is where the default constructor worked
   }
}
للوهلة الأولى هذا ليس ملحوظا. حسنًا، لقد خلقنا شيئًا وأبدعناه، أين عمل المصمم؟ لرؤية ذلك، دعونا نكتب منشئًا فارغًا لفئة Cat بأيدينا ، وسنقوم بداخله بطباعة بعض العبارات على وحدة التحكم. إذا تم عرضه، فهذا يعني أن المنشئ قد عمل.
public class Cat {

   public Cat() {
       System.out.println("Created a cat!");
   }

   public static void main(String[] args) {

       Cat barsik = new Cat(); //this is where the default constructor worked
   }
}
إخراج وحدة التحكم:
لقد خلقوا قطة!
وهنا التأكيد! المنشئ الافتراضي موجود دائمًا بشكل غير مرئي في فصولك الدراسية. ولكن عليك أن تعرف ميزة أخرى منه. يختفي المُنشئ الافتراضي من الفصل عند إنشاء مُنشئ باستخدام الوسائط. والدليل على ذلك، في الواقع، رأيناه بالفعل أعلاه. هنا في هذا الكود:
public class Cat {

   String name;
   int age;

   public Cat(String name, int age) {
       this.name = name;
       this.age = age;
   }

   public static void main(String[] args) {

       Cat barsik = new Cat(); //error!
   }
}
لم نتمكن من إنشاء قطة بدون اسم وعمر لأننا حددنا مُنشئًا لـ Cat : سلسلة + رقم. اختفى المنشئ الافتراضي من الفصل مباشرة بعد ذلك. لذلك، تأكد من تذكر: إذا كنت بحاجة إلى العديد من المنشئين في صفك، بما في ذلك فارغة، فأنت بحاجة إلى إنشائها بشكل منفصل. على سبيل المثال، نقوم بإنشاء برنامج لعيادة بيطرية. تريد عيادتنا عمل الخير ومساعدة القطط المشردة التي لا نعرف اسمها ولا عمرها. ثم يجب أن يبدو الكود الخاص بنا كما يلي:
public class Cat {

   String name;
   int age;

   //for domestic cats
   public Cat(String name, int age) {
       this.name = name;
       this.age = age;
   }

   //for street cats
   public Cat() {
   }

   public static void main(String[] args) {

       Cat barsik = new Cat("Barsik", 5);
       Cat streetCat = new Cat();
   }
}
الآن بعد أن كتبنا مُنشئًا افتراضيًا بشكل صريح، يمكننا إنشاء قطات من كلا النوعين :) بالنسبة للمنشئ (كما هو الحال مع أي طريقة)، فإن ترتيب الوسائط مهم جدًا. لنقم بتبديل وسيطات الاسم والعمر في مُنشئنا.
public class Cat {

   String name;
   int age;

   public Cat(int age, String name) {
       this.name = name;
       this.age = age;
   }

   public static void main(String[] args) {

       Cat barsik = new Cat("Barsik", 10); //error!
   }
}
خطأ! ينص المنشئ بوضوح على أنه عند إنشاء كائن Cat، يجب تمرير رقم وسلسلة، بهذا الترتيب. لهذا السبب لا يعمل الكود الخاص بنا. تأكد من تذكر ذلك ووضعه في الاعتبار عند إنشاء فصولك الدراسية الخاصة:
public Cat(String name, int age) {
   this.name = name;
   this.age = age;
}

public Cat(int age, String name) {
   this.age = age;
   this.name = name;
}
هذان مصممان مختلفان تمامًا! إذا عبرنا عن إجابة السؤال "لماذا نحتاج إلى مُنشئ؟" في جملة واحدة، يمكننا أن نقول: بحيث تكون الكائنات دائمًا في الحالة الصحيحة. عند استخدام المنشئات، سيتم تهيئة جميع المتغيرات الخاصة بك بشكل صحيح، ولن تكون هناك سيارات ذات سرعة 0 أو كائنات أخرى "غير صحيحة" في البرنامج. استخدامها مفيد جدًا للمبرمج نفسه في المقام الأول. إذا قمت بتهيئة الحقول بنفسك، فهناك احتمال كبير بفقدان شيء ما وارتكاب خطأ. لكن هذا لن يحدث مع المُنشئ: إذا لم تقم بتمرير جميع الوسائط المطلوبة إليه أو خلطت أنواعها، فسيقوم المترجم بإلقاء خطأ على الفور. ومن الجدير بالذكر بشكل منفصل أنه لا يجب عليك وضع منطق برنامجك داخل المُنشئ. للقيام بذلك، لديك طرق تحت تصرفك يمكنك من خلالها وصف جميع الوظائف التي تحتاجها. دعونا نلقي نظرة على سبب كون منطق المنشئ فكرة سيئة:
public class CarFactory {

   String name;
   int age;
   int carsCount;

   public CarFactory(String name, int age, int carsCount) {
   this.name = name;
   this.age = age;
   this.carsCount = carsCount;

   System.out.println("Our car factory is called" + this.name);
   System.out.println("She was founded" + this.age + " years ago" );
   System.out.println("During this time it was produced" + this.carsCount +  "cars");
   System.out.println("On average she produces" + (this.carsCount/this.age) + "cars per year");
}

   public static void main(String[] args) {

       CarFactory ford = new CarFactory("Ford", 115 , 50000000);
   }
}
لدينا فئة CarFactory التي تصف مصنعًا لإنتاج السيارات. داخل المُنشئ، نقوم بتهيئة جميع الحقول ووضع المنطق هنا: نعرض بعض المعلومات حول المصنع على وحدة التحكم. يبدو أنه لا حرج في هذا، البرنامج يعمل بشكل مثالي. إخراج وحدة التحكم:
مصنعنا للسيارات اسمه فورد، تأسس منذ 115 عاماً، وخلال هذه الفترة أنتج 50 مليون سيارة، وينتج في المتوسط ​​434,782 سيارة سنوياً.
لكن في الواقع، لقد زرعنا قنبلة موقوتة. ويمكن أن يؤدي مثل هذا الرمز بسهولة إلى حدوث أخطاء. لنتخيل أننا الآن لا نتحدث عن شركة فورد، بل عن المصنع الجديد "أميجو موتورز" الذي مضى على وجوده أقل من عام وأنتج 1000 سيارة:
public class CarFactory {

   String name;
   int age;
   int carsCount;

   public CarFactory(String name, int age, int carsCount) {
   this.name = name;
   this.age = age;
   this.carsCount = carsCount;

   System.out.println("Our car factory is called" + this.name);
   System.out.println("She was founded" + this.age + " years ago" );
   System.out.println("During this time it was produced" + this.carsCount +  "cars");
   System.out.println("On average she produces" + (this.carsCount/this.age) + "cars per year");
}


   public static void main(String[] args) {

       CarFactory ford = new CarFactory("Amigo Motors", 0 , 1000);
   }
}
إخراج وحدة التحكم:
يُطلق على مصنع السيارات الخاص بنا اسم Amigo Motors Exception في مؤشر الترابط "الرئيسي" java.lang.ArithmeticException: / بواسطة Zero تم تأسيسه منذ 0 سنوات وخلال هذا الوقت، أنتج 1000 سيارة في CarFactory.<init>(CarFactory.java:15) في CarFactory.main(CarFactory.java:23) انتهت العملية برمز الخروج 1</init>
لقد وصلنا! انتهى البرنامج ببعض الأخطاء الغريبة. هل ستحاول تخمين ما هو السبب؟ السبب هو المنطق الذي وضعناه في المُنشئ. وتحديداً في هذا السطر:
System.out.println("On average she produces" + (this.carsCount/this.age) + "cars per year");
وهنا نقوم بالحساب وتقسيم عدد السيارات المنتجة على عمر المصنع. وبما أن مصنعنا جديد (أي عمره 0 سنة)، فإن النتيجة هي القسمة على 0، وهو أمر محظور في الرياضيات. ونتيجة لذلك، ينتهي البرنامج مع وجود خطأ. ماذا كان ينبغي علينا أن نفعل؟ انقل كل المنطق إلى طريقة منفصلة وقم بتسميته، على سبيل المثال، printFactoryInfo() . يمكنك تمرير كائن CarFactory إليه كمعلمة . يمكنك أيضًا وضع كل المنطق هناك، وفي نفس الوقت - معالجة الأخطاء المحتملة، مثل أخطاءنا التي لا تحتوي على سنوات. كل لوحده. هناك حاجة إلى منشئين لتعيين حالة الكائن بشكل صحيح. لمنطق الأعمال لدينا أساليب. لا يجب أن تخلط أحدهما مع الآخر.
تعليقات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION