Animal
کلاس داریم:
public class Animal {
String name;
int age;
}
ما می توانیم 2 کلاس فرزند را اعلام کنیم: Cat
و Dog
. این کار با استفاده از کلمه کلیدی extends انجام می شود .
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();
}
}
خروجی کنسول: Animal constructor اجرا شد Cat constructor اجرا شد! در واقع، این روش کار می کند! چرا؟ یکی از دلایل این است که از تکرار فیلدهای مشترک بین دو کلاس خودداری کنید. مثلاً هر حیوانی قلب و مغز دارد اما هر حیوانی دم ندارد. میتوانیم میدانهای مغز و قلب را که برای همه حیوانات مشترک هستند، در Animal
کلاس والد و یک میدان دم در Cat
زیر کلاس اعلام کنیم. . اکنون یک سازنده کلاس اعلام می کنیم Cat
که برای هر 3 فیلد آرگومان می گیرد.
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
. کلاس inheriting به فیلدهای کلاس پایه دسترسی دارد ، بنابراین آنها در Cat
کلاس ما قابل مشاهده هستند. در نتیجه نیازی نیست که این فیلدها را در Cat
کلاس کپی کنیم. می توانیم آنها را از کلاس ببریم Animal
. علاوه بر این، ما می توانیم به صراحت سازنده کلاس پایه را در سازنده کلاس فرزند صدا کنیم. به یک کلاس پایه " superclass " نیز می گویند . به همین دلیل جاوا از کلمه کلیدی 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
. به یاد داشته باشید که ذکر کردیم که سازنده کلاس والد هنگام ایجاد یک شی، ابتدا فراخوانی می شود؟ به همین دلیل است که super() همیشه باید در یک سازنده اول باشد! در غیر این صورت، منطق سازنده نقض می شود و برنامه خطایی ایجاد می کند.
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
and Cat
. با استفاده از این دو کلاس به عنوان مثال، اکنون به فرآیند ایجاد یک شی و مقداردهی اولیه متغیرها خواهیم پرداخت. می دانیم که متغیرهای ثابت و نمونه (غیر ایستا) وجود دارد . همچنین می دانیم که Animal
کلاس پایه دارای متغیرهایی است و Cat
کلاس فرزند متغیرهای خود را دارد. Animal
برای وضوح، هر کدام یک متغیر استاتیک به کلاسهای and اضافه میکنیم 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 سازنده کلاس Animal base تمام شد! مقدار کنونی مغز = مغز مقدار فعلی قلب = قلب سازنده کلاس cat شروع شده است (سازنده Animal قبلاً تمام شده است) مقدار فعلی متغیر ثابت catCount = 37 مقدار فعلی دم = مقدار اولیه دم در کلاس Cat مقدار فعلی دم = Tail بنابراین، اکنون می توانیم ترتیب اولیه سازی متغیر و فراخوانی سازنده را هنگام ایجاد یک شی جدید به وضوح ببینیم:
- متغیرهای استاتیک از کلاس پایه (
Animal
) مقداردهی اولیه می شوند. در مورد ما، متغیر animalCountAnimal
کلاس روی 7700000 تنظیم شده است. -
متغیرهای استاتیک کلاس فرزند (
Cat
) مقداردهی اولیه می شوند.توجه: ما هنوز داخل سازنده هستیم
Animal
و قبلاً نمایش داده ایم:سازنده کلاس Animal در حال اجرا است
آیا متغیرهای کلاس Animal قبلاً مقداردهی اولیه شده اند؟
مقدار فعلی متغیر استاتیک animalCount = 7700000
مقدار فعلی مغز در کلاس Animal = مقدار اولیه مغز در کلاس Animal
مقدار فعلی قلب در کلاس Animal = مقدار اولیه قلب در کلاس Animal
متغیرهای کلاس Cat قبلاً اولیه شده است؟
مقدار فعلی متغیر استاتیک catCount = 37 -
سپس متغیرهای غیر استاتیک کلاس پایه مقدار دهی اولیه می شوند. ما به طور خاص مقادیر اولیه را به آنها اختصاص دادیم، که سپس در سازنده جایگزین می شوند. سازنده Animal هنوز تمام نشده است، اما مقادیر اولیه مغز و قلب قبلاً تعیین شده است:
سازنده کلاس Animal در حال اجرا است
آیا متغیرهای کلاس Animal قبلاً مقداردهی اولیه شده اند؟
مقدار فعلی متغیر استاتیک animalCount = 7700000
مقدار فعلی مغز در کلاس Animal = مقدار اولیه مغز در کلاس Animal
مقدار فعلی قلب در کلاس Animal = مقدار اولیه قلب در کلاس Animal -
سازنده کلاس پایه شروع می شود.
ما قبلاً خودمان را متقاعد کرده ایم که این مرحله چهارم است: در سه مرحله اول در ابتدای سازندهAnimal
، به بسیاری از متغیرها قبلاً مقادیر اختصاص داده شده است. -
فیلدهای غیر استاتیک کلاس فرزند (
Cat
) مقدار دهی اولیه می شوند.
این قبل ازCat
شروع اجرا شدن سازنده اتفاق می افتد.
هنگامی که شروع به اجرا می کند، متغیر tail از قبل دارای یک مقدار است:سازنده کلاس cat شروع شده است (سازنده Animal قبلاً تمام شده است) مقدار فعلی متغیر استاتیک catCount = 37 مقدار فعلی دم = مقدار اولیه دم در کلاس Cat
-
سازنده
Cat
کلاس فرزند فراخوانی می شودو این چیزی است که ایجاد یک شی در جاوا به نظر می رسد!
باید بگویم که ما طرفدار زیادی از یادگیری روت نیستیم، اما بهتر است ترتیب اولیه سازی متغیرها و فراخوانی های سازنده را به خاطر بسپاریم .
این امر درک شما را از جریان برنامه و وضعیت اشیاء شما در هر لحظه خاص بسیار افزایش می دهد.
علاوه بر این، بسیاری از کلاس ها از وراثت استفاده نمی کنند. در این مورد، مراحل مربوط به کلاس پایه اعمال نمی شود.
ادامه مطلب: |
---|
GO TO FULL VERSION