เราจะวิเคราะห์ตั้งแต่ต้นจนจบ: วิธีเรียกคอนสตรัคเตอร์ วิธีและในฟิลด์คำสั่งใด (รวมถึงฟิลด์สแตติก) เริ่มต้นอย่างไร ฯลฯ ก่อนหน้านี้เราได้กล่าวถึงบางประเด็นที่กล่าวถึงในบทความ ดังนั้นคุณจึงสามารถมองข้าม วัสดุบน ตัวสร้าง คลาสพื้นฐาน ก่อนอื่น เรามานึกถึงวิธีการสร้างวัตถุกันก่อน คุณคงจำได้ดีว่ากระบวนการนี้มองจากมุมมองของนักพัฒนาอย่างไร เขาสร้างคลาส เขียนnewและทุกอย่างก็พร้อม :) ที่นี่เราจะพูดถึงสิ่งที่เกิดขึ้นภายในคอมพิวเตอร์และเครื่อง Java เมื่อเราเขียน ตัวอย่างเช่น:
Cat cat = new Cat();
เราเคยพูดถึงเรื่องนี้มาก่อนแล้ว แต่ในกรณีที่เราจะเตือนคุณ:
- ขั้นแรกให้จัดสรรหน่วยความจำสำหรับจัดเก็บวัตถุ
- ถัดไป เครื่อง Java สร้างการอ้างอิงถึงวัตถุ (ในกรณีของเรา การอ้างอิงคือ 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);
}
}
สำหรับเครื่อง Java กระบวนการจะมีลักษณะดังนี้:
-
สิ่งแรกที่เกิดขึ้นคือตัวแปรสแตติกของ
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ภายในTruckคอนสตรัคเตอร์ก่อนที่ฟิลด์อื่นๆ จะเริ่มต้น:public 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