ב-Java, הַבנאי (Constructor) משמש ליצירת המופע של המחלקה. הבנאים דומים כמעט לשיטות, חוץ משני דברים – שמו זהה לשם המחלקה ואין לו סוג החזרה. לפעמים הבנאים נקראים גם בתור שיטות מיוחדות לאיתור אובייקט.
בנאי ב-Java
כאשר אנו משתמשים במילת המפתח new כדי ליצור מופע של מחלקה, הבנאי מתקיים והאובייקט של המחלקה מוחזר. מאחר והבנאי יכול להחזיר רק את האובייקט למחלקה, זה נעשה משמעותית על ידי זמן הריצה של Java ואנחנו לא צריכים להוסיף סוג ההחזרה אליו. אם נוסיף סוג החזרה לבנאי, הוא יהפוך לשיטה של המחלקה. זהו הדרך שבה זמן הריצה של Java מכליל בין שיטה רגילה ובנאי. נניח שיש לנו את הקוד הבא במחלקת Employee.
public Employee() {
System.out.println("Employee Constructor");
}
public Employee Employee() {
System.out.println("Employee Method");
return new Employee();
}
הנה הראשון הוא בנאי, שים לב שאין סוג ההחזרה ואין הצהרת החזרה. השני הוא שיטה רגילה שבה שוב אנו קוראים לבנאי הראשון כדי לקבל את המופע של Employee ולהחזיר אותו. מומלץ שלא להשתמש בשם השיטה הזהה לשם המחלקה משום שזה יוצר בלבול.
סוגי בנאי ב-Java
יש שלושה סוגים של בנאי ב-Java.
- בנאי ברירת המחדל
- בנאי ללא פרמטרים
- בנאי עם פרמטרים
בואו נסתכל על כל אלו סוגי בנאי עם דוגמאות.
בנאי ברירת המחדל ב-Java
אין תמיד צורך לספק יישום של בנאי בקוד המחלקה. אם אין לנו בנאי, Java מספקת יישום של בנאי ברירת המחדל שאנו יכולים להשתמש בו. בואו נסתכל על תכנית פשוטה בה בנאי ברירת המחדל משתמשים מאחר שלא הגדרנו באופן ברור בנאי.
package com.journaldev.constructor;
public class Data {
public static void main(String[] args) {
Data d = new Data();
}
}
- התפקיד היחיד של בנאי ברירת המחדל הוא לאתחל את האובייקט ולהחזיר אותו לקוד הקורא.
- בנאי ברירת המחדל תמיד ללא ארגומנט ומסופק על ידי מהדר Java רק כאשר אין בנאי קיים.
- רוב הזמן אנו יכולים להשתמש בבנאי ברירת המחדל בלבד מאחר ונכנס אל תוכו ניתן לגשת ולאתחל את המאפיינים באמצעות פעולות ה-getter וה-setter.
בנאי ללא ארגומנטים
אין צורך בפרמטרים בבנאי קונקרטי, מוכרח להיות תכנית קונקרטית ללא פרמטרים. דומה לדרישת שינוי בבנאי המחדל ונמצא בשימוש לביצוע פעולות קדם-אתחול כגון בדיקת משאבים, חיבורי רשת, רישום וכו '. בואו נבדוק במהירות את הבנאי ללא פרמטרים ב-Java.
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()
, אז הבנאי ללא פרמטרים שלנו ייקרא. התמונה למטה ממחישה את התנהגות זו, בדקו את הפלט בקונסול של התוכנית.
בנאי עם פרמטרים
בנאי עם פרמטרים נקרא בנאי פרמטריזציה. בואו נסתכל על דוגמה של בנאי פרמטריזציה ב-Java.
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());
}
}
העמסת בנאים ב-Java
כאשר יש לנו יותר מבנאי אחד, אז יש עמסת בנאים ב-Java. בואו נבחן דוגמה של עמסת בנאים בתוכנית Java.
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);
}
}
בנאי פרטי ב-Java
שים לב שאין לנו אפשרות להשתמש במילות המפתח abstract, final, static ו-synchronized עם בנאים. יש לנו אפשרות, עם זאת, להשתמש באפשרויות לשלוט על הצורה בה ניתן ליצור את אובייקט המחלקה. שימוש בגישות public
ו-default
עדיין אפשרי, אך מהו הטעם שבהפוך בנאי לפרטי? במקרה כזה, לא יהיה ניתן ליצור את האובייקט של המחלקה מכל מחלקה אחרת. אז למה להגדיר בנאי כפרטי? במקרה זה, כאשר רוצים ליישם תבנית עיצוב יחיד. מאחר ו-Java מספקת באופן אוטומטי בנאי ברירת מחדל, עלינו ליצור באופן מפורש בנאי ולשמור עליו כפרטי. מחלקות לקוח מקבלות שיטת ישות סטטית כדי לקבל את האינסטנס של המחלקה. לדוגמה של בנאי פרטי עבור מחלקת Data
מוצגת למטה.
// בנאי פרטי
private Data() {
// בנאי ריק ליישום תבנית יחיד
// יכול להכיל קוד המיועד לשימוש בתוך שיטת getInstance() של המחלקה
}
שרשור בנאי ב-Java
כאשר בנאי קורא לבנאי נוסף באותה מחלקה, זה נקרא שרשור בנאי. עלינו להשתמש במילת המפתח this
כדי לקרוא לבנאי נוסף של המחלקה. לעיתים נעשה שימוש בו כדי להגדיר ערכי ברירת מחדל של משתני המחלקה. שים לב שקריאה לבנאי נוסף צריכה להיות הפקודה הראשונה בבלוק הקוד. כמו כן, לא צריך להיות קריאות רקורסיביות שיגרמו ללולאה אינסופית. בוא נראה דוגמה לשרשור בנאי בתוכנית Java.
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 ב-Java
לעיתים קלאס מורשה מקלאס אב, במקרה כזה, אם עלינו לקרוא לקונסטרקטור של הקלאס האב, אז נוכל לעשות זאת באמצעות המילה השמורה 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]
שימו לב שכאשר משתמשים בקונסטרוקטור להעתקה, האובייקט המקורי וההעתק שלו אינם קשורים זה לזה, וכל שינויים באחד מהם לא ישפיעו על השני. זהו הכל לגבי הקונסטרוקטור ב־Java.
Source:
https://www.digitalocean.com/community/tutorials/constructor-in-java