ما آن را از ابتدا تا انتها تجزیه و تحلیل میکنیم: سازندهها چگونه فراخوانی میشوند، چگونه و به چه ترتیبی فیلدها (از جمله فیلدهای استاتیک) مقداردهی اولیه میشوند و غیره. ما قبلاً برخی از نکات مورد بحث در مقاله را لمس کردهایم، بنابراین میتوانید به آن نگاه کنید. مواد بر روی سازنده های کلاس پایه
ابتدا بیایید به یاد بیاوریم که یک شی چگونه ایجاد می شود. شما به خوبی به یاد دارید که این فرآیند از دیدگاه یک توسعه دهنده چگونه به نظر می رسد: او یک کلاس ایجاد می کند، می نویسد newو همه چیز آماده است :) در اینجا ما در مورد آنچه که در داخل کامپیوتر و ماشین جاوا هنگام نوشتن اتفاق می افتد صحبت خواهیم کرد، به عنوان مثال:
Cat cat = new Cat(); قبلاً در این مورد صحبت کردهایم، اما اگر به شما یادآوری کنیم:
- ابتدا حافظه برای ذخیره شی اختصاص داده می شود.
- سپس، ماشین جاوا یک مرجع به شی ایجاد می کند (در مورد ما مرجع Cat cat است).
- در نهایت، متغیرها مقداردهی اولیه می شوند و سازنده فراخوانی می شود (ما این فرآیند را با جزئیات بیشتری بررسی می کنیم).
این دو نکته اول نباید سؤال خاصی ایجاد کند. تخصیص حافظه یک فرآیند ساده است و تنها دو نتیجه ممکن وجود دارد: یا حافظه وجود دارد یا وجود ندارد :) و ایجاد یک پیوند غیرعادی نیست. اما نکته سوم مجموعه کاملی از عملیات را نشان می دهد که به ترتیب دقیق اجرا شده اند. من طرفدار انباشته کردن تست ها نیستم، اما شما باید این فرآیند را به خوبی درک کنید و باید این توالی از عملیات را به خاطر بسپارید . وقتی در درس های قبلی در مورد فرآیند ایجاد شی صحبت کردیم، شما واقعاً هنوز چیزی در مورد وراثت نمی دانستید، بنابراین توضیح برخی چیزها مشکل ساز بود. حالا شما خیلی چیزها را می دانید و در نهایت می توانیم این سوال را به طور کامل در نظر بگیریم :) بنابراین نکته سوم می گوید "در نهایت متغیرها مقداردهی اولیه می شوند و سازنده فراخوانی می شود. " اما همه اینها به چه ترتیبی اتفاق می افتد؟ برای درک بهتر، بیایید دو کلاس فوق العاده ساده ایجاد کنیم - یک والدین و یک فرزند:
public class Vehicle {
public static int vehicleCounter = 0;
private String description = "Vehicle";
public Vehicle() {
}
public String getDescription() {
return description;
}
}
public class Truck extends Vehicle {
private static int truckCounter = 0;
private int yearOfManufacture;
private String model;
private int maxSpeed;
public Truck(int yearOfManufacture, String model, int maxSpeed) {
this.yearOfManufacture = yearOfManufacture;
this.model = model;
this.maxSpeed = maxSpeed;
Vehicle.vehicleCounter++;
truckCounter++;
}
} کلاس Truckپیاده سازی یک کامیون با فیلدهایی است که سال، مدل و حداکثر سرعت آن را نشان می دهد. اکنون می خواهیم یکی از این شی را ایجاد کنیم:
public class Main {
public static void main(String[] args) throws IOException {
Truck truck = new Truck(2017, "Scania S 500 4x2", 220);
}
} برای ماشین جاوا، فرآیند به صورت زیر خواهد بود:
-
اولین چیزی که اتفاق می افتد این است که متغیرهای استاتیک کلاس
Vehicleمقداردهی اولیه می شوند . بله گفتمVehicleکلاس نهTruck. متغیرهای استاتیک قبل از فراخوانی سازنده ها مقداردهی اولیه می شوند و این کار در کلاس والد شروع می شود. بیایید سعی کنیم این را تأیید کنیم.vehicleCounterفیلدVehicleکلاس را برابر با 10 قرار می دهیم و سعی می کنیم آن را در هر دو سازندهVehicleو نمایش دهیمTruck.public class Vehicle { public static int vehicleCounter = 10; private String description = "Vehicle"; public Vehicle() { System.out.println(vehicleCounter); } public String getDescription() { return description; } } public class Truck extends Vehicle { private static int truckCount = 0; private int yearOfManufacture; private String model; private int maxSpeed; public Truck(int yearOfManufacture, String model, int maxSpeed) { System.out.println(vehicleCounter); this.yearOfManufacture = yearOfManufacture; this.model = model; this.maxSpeed = maxSpeed; Vehicle.vehicleCounter++; truckCount++; } }ما عمداً عبارت println را در همان ابتدای سازنده قرار دادیم
Truckتا مطمئن شویم که فیلدهای کامیون هنگامvehicleCounterنمایش هنوز مقدار دهی اولیه نشده اند.و این هم نتیجه:
10 10 -
بعد از اینکه متغیرهای استاتیک کلاس والد مقداردهی اولیه شد، متغیرهای استاتیک کلاس فرزند مقداردهی اولیه می شوند. در مورد ما، این
truckCounterزمینه کلاس استTruck.بیایید آزمایش دیگری انجام دهیم که در آن سعی می کنیم قبل از مقداردهی اولیه فیلدهای دیگر، مقدار
truckCounterداخل سازنده را نمایش دهیم:Truckpublic class Truck extends Vehicle { private static int truckCounter = 10; private int yearOfManufacture; private String model; private int maxSpeed; public Truck(int yearOfManufacture, String model, int maxSpeed) { System.out.println(truckCounter); this.yearOfManufacture = yearOfManufacture; this.model = model; this.maxSpeed = maxSpeed; Vehicle.vehicleCounter++; truckCounter++; } }همانطور که می بینید، مقدار 10 قبلاً با شروع سازنده به متغیر استاتیک ما اختصاص داده شده است
Truck. -
هنوز وقت سازندگان نرسیده است! مقداردهی اولیه متغیر ادامه دارد. متغیرهای غیراستاتیک کلاس والد در مرحله سوم مقداردهی اولیه می شوند. همانطور که می بینید، وراثت به طور قابل توجهی فرآیند ایجاد یک شی را پیچیده می کند، اما هیچ کاری نمی توانید در مورد آن انجام دهید: فقط باید برخی چیزها را در برنامه نویسی به خاطر بسپارید :)
به عنوان یک آزمایش، میتوانیم مقداری اولیه را به
descriptionمتغیرVehicleکلاس اختصاص دهیم و سپس آن را در سازنده تغییر دهیم.public class Vehicle { public static int vehicleCounter = 10; private String description = "Initial value of the description field"; public Vehicle() { System.out.println(description); description = "Vehicle"; System.out.println(description); } public String getDescription() { return description; } }بیایید
main()روش خود را برای ایجاد یک کامیون اجرا کنیم:public class Main { public static void main(String[] args) throws IOException { Truck truck = new Truck(2017, "Scania S 500 4x2", 220); } }نتیجه زیر را می گیریم:
Initial value of the description field Vehicleاین ثابت می کند که وقتی
Vehicleسازنده شروع می شود بهdescriptionفیلد قبلاً یک مقدار اختصاص داده شده است. -
بالاخره نوبت سازندگان می رسد! به طور دقیق تر، زمان سازنده کلاس پایه فرا رسیده است. در مرحله چهارم فرآیند ایجاد شیء فراخوانی می شود.
تأیید این موضوع نیز نسبتاً آسان است. بیایید خروجی دو خط را به کنسول امتحان کنیم: یکی در
Vehicleسازنده کلاس پایه و دومی درTruckسازنده.Vehicleما باید متقاعد شویم که ابتدا خط داخل نمایش داده می شود:public Vehicle() { System.out.println("Hello from the Vehicle constructor!"); } public Truck(int yearOfManufacture, String model, int maxSpeed) { System.out.println("Hello from the Truck constructor!"); this.yearOfManufacture = yearOfManufacture; this.model = model; this.maxSpeed = maxSpeed; Vehicle.vehicleCounter++; truckCounter++; }ما
main()روش خود را اجرا می کنیم و به نتیجه نگاه می کنیم:Hello from the Vehicle constructor! Hello from the Truck constructor!عالی یعنی ما اشتباه نمیکنیم :) بریم جلو.
-
حالا نوبت مقداردهی اولیه فیلدهای غیراستاتیک کلاس فرزند یعنی
Truckکلاس ماست. فیلدهای بلافاصله داخل کلاس در حال نمونه سازی تا مرحله پنجم مقداردهی اولیه نمی شوند! شگفتآور است، اما درست است :) باز هم، ما یک بررسی ساده انجام میدهیم - درست مانند کلاس والد: مقداری اولیه برای متغیر تعیین میکنیمmaxSpeedو درTruckسازنده بررسی میکنیم که مقدار قبل از شروع سازنده اختصاص داده شده باشد:public class Truck extends Vehicle { private static int truckCounter = 10; private int yearOfManufacture; private String model; private int maxSpeed = 150; public Truck(int yearOfManufacture, String model, int maxSpeed) { System.out.println("Initial value of maxSpeed = " + this.maxSpeed); this.yearOfManufacture = yearOfManufacture; this.model = model; this.maxSpeed = maxSpeed; Vehicle.vehicleCounter++; truckCounter++; } }خروجی کنسول:
Initial value of maxSpeed = 150همانطور که می بینید، زمانی که
Truckسازنده شروع می شود،maxSpeedدر حال حاضر برابر با 150 است! -
سازنده
Truckکلاس فرزند فراخوانی می شود.و تنها در این مرحله، آخر از همه، سازنده کلاسی که ما نمونه سازی می کنیم فراخوانی می شود!
فقط در مرحله ششم مقادیری که به عنوان آرگومان به کامیون خود ارسال می کنیم به فیلدها اختصاص داده می شود.
همانطور که می بینید، "ساخت" یک کامیون، یعنی فرآیند ایجاد شی، ساده نیست. اما به نظر می رسد که ما آن را به کوچکترین قسمت ها تقسیم کرده ایم :)
چرا درک این فرآیند بسیار مهم است؟ تصور کنید که اگر دقیقاً نمی دانستید "زیر کاپوت" چه اتفاقی می افتد، نتایج ایجاد یک شی معمولی چقدر غیرمنتظره می تواند باشد :) حالا زمان بازگشت به دوره و انجام برخی کارها است! موفق باشید و به زودی شما را می بینم! :)
GO TO FULL VERSION