المُنشئ في جافا يُستخدم لإنشاء نموذج من الصنف. المنشئات مُشابهة تقريبًا للوظائف باستثناء شيئين – اسمها يكون نفس اسم الصنف وليس لها نوع إرجاع. في بعض الأحيان يُشار أيضًا إلى المُنشئات بأنها وظائف خاصة لتهيئة كائن.
المُنشئ في جافا
عندما نستخدم الكلمة الأساسية “new” لإنشاء نموذج من الصنف، يُستدعى المُنشئ ويتم إرجاع كائن الصنف. نظرًا لأن المُنشئ يمكنه فقط إرجاع كائن إلى الصنف، يتم ذلك ضمنًا بواسطة تشغيل جافا ولا يُفترض أن نضيف نوع إرجاع له. إذا أضفنا نوع إرجاع للمُنشئ، فسيتحول إلى وظيفة للصنف. هذه هي الطريقة التي يميز بها تشغيل جافا بين وظيفة عادية ومُنشئ. دعونا نفترض أن لدينا الشفرة التالية في صنف “Employee”.
public Employee() {
System.out.println("Employee Constructor");
}
public Employee Employee() {
System.out.println("Employee Method");
return new Employee();
}
هنا الأولى هي مُنشئ، لاحظ أنه ليس لها نوع إرجاع ولا تحتوي على عبارة إرجاع. الثانية هي وظيفة عادية حيث نقوم مجددًا باستدعاء المُنشئ الأول للحصول على نموذج “Employee” وإرجاعه. من المُوصى به عدم وجود اسم الوظيفة نفس اسم الصنف لأنه يُخلق بالتبعية لبعض الارتباك.
أنواع المُنشئ في جافا
هناك ثلاثة أنواع من المُنشئ في جافا.
- المنشئ الافتراضي
- المنشئ بدون وسائط
- المنشئ المعرف بوسائط
لنتعمق في جميع أنواع المنشئين مع برامج أمثلة.
المنشئ الافتراضي في جافا
ليس من الضروري دائمًا تقديم تنفيذ للمنشئ في كود الصف. إذا لم نقدم منشئًا، فإن جافا توفر تنفيذ المنشئ الافتراضي لنا للاستخدام. لننظر إلى برنامج بسيط حيث يتم استخدام المنشئ الافتراضي لأننا لن نعرف منشئًا بشكل صريح.
package com.journaldev.constructor;
public class Data {
public static void main(String[] args) {
Data d = new Data();
}
}
- الدور الوحيد للمنشئ الافتراضي هو تهيئة الكائن وإعادته إلى الكود الذي يستدعيه.
- المنشئ الافتراضي دائمًا بدون وسائط ويتم توفيره من قبل مترجم جافا فقط عندما لا يوجد منشئ معرف مسبقًا.
- معظم الوقت نحن بخير مع المنشئ الافتراضي نفسه حيث يمكن الوصول إلى الخصائص الأخرى وتهيئتها من خلال طرق الحصول والتعيين.
المنشئ بدون وسائط
البناء بدون أي وسيط هو ما يسمى البناء بدون معاملات. إنه مثل استبدال البناء الافتراضي ويستخدم للقيام ببعض الاستعدادات الأولية مثل التحقق من الموارد، والاتصالات الشبكية، والتسجيل، إلخ. دعنا نلقي نظرة سريعة على البناء بدون معاملات في جافا.
package com.journaldev.constructor;
public class Data {
// البناء بدون معاملات
public Data() {
System.out.println("No-Args Constructor");
}
public static void main(String[] args) {
Data d = new Data();
}
}
الآن عندما نقوم بالاتصال new Data()
، سيتم استدعاء البناء بدون معاملات لدينا. الصورة أدناه توضح هذا السلوك، تحقق من إخراج الوحدة النمطية للبرنامج.
البناء بمعاملات
يتم استدعاء البناء مع المعاملات بالبناء بالمعاملات. دعنا نلقي نظرة على مثال للبناء بمعاملات في جافا.
package com.journaldev.constructor;
public class Data {
private String name;
public Data(String n) {
System.out.println("Parameterized Constructor");
this.name = n;
}
public String getName() {
return name;
}
public static void main(String[] args) {
Data d = new Data("Java");
System.out.println(d.getName());
}
}
التحميل الزائد للبناء في جافا
عندما يكون لدينا أكثر من بناء واحد، فهو التحميل الزائد للبناء في جافا. دعنا نلقي نظرة على مثال للتحميل الزائد للبناء في برنامج جافا.
package com.journaldev.constructor;
public class Data {
private String name;
private int id;
// البناء بدون معاملات
public Data() {
this.name = "Default Name";
}
// البناء بمعامل واحد
public Data(String n) {
this.name = n;
}
// البناء بمعاملين
public Data(String n, int i) {
this.name = n;
this.id = i;
}
public String getName() {
return name;
}
public int getId() {
return id;
}
@Override
public String toString() {
return "ID="+id+", Name="+name;
}
public static void main(String[] args) {
Data d = new Data();
System.out.println(d);
d = new Data("Java");
System.out.println(d);
d = new Data("Pankaj", 25);
System.out.println(d);
}
}
البناء الخاص في جافا
لاحظ أنه لا يمكننا استخدام الكلمات المفتاحية abstract, final, static و synchronized مع البناء. ومع ذلك، يمكننا استخدام المحددات للتحكم في إنشاء كائن فئة. استخدام public
و default
للوصول ما زال مقبولًا، ولكن ما هو الفائدة من جعل بناء خاص؟ في هذه الحالة، لن يكون بإمكان أي فئة أخرى إنشاء نسخة من الفئة. حسنًا، يتم جعل البناء خاصًا في حالة رغبتنا في تنفيذ نمط تصميم الفردي. نظرًا لأن جافا توفر تلقائيًا بناءً فارغًا، يجب علينا إنشاء بناء بشكل صريح والاحتفاظ به كخاص. يتم توفير فئات العميل بطريقة مرافقة ثابتة للحصول على نسخة من الفئة. يتم تقديم مثال على البناء الخاص لفئة Data
أدناه.
// البناء الخاص
private Data() {
// بناء فارغ لتنفيذ نمط الفردي
// يمكن أن يحتوي على الكود الذي يتم استخدامه داخل طريقة getInstance() للفئة
}
سلسلة بناء المُنشئ في جافا
عندما يُستدعى مُنشئ آخر من نفس الفئة، يُعرف ذلك بسلسلة بناء المُنشئ. علينا استخدام الكلمة الأساسية this
لاستدعاء مُنشئ آخر من الفئة. في بعض الأحيان يُستخدم لتعيين بعض القيم الافتراضية لمتغيرات الفئة. يجب أن يكون استدعاء مُنشئ آخر أمام العبارة الأولى في كتلة الكود. كما يجب ألا تكون هناك استدعاءات متكررة التي ستنشئ حلقة تكرارية لا نهائية. دعنا نرى مثالًا على سلسلة بناء المُنشئ في برنامج جافا.
package com.journaldev.constructor;
public class Employee {
private int id;
private String name;
public Employee() {
this("John Doe", 999);
System.out.println("Default Employee Created");
}
public Employee(int i) {
this("John Doe", i);
System.out.println("Employee Created with Default Name");
}
public Employee(String s, int i) {
this.id = i;
this.name = s;
System.out.println("Employee Created");
}
public static void main(String[] args) {
Employee emp = new Employee();
System.out.println(emp);
Employee emp1 = new Employee(10);
System.out.println(emp1);
Employee emp2 = new Employee("Pankaj", 20);
System.out.println(emp2);
}
@Override
public String toString() {
return "ID = "+id+", Name = "+name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
I have overridden the toString() method to print some useful information about Employee object. Below is the output produced by above program.
Employee Created
Default Employee Created
ID = 999, Name = John Doe
Employee Created
Employee Created with Default Name
ID = 10, Name = John Doe
Employee Created
ID = 20, Name = Pankaj
لاحظ كيف يتم استدعاء مُنشئ واحد من مُنشئ آخر، هذا ما يُعرف بعملية سلسلة بناء المُنشئ.
مُنشئ سوبر في جافا
بعض الأحيان يتم الاشتقاق من الفئة الأساسية، في هذه الحالة، إذا كان علينا استدعاء بناء الفئة الأساسية فيمكننا فعل ذلك باستخدام الكلمة الأساسية super
. دعونا نلقي نظرة على مثال على استخدام بناء فئة فوقية. لاحظ أن استدعاء بناء الكلمة الأساسية يجب أن يكون أول عبارة في بناء الفئة الفرعية. كما عند إنشاء مثيل لبناء الفئة الفرعية، يقوم جافا أولاً بتهيئة الفئة الأساسية ثم الفئة الفرعية. لذلك، إذا لم يتم استدعاء بناء الفئة الأساسية بشكل صريح، فإن جافا تقوم بالاتصال ببناء الافتراضي أو بدون معاملات بواسطة وقت التشغيل. لنفهم هذه المفاهيم من خلال بعض البرامج المثالية. لنفترض أن لدينا فئتين كما يلي.
package com.journaldev.constructor;
public class Person {
private int age;
public Person() {
System.out.println("Person Created");
}
public Person(int i) {
this.age = i;
System.out.println("Person Created with Age = " + i);
}
}
package com.journaldev.constructor;
public class Student extends Person {
private String name;
public Student() {
System.out.println("Student Created");
}
public Student(int i, String n) {
super(i); // super class constructor called
this.name = n;
System.out.println("Student Created with name = " + n);
}
}
الآن إذا قمنا بإنشاء كائن طالب كما يلي؛
Student st = new Student();
ماذا سيكون الإخراج المنتج؟ إن الإخراج من الكود أعلاه سيكون:
Person Created
Student Created
لذا، تم الاتصال ببناء بدون معاملات لفئة الطالب نظرًا لعدم وجود استدعاء فوقي في العبارة الأولى، يتم استدعاء بناء الافتراضي أو بدون معاملات لفئة الشخص. وبالتالي، الإخراج. ماذا لو كنا نستخدم بناء الطالب مع معلمة كما في Student st = new Student(34، "Pankaj");
، سيكون الإخراج كالتالي:
Person Created with Age = 34
Student Created with name = Pankaj
هنا يكون الإخراج واضحًا لأننا نستدعي بناء الفئة الأساسية بشكل صريح، لذا لا تحتاج جافا إلى أي عمل إضافي من جانبهم.
بناء نسخ جافا
تأخذ نسخة Java منشئ الكائن من نفس الفئة كمعامل وتنشئ نسخة منه. في بعض الأحيان نحتاج إلى نسخة من كائن آخر للقيام ببعض المعالجة. يمكننا القيام بذلك بالطرق التالية:
- تنفيذ الاستنساخ
- توفير طريقة مساعدة لنسخ عميق للكائن.
- وجود منشئ نسخة
الآن دعونا نرى كيفية كتابة منشئ نسخة. لنفترض أن لدينا فئة Fruits
مثل ما يلي.
package com.journaldev.constructor;
import java.util.ArrayList;
import java.util.List;
public class Fruits {
private List<String> fruitsList;
public List<String> getFruitsList() {
return fruitsList;
}
public void setFruitsList(List<String> fruitsList) {
this.fruitsList = fruitsList;
}
public Fruits(List<String> fl) {
this.fruitsList = fl;
}
public Fruits(Fruits fr) {
List<String> fl = new ArrayList<>();
for (String f : fr.getFruitsList()) {
fl.add(f);
}
this.fruitsList = fl;
}
}
لاحظ أن Fruits(Fruits fr)
يقوم بنسخ عميق لإرجاع نسخة من الكائن. دعنا نلقي نظرة على برنامج اختبار لفهم سبب أفضلية وجود منشئ نسخة لنسخ كائن.
package com.journaldev.constructor;
import java.util.ArrayList;
import java.util.List;
public class CopyConstructorTest {
public static void main(String[] args) {
List<String> fl = new ArrayList<>();
fl.add("Mango");
fl.add("Orange");
Fruits fr = new Fruits(fl);
System.out.println(fr.getFruitsList());
Fruits frCopy = fr;
frCopy.getFruitsList().add("Apple");
System.out.println(fr.getFruitsList());
frCopy = new Fruits(fr);
frCopy.getFruitsList().add("Banana");
System.out.println(fr.getFruitsList());
System.out.println(frCopy.getFruitsList());
}
}
إخراج البرنامج أعلاه هو:
[Mango, Orange]
[Mango, Orange, Apple]
[Mango, Orange, Apple]
[Mango, Orange, Apple, Banana]
لاحظ أنه عند استخدام منشئ النسخة، كلا الكائن الأصلي ونسخته غير مرتبطين ببعضهما البعض وأي تعديل في أحدهما لن ينعكس على الآخر. هذا كل شيء بالنسبة للمنشئ في جافا.
Source:
https://www.digitalocean.com/community/tutorials/constructor-in-java