

Animal
מחלקה:
public class Animal {
String name;
int age;
}
אנו יכולים להכריז על 2 כיתות ילדים: Cat
ו Dog
. זה נעשה באמצעות מילת המפתח מרחיב .
public class Cat extends Animal {
}
public class Dog extends Animal {
}
זה עשוי להיות מועיל בעתיד. לדוגמה, אם יש משימה לתפוס עכברים, ניצור אובייקט Cat
בתוכנית שלנו. אם המשימה היא לרדוף אחרי מקל, אז נשתמש בחפץ Dog
. ואם ניצור תוכנית המדמה מרפאה וטרינרית, היא תעבוד עם Animal
הכיתה (ובכך תוכל לטפל גם בחתולים וגם בכלבים). חשוב מאוד לזכור שכאשר אובייקט נוצר, הבנאי של מחלקת הבסיס שלו נקרא תחילה . רק לאחר סיום הבנאי, התוכנית מבצעת את הבנאי של המחלקה התואמת לאובייקט שאנו יוצרים. במילים אחרות, בעת יצירת Cat
אובייקט, הבנאי Animal
מופעל תחילה ורק לאחר מכן Cat
מבוצע הבנאי . כדי לראות זאת, הוסף פלט מסוף לבנאי Cat
ו Animal
.
public class Animal {
public Animal() {
System.out.println("Animal constructor executed");
}
}
public class Cat extends Animal {
public Cat() {
System.out.println("Cat constructor executed!");
}
public static void main(String[] args) {
Cat cat = new Cat();
}
}
פלט קונסולה: בונה בעלי חיים הוצא להורג בנאי חתול הוצא להורג! אכן, זה עובד ככה! למה? אחת הסיבות היא להימנע משכפול שדות משותפים בין שתי המחלקות. לדוגמה, לכל חיה יש לב ומוח, אבל לא לכל חיה יש זנב. נוכל להכריז על שדות מוח ולב , המשותפים לכל בעלי החיים, במחלקת האב, ושדה זנב בתת המעמד . . כעת נכריז על בנאי מחלקה שלוקח ארגומנטים עבור כל 3 השדות. Animal
Cat
Cat
public class Cat extends Animal {
String tail;
public Cat(String brain, String heart, String tail) {
this.brain = brain;
this.heart = heart;
this.tail = tail;
}
public static void main(String[] args) {
Cat cat = new Cat("Brain", "Heart", "Tail");
}
}
הערה: הבנאי פועל כהלכה למרות שלמחלקה Cat
אין שדות מוח ולב . שדות אלה "עברו בירושה" ממחלקת Animal
הבסיס. למחלקה היורשת יש גישה לשדות של מחלקת הבסיס , כך שהם גלויים במחלקה שלנו Cat
. כתוצאה מכך, איננו צריכים לשכפל שדות אלה בכיתה Cat
. אנחנו יכולים לקחת אותם מהכיתה Animal
. מה שכן, אנו יכולים לקרוא במפורש לבנאי מחלקות הבסיס בבנאי מחלקות הילד. מחלקה בסיסית נקראת גם " מחלקה על ". זו הסיבה ש-Java משתמשת במילת המפתח super כדי לציין את מחלקת הבסיס. בדוגמה הקודמת
public Cat(String brain, String heart, String tail) {
this.brain = brain;
this.heart = heart;
this.tail = tail;
}
הקצינו בנפרד כל שדה בכיתת ההורים שלנו. אנחנו בעצם לא צריכים לעשות את זה. מספיק לקרוא לבנאי מחלקת האב ולהעביר את הטיעונים הדרושים:
public class Animal {
String brain;
String heart;
public Animal(String brain, String heart) {
this.brain = brain;
this.heart = heart;
}
public class Cat extends Animal {
String tail;
public Cat(String brain, String heart, String tail) {
super(brain, heart);
this.tail = tail;
}
public static void main(String[] args) {
Cat cat = new Cat("Brain", "Heart", "Tail");
}
}
בקונסטרוקטור Cat
קראנו לקונסטרוקטור Animal
ועברנו שני שדות. היה לנו רק שדה אחד לאתחל במפורש: tail , שאינו ב- Animal
. זוכרים שהזכרנו שבנאי מחלקת האב נקרא ראשון כאשר אובייקט נוצר? זו הסיבה שסופר() תמיד צריך להיות ראשון בקונסטרוקטור! אחרת, הלוגיקה של הבנאי תופר והתוכנית תיצור שגיאה.
public class Cat extends Animal {
String tail;
public Cat(String brain, String heart, String tail) {
this.tail = tail;
super(brain, heart);// Error!
}
public static void main(String[] args) {
Cat cat = new Cat("Brain", "Heart", "Tail");
}
}
המהדר יודע שכאשר נוצר אובייקט של מחלקת ילד, הבנאי מחלקות הבסיס נקרא ראשון. ואם תנסה לשנות התנהגות זו באופן ידני, המהדר לא יאפשר זאת.
איך נוצר אובייקט
הסתכלנו בעבר על דוגמה עם כיתת בסיס והורה:Animal
ו Cat
. באמצעות שתי המחלקות הללו כדוגמאות, כעת נסתכל על תהליך יצירת אובייקט ואתחול משתנים. אנו יודעים שיש משתנים סטטיים ומשתנים מופעיים (לא סטטיים) . אנחנו גם יודעים שלמחלקת Animal
הבסיס יש משתנים, ולמחלקת Cat
הילד יש משלה. למען הבהירות, נוסיף משתנה סטטי אחד כל אחד למחלקות Animal
ו Cat
. המשתנה animalCount במחלקה ייצג את המספר הכולל של מיני בעלי חיים על פני כדור הארץ, והמשתנה catCount יסמן את מספר מיני החתולים. בנוסף, נקצה ערכי התחלה לכל המשתנים הלא סטטיים בשתי המחלקות (אשר ישתנו לאחר מכן בבנאי). Animal
public class Animal {
String brain = "Initial value of brain in the Animal class";
String heart = "Initial value of heart in the Animal class";
public static int animalCount = 7700000;
public Animal(String brain, String heart) {
System.out.println("Animal base class constructor is running");
System.out.println("Have the variables of the Animal class already been initialized?");
System.out.println("Current value of static variable animalCount = " + animalCount);
System.out.println("Current value of brain in the Animal class = " + this.brain);
System.out.println("Current value of heart in the Animal class = " + this.heart);
System.out.println("Have the variables of the Cat class already been initialized?");
System.out.println("Current value of static variable catCount = " + Cat.catCount);
this.brain = brain;
this.heart = heart;
System.out.println("Animal base class constructor is done!");
System.out.println("Current value of brain = " + this.brain);
System.out.println("Current value of heart = " + this.heart);
}
}
public class Cat extends Animal {
String tail = "Initial value of tail in the Cat class";
static int catCount = 37;
public Cat(String brain, String heart, String tail) {
super(brain, heart);
System.out.println("The cat class constructor has started (The Animal constructor already finished)");
System.out.println("Current value of static variable catCount = " + catCount);
System.out.println("Current value of tail = " + this.tail);
this.tail = tail;
System.out.println("Current value of tail = " + this.tail);
}
public static void main(String[] args) {
Cat cat = new Cat("Brain", "Heart", "Tail");
}
}
אז אנחנו יוצרים מופע חדש של המחלקה Cat
, שיורש Animal
. הוספנו פלט מפורט של המסוף כדי לראות מה קורה ובאיזה סדר. זה מה שיוצג בעת Cat
יצירת אובייקט: בנאי מחלקות בעלי חיים פועל האם המשתנים של מחלקת ה- Animal כבר אותחלו? ערך נוכחי של משתנה סטטי animalCount = 7700000 ערך נוכחי של מוח במחלקה Animal = ערך התחלתי של מוח במחלקת Animal ערך נוכחי של לב במחלקת Animal = ערך התחלתי של לב במחלקת Animal יש כבר את המשתנים של מחלקת Cat אותחל? ערך נוכחי של משתנה סטטי catCount = 37 הבנאי מחלקות בסיס בעלי חיים נעשה! ערך נוכחי של מוח = מוח ערך נוכחי לב = לב בנאי מחלקת החתול התחיל (בנאי החיות כבר הסתיים) ערך נוכחי של משתנה סטטי catCount = 37 ערך נוכחי של זנב = ערך התחלתי של זנב במחלקה חתול ערך נוכחי של זנב = זנב אז, כעת אנו יכולים לראות בבירור את סדר האתחול המשתנים וקריאות הבנאים כאשר אובייקט חדש נוצר:
- משתנים סטטיים של מחלקת הבסיס (
Animal
) מאותחלים. במקרה שלנו,Animal
המשתנה animalCount של המחלקה מוגדר ל-7700000. -
משתנים סטטיים של מחלקת הילד (
Cat
) מאותחלים.הערה: אנחנו עדיין בתוך
Animal
הבנאי וכבר הצגנו:בונה מחלקות בסיס בעלי חיים פועל
האם המשתנים של מחלקת החיות כבר אותחלו?
ערך נוכחי של משתנה סטטי animalCount = 7700000
ערך נוכחי של מוח במחלקה Animal = ערך התחלתי של מוח במחלקת Animal ערך
נוכחי של לב במחלקת Animal = ערך התחלתי של לב במחלקת Animal
יש כבר את המשתנים של מחלקת Cat אותחל?
ערך נוכחי של משתנה סטטי catCount = 37 -
לאחר מכן מאותחלים המשתנים הלא סטטיים של מחלקת הבסיס . הקצינו להם במיוחד ערכים ראשוניים, שמוחלפים לאחר מכן בבנאי. בונה החיות עדיין לא סיים, אבל הערכים ההתחלתיים של המוח והלב כבר הוקצו:
בונה מחלקות בסיס בעלי חיים פועל
האם המשתנים של מחלקת החיות כבר אותחלו?
ערך נוכחי של משתנה סטטי animalCount = 7700000
ערך נוכחי של מוח במחלקת בעלי חיים = ערך התחלתי של מוח במחלקת בעלי חיים ערך
נוכחי של לב במחלקת בעלי חיים = ערך התחלתי של לב במחלקת בעלי חיים -
הבנאי מחלקות הבסיס מתחיל.
כבר שכנענו את עצמנו שהשלב הזה הוא רביעי: בשלושת השלבים הראשונים בתחילת הבנאיAnimal
, למשתנים רבים כבר הוקצו ערכים. -
שדות לא סטטיים של כיתת הילד (
Cat
) מאותחלים.
זה קורה לפני שהקונסטרוקטורCat
מתחיל לפעול.
כאשר הוא מתחיל לפעול, למשתנה הזנב כבר יש ערך:בנאי מחלקות החתול התחיל (בנאי החיות כבר הסתיים) ערך נוכחי של משתנה סטטי catCount = 37 ערך נוכחי של זנב = ערך התחלתי של זנב במחלקה Cat
-
הקונסטרוקטור של
Cat
כיתת הילד נקראוכך נראית יצירת אובייקט בג'אווה!
אני חייב לומר שאנחנו לא מעריצים גדולים של לימוד רוטינה, אבל עדיף לשנן את סדר האתחול המשתנים וקריאות הבנאים .
זה יגדיל מאוד את ההבנה שלך לגבי זרימת התוכנית, ואת מצב האובייקטים שלך בכל רגע מסוים.
יתר על כן, שיעורים רבים אינם משתמשים בירושה. במקרה זה, השלבים הקשורים למחלקת הבסיס אינם חלים.
קריאה נוספת: |
---|
GO TO FULL VERSION