JavaRush /จาวาบล็อก /Random-TH /ตัวสร้างคลาส จาวา JDK 1.5
articles
ระดับ

ตัวสร้างคลาส จาวา JDK 1.5

เผยแพร่ในกลุ่ม
ตัวสร้างคลาส  จาวา JDK 1.5 - 1

ข้อมูลทั่วไปเกี่ยวกับตัวสร้าง

Конструкторเป็นโครงสร้างที่คล้ายกับวิธีการ โดยมีวัตถุประสงค์เพื่อสร้างอินสแตนซ์ของคลาส ลักษณะของนักออกแบบ:
  • ชื่อของตัวสร้างจะต้องตรงกับชื่อของคลาส (ตามแบบแผน ตัวอักษรตัวแรกเป็นตัวพิมพ์ใหญ่ โดยปกติจะเป็นคำนาม)
  • มีตัวสร้างในคลาสใดก็ได้ แม้ว่าคุณจะไม่ได้เขียนสิ่งใดเลย คอมไพเลอร์ Java จะสร้างคอนสตรัคเตอร์เริ่มต้น ซึ่งจะว่างเปล่าและไม่ทำอะไรเลยนอกจากเรียกคอนสตรัคเตอร์ซูเปอร์คลาส
  • Constructor นั้นคล้ายคลึงกับ Method แต่ไม่ใช่ Method และไม่ถือว่าเป็นสมาชิกของ Class ด้วยซ้ำ ดังนั้นจึงไม่สามารถสืบทอดหรือแทนที่ในคลาสย่อยได้
  • ตัวสร้างไม่ได้รับการสืบทอด
  • สามารถมีคอนสตรัคเตอร์ได้หลายตัวในคลาส ในกรณีนี้ คอนสตรัคเตอร์ถูกกล่าวว่ามีภาระมากเกินไป
  • หากคลาสไม่ได้กำหนดคอนสตรัคเตอร์ คอมไพลเลอร์จะเพิ่มคอนสตรัคเตอร์แบบไม่มีพารามิเตอร์ให้กับโค้ดโดยอัตโนมัติ
  • Constructor ไม่มีประเภทที่ส่งคืน ไม่สามารถเป็นประเภทได้voidหากประเภทถูกส่งคืนvoidแสดงว่าไม่ใช่ Constructor อีกต่อไป แต่เป็นเมธอด แม้ว่าจะเป็นเรื่องบังเอิญกับชื่อคลาสก็ตาม
  • ตัวดำเนินการได้รับอนุญาตในตัวสร้างreturnแต่ว่างเปล่าเท่านั้น โดยไม่มีค่าส่งคืนใดๆ
  • Constructor อนุญาตให้ใช้ตัวแก้ไขการเข้าถึง คุณสามารถตั้งค่าตัวแก้ไขตัวใดตัวหนึ่งได้: public, protectedหรือprivateไม่มีตัวแก้ไข
  • ตัวสร้างไม่สามารถมีตัวดัดแปลงabstract, final, nativeหรือstatic;synchronized
  • คำหลักthisอ้างอิงถึงตัวสร้างอื่นในคลาสเดียวกัน หากใช้ การเรียกจะต้องเป็นบรรทัดแรกของตัวสร้าง
  • คำหลักsuperเรียกตัวสร้างของคลาสพาเรนต์ หากใช้ การอ้างอิงจะต้องเป็นบรรทัดแรกของตัวสร้าง
  • ถ้า Constructor ไม่ได้ทำการเรียกsuperConstructor ของคลาสบรรพบุรุษ (โดยมีหรือไม่มีอาร์กิวเมนต์) คอมไพเลอร์จะเพิ่มโค้ดโดยอัตโนมัติเพื่อเรียก Constructor ของคลาสบรรพบุรุษโดยไม่มีอาร์กิวเมนต์

ตัวสร้างเริ่มต้น

มีตัวสร้างในคลาสใดก็ได้ แม้ว่าคุณจะไม่ได้เขียนก็ตาม คอมไพเลอร์ Java จะสร้างคอนสตรัคเตอร์เริ่มต้น Constructor นี้ว่างเปล่าและไม่ได้ทำอะไรนอกจากเรียก Constructor ซูเปอร์คลาส เหล่านั้น. ถ้าคุณเขียน:
public class Example {}
นี่เทียบเท่ากับการเขียน:
public class Example
{
     Example()
     {
          super;
     }
}
ในกรณีนี้ คลาส ancestor ไม่ได้ระบุไว้อย่างชัดเจน และโดยดีฟอลต์ คลาส Java ทั้งหมดสืบทอดคลาสObjectดังนั้นตัวสร้างคลาสจึงถูกเรียกObjectว่า หากคลาสกำหนดคอนสตรัคเตอร์แบบไม่มีพารามิเตอร์ แต่ไม่มีคอนสตรัคเตอร์แบบไร้พารามิเตอร์ที่โอเวอร์โหลด ดังนั้นการเรียกคอนสตรัคเตอร์แบบไร้พารามิเตอร์ถือเป็นข้อผิดพลาด อย่างไรก็ตาม ใน Java ตั้งแต่เวอร์ชัน 1.5 คุณสามารถใช้ตัวสร้างที่มีอาร์กิวเมนต์ความยาวผันแปรได้ และหากมีตัวสร้างที่มีอาร์กิวเมนต์ความยาวผันแปรได้ การเรียกตัวสร้างเริ่มต้นจะไม่เกิดข้อผิดพลาด จะไม่เป็นเพราะอาร์กิวเมนต์ความยาวผันแปรสามารถเว้นว่างได้ ตัวอย่างเช่น ตัวอย่างต่อไปนี้จะไม่คอมไพล์ แต่ถ้าคุณยกเลิกหมายเหตุคอนสตรัคเตอร์ด้วยอาร์กิวเมนต์ความยาวผันแปรได้ มันจะคอมไพล์และรันได้สำเร็จ และส่งผลให้บรรทัดของโค้ดรันDefaultDemo dd = new DefaultDemo(); ตัวสร้างจะถูกเรียกDefaultDemo(int ... v)ว่า โดยปกติแล้วในกรณีนี้จำเป็นต้องใช้ JSDK 1.5 ไฟล์DefaultDemo.java
class DefaultDemo
{
 DefaultDemo(String s)
 {
  System.out.print("DefaultDemo(String)");
 }
 /*
 DefaultDemo(int ... v)
 {
  System.out.println("DefaultDemo(int ...)");
 }
 */

 public static void main(String args[])
 {
  DefaultDemo dd = new DefaultDemo();
 }
}
ผลลัพธ์ของเอาต์พุตของโปรแกรมที่ไม่มีคอนสตรัคเตอร์:
DefaultDemo(int ...)
อย่างไรก็ตาม ในกรณีทั่วไปที่คลาสไม่ได้กำหนดคอนสตรัคเตอร์ใดๆ เลย การเรียกคอนสตรัคเตอร์เริ่มต้น (โดยไม่มีพารามิเตอร์) จะมีความจำเป็น เนื่องจากการแทนที่คอนสตรัคเตอร์เริ่มต้นจะเกิดขึ้นโดยอัตโนมัติ

การสร้างวัตถุและตัวสร้าง

เมื่อสร้างวัตถุ การดำเนินการต่อไปนี้จะดำเนินการตามลำดับ:
  • คลาสอ็อบเจ็กต์จะถูกค้นหาระหว่างคลาสที่ใช้ในโปรแกรมอยู่แล้ว หากไม่มีอยู่ ระบบจะค้นหาในแค็ตตาล็อกและไลบรารีทั้งหมดที่มีอยู่ในโปรแกรม เมื่อค้นพบคลาสในไดเร็กทอรีหรือไลบรารีแล้ว ฟิลด์สแตติกของคลาสจะถูกสร้างขึ้นและเตรียมใช้งาน เหล่านั้น. สำหรับแต่ละคลาส ฟิลด์สแตติกจะเริ่มต้นได้เพียงครั้งเดียวเท่านั้น
  • หน่วยความจำถูกจัดสรรให้กับอ็อบเจ็กต์
  • กำลังเตรียมใช้งานฟิลด์คลาส
  • ตัวสร้างคลาสดำเนินการ
  • ลิงก์ไปยังวัตถุที่สร้างและเตรียมใช้งานจะถูกสร้างขึ้น การอ้างอิงนี้คือค่าของนิพจน์ที่สร้างวัตถุ วัตถุยังสามารถสร้างได้โดยการเรียกเมธอดnewInstance()คลาส java.lang.Classในกรณีนี้ จะใช้ตัวสร้างที่ไม่มีรายการพารามิเตอร์

ตัวสร้างที่โอเวอร์โหลด

ตัวสร้างคลาสเดียวกันสามารถมีชื่อเดียวกันและลายเซ็นต่างกันได้ คุณสมบัตินี้เรียกว่าการรวมกันหรือการโอเวอร์โหลด หากคลาสมีคอนสตรัคเตอร์หลายตัว แสดงว่ามีคอนสตรัคเตอร์โอเวอร์โหลดอยู่

ตัวสร้างแบบพารามิเตอร์

ลายเซ็นต์ของตัวสร้างคือจำนวนและประเภทของพารามิเตอร์ เช่นเดียวกับลำดับของประเภทในรายการพารามิเตอร์ตัวสร้าง ประเภทการคืนสินค้าจะไม่ถูกนำมาพิจารณา ตัวสร้างไม่ส่งคืนพารามิเตอร์ใดๆ คำสั่งนี้อธิบายในแง่หนึ่งว่า Java แยกความแตกต่างระหว่างตัวสร้างหรือเมธอดที่โอเวอร์โหลดอย่างไร Java แยกความแตกต่างวิธีการโอเวอร์โหลดไม่ใช่ตามประเภทการส่งคืน แต่ตามจำนวน ประเภท และลำดับประเภทของพารามิเตอร์อินพุต Constructor ไม่สามารถแม้แต่จะคืนค่า type voidมิฉะนั้นจะกลายเป็นวิธีการปกติ แม้ว่าจะคล้ายกับชื่อคลาสก็ตาม ตัวอย่างต่อไปนี้แสดงให้เห็นถึงสิ่งนี้ ไฟล์VoidDemo.java
class VoidDemo
{
 /**
  * Это конструктор
  */
 VoidDemo()
 {
  System.out.println("Constructor");
 }

 /**
  * А это уже обычный метод, даже не смотря на сходство с
  * именем класса, поскольку имеется возвращаемый тип void
  */
 void VoidDemo()
 {
  System.out.println("Method");
 }

 public static void main(String s[])
 {
  VoidDemo m = new VoidDemo();
 }
}
ผลลัพธ์ที่ได้คือผลลัพธ์ของโปรแกรม:
Constructor
นี่เป็นการพิสูจน์อีกครั้งว่าตัวสร้างเป็นวิธีการที่ไม่มีพารามิเตอร์ส่งคืน อย่างไรก็ตาม ตัว สร้างสามารถกำหนดหนึ่งในสามตัวแก้ไขpublic, privateหรือ protectedและตัวอย่างจะมีลักษณะดังนี้: ไฟล์VoidDemo2.java
class VoidDemo2
{
 /**
  * Это конструктор
  */
 public VoidDemo2()
 {
  System.out.println("Constructor");
 }

 /**
  * А это уже обычный метод, даже не смотря на сходство с
  * именем класса, поскольку имеется возвращаемый тип void
  */
 private void VoidDemo2()
 {
  System.out.println("Method");
 }

 public static void main(String s[])
 {
  VoidDemo2 m = new VoidDemo2();
 }
}
อนุญาตให้เขียนตัวดำเนินการใน Constructor returnได้ แต่จะเขียนได้เฉพาะตัวดำเนินการว่างเท่านั้น โดยไม่มีค่าส่งคืนใดๆ ไฟล์ReturnDemo.java
class ReturnDemo
{
 /**
  * В конструкторе допускается использование оператора
  * return без параметров.
  */
 public ReturnDemo()
 {
  System.out.println("Constructor");
  return;
 }

 public static void main(String s[])
 {
  ReturnDemo r = new ReturnDemo();
 }
}

ตัวสร้างกำหนดพารามิเตอร์ด้วยอาร์กิวเมนต์ที่มีความยาวผันแปรได้

Java SDK 1.5 เปิดตัวเครื่องมือที่รอคอยมานาน - อาร์กิวเมนต์ที่มีความยาวผันแปรได้สำหรับตัวสร้างและวิธีการ ก่อนหน้านี้ เอกสารจำนวนหนึ่งที่แปรผันได้รับการประมวลผลด้วยวิธีที่ไม่สะดวกสองวิธี ประการแรกได้รับการออกแบบมาเพื่อให้แน่ใจว่าจำนวนอาร์กิวเมนต์สูงสุดถูกจำกัดไว้ที่จำนวนน้อยและทราบล่วงหน้า ในกรณีนี้ เป็นไปได้ที่จะสร้างเวอร์ชันที่โอเวอร์โหลดของเมธอด หนึ่งเวอร์ชันสำหรับรายการอาร์กิวเมนต์แต่ละเวอร์ชันที่ส่งไปยังเมธอด วิธีที่สองได้รับการออกแบบมาสำหรับสิ่งที่ไม่ทราบล่วงหน้าและมีข้อโต้แย้งจำนวนมาก ในกรณีนี้ อาร์กิวเมนต์ถูกวางไว้ในอาร์เรย์ และอาร์เรย์นี้ถูกส่งผ่านไปยังเมธอด อาร์กิวเมนต์ความยาวผันแปรมักเกี่ยวข้องกับการยักย้ายภายหลังด้วยการกำหนดค่าเริ่มต้นของตัวแปร สะดวกในการแทนที่การไม่มีตัวสร้างหรืออาร์กิวเมนต์เมธอดบางตัวที่คาดหวังด้วยค่าเริ่มต้น อาร์กิวเมนต์ความยาวผันแปรคืออาร์เรย์ และถือเป็นอาร์เรย์ ตัวอย่างเช่น ตัวสร้างสำหรับคลาสCheckingที่มีจำนวนอาร์กิวเมนต์ที่แปรผันจะมีลักษณะดังนี้:
class Checking
{
 public Checking(int ... n)
 {
 }
}
การรวมอักขระ ... บอกคอมไพเลอร์ว่าจะใช้จำนวนอาร์กิวเมนต์ของตัวแปร และอาร์กิวเมนต์เหล่านี้จะถูกเก็บไว้ในอาร์เรย์ที่มีค่าอ้างอิงอยู่ในตัวแปร n Constructor สามารถเรียกได้ด้วยจำนวนอาร์กิวเมนต์ที่แตกต่างกัน รวมถึงการไม่มีอาร์กิวเมนต์เลยด้วย อาร์กิวเมนต์จะถูกวางในอาร์เรย์โดยอัตโนมัติและส่งผ่าน n หากไม่มีอาร์กิวเมนต์ ความยาวของอาร์เรย์จะเป็น 0 รายการพารามิเตอร์ พร้อมด้วยอาร์กิวเมนต์ความยาวผันแปร ยังสามารถรวมพารามิเตอร์บังคับได้ด้วย ในกรณีนี้ พารามิเตอร์ที่มีจำนวนอาร์กิวเมนต์ที่เปลี่ยนแปลงได้จะต้องเป็นพารามิเตอร์สุดท้ายในรายการพารามิเตอร์ ตัวอย่างเช่น:
class Checking
{
 public Checking(String s, int ... n)
 {
 }
}
ข้อจำกัดที่ชัดเจนมากเกี่ยวข้องกับจำนวนพารามิเตอร์ความยาวผันแปรได้ ต้องมีพารามิเตอร์ความยาวผันแปรได้เพียงตัวเดียวในรายการพารามิเตอร์ ด้วยพารามิเตอร์ที่มีความยาวผันแปรได้สองตัว จึงเป็นไปไม่ได้ที่คอมไพลเลอร์จะกำหนดว่าพารามิเตอร์ตัวหนึ่งสิ้นสุดที่ใดและอีกตัวหนึ่งเริ่มต้นที่ใด ตัวอย่างเช่น:
class Checking
{
 public Checking(String s, int ... n, double ... d) //ОШИБКА!
 {
 }
}
ไฟล์Checking.java ตัวอย่างเช่น มีอุปกรณ์ที่สามารถจดจำป้ายทะเบียนรถและจดจำจำนวนสี่เหลี่ยมของพื้นที่ที่รถแต่ละคันเข้าเยี่ยมชมในระหว่างวัน มีความจำเป็นต้องเลือกจากมวลรวมของรถยนต์ที่บันทึกไว้ซึ่งในระหว่างวันได้ไปเยี่ยมชมจัตุรัสสองแห่งที่กำหนด เช่น 22 และ 15 ตามแผนที่พื้นที่ เป็นเรื่องปกติที่รถยนต์สามารถเยี่ยมชมจัตุรัสหลายแห่งในระหว่างวันหรืออาจจะเพียงแห่งเดียวเท่านั้น แน่นอนว่าจำนวนช่องสี่เหลี่ยมที่เยี่ยมชมนั้นถูกจำกัดด้วยความเร็วทางกายภาพของรถ มาสร้างโปรแกรมขนาดเล็กที่ตัวสร้างคลาสจะใช้เป็นอาร์กิวเมนต์หมายเลขรถยนต์เป็นพารามิเตอร์บังคับและจำนวนกำลังสองที่เยี่ยมชมของพื้นที่ซึ่งจำนวนนี้สามารถเปลี่ยนแปลงได้ ตัวสร้างจะตรวจสอบว่ารถยนต์ปรากฏในช่องสี่เหลี่ยมสองช่องหรือไม่ หากมี ก็แสดงหมายเลขบนหน้าจอ

การส่งพารามิเตอร์ไปยังตัวสร้าง

พารามิเตอร์ในภาษาการเขียนโปรแกรมส่วนใหญ่มีสองประเภท:
  • ประเภทพื้นฐาน (ดั้งเดิม);
  • การอ้างอิงถึงวัตถุ
คำว่า call by value หมายความว่า Constructor ได้รับค่าที่ส่งผ่านไปยังโมดูลที่เรียก ในทางตรงกันข้าม การเรียกโดยการอ้างอิงหมายความว่าตัวสร้างได้รับที่อยู่ของตัวแปรจากผู้เรียก Java ใช้การเรียกตามค่าเท่านั้น ตามค่าพารามิเตอร์และตามค่าลิงก์พารามิเตอร์ Java ไม่ได้ใช้การเรียกโดยการอ้างอิงสำหรับอ็อบเจ็กต์ (แม้ว่าโปรแกรมเมอร์จำนวนมากและผู้แต่งหนังสือบางเล่มจะอ้างสิทธิ์ในสิ่งนี้) เมื่อส่งวัตถุไปยัง Java พารามิเตอร์จะถูกส่งผ่านไม่ได้โดยการอ้างอิงแต่โดยค่าของการอ้างอิงวัตถุ ! ไม่ว่าในกรณีใด Constructor จะได้รับสำเนาค่าของพารามิเตอร์ทั้งหมด ตัวสร้างไม่สามารถทำอะไรกับพารามิเตอร์อินพุตได้:
  • ตัวสร้างไม่สามารถเปลี่ยนค่าของพารามิเตอร์อินพุตของประเภทหลัก (ดั้งเดิม)
  • ตัวสร้างไม่สามารถเปลี่ยนการอ้างอิงพารามิเตอร์อินพุตได้
  • ตัวสร้างไม่สามารถกำหนดการอ้างอิงพารามิเตอร์อินพุตใหม่ให้กับวัตถุใหม่ได้
ตัวสร้างสามารถทำได้ด้วยพารามิเตอร์อินพุต:
  • เปลี่ยนสถานะของวัตถุที่ส่งผ่านเป็นพารามิเตอร์อินพุต
ตัวอย่างต่อไปนี้พิสูจน์ว่าใน Java พารามิเตอร์อินพุตไปยังตัวสร้างจะถูกส่งผ่านโดยค่าอ้างอิงอ็อบเจ็กต์ ตัวอย่างนี้ยังสะท้อนให้เห็นว่า Constructor ไม่สามารถเปลี่ยนการอ้างอิงพารามิเตอร์อินพุตได้ แต่จริงๆ แล้วเปลี่ยนการอ้างอิงสำเนาของพารามิเตอร์อินพุต ไฟล์Empoyee.java
class Employee
{
 Employee(String x, String y)
 {
  String temp = x;
  x = y;
  y = temp;
 }
 public static void main(String args[])
 {
  String name1 = new String("Alice");
  String name2 = new String("Mary");
  Employee a = new Employee(name1, name2);
  System.out.println("name1="+name1);
  System.out.println("name2="+name2);
 }
}
ผลลัพธ์ของโปรแกรมคือ:
name1=Alice
name2=Mary
หาก Java ใช้การเรียกโดยการอ้างอิงเพื่อส่งวัตถุเป็นพารามิเตอร์ ตัวสร้างจะสลับname1และ ในตัวอย่าง name2นี้ ตัวสร้างจะไม่สลับการอ้างอิงวัตถุที่เก็บไว้ในname1และname2ตัวแปร นี่แสดงให้เห็นว่าพารามิเตอร์ตัวสร้างเริ่มต้นได้ด้วยสำเนาของการอ้างอิงเหล่านี้ จากนั้นตัวสร้างจะสลับสำเนา เมื่อตัวสร้างทำงานเสร็จ ตัวแปร x และ y จะถูกทำลาย และตัวแปรดั้งเดิมname1ยังคงname2อ้างอิงถึงออบเจ็กต์ก่อนหน้าต่อไป

การเปลี่ยนพารามิเตอร์ที่ส่งผ่านไปยังตัวสร้าง

ตัวสร้างไม่สามารถแก้ไขพารามิเตอร์ที่ส่งผ่านของประเภทพื้นฐานได้ อย่างไรก็ตาม Constructor สามารถแก้ไขสถานะของอ็อบเจ็กต์ที่ส่งผ่านเป็นพารามิเตอร์ได้ ตัวอย่างเช่น พิจารณาโปรแกรมต่อไปนี้: ไฟล์Salary1.java
class Salary1
{
 Salary1(int x)
 {
  x = x * 3;
  System.out.println("x="+x);
 }
 public static void main(String args[])
 {
  int value = 1000;
  Salary1 s1 = new Salary1(value);
  System.out.println("value="+value);
 }
}
ผลลัพธ์ของโปรแกรมคือ:
x=3000
value=1000
แน่นอนว่าวิธีนี้จะไม่เปลี่ยนพารามิเตอร์ประเภทหลัก ดังนั้น หลังจากการเรียก Constructor แล้ว ค่าของตัวแปรจะvalueยังคงเท่ากับ 1000โดยพื้นฐานแล้วมีสามสิ่งที่เกิดขึ้น:
  1. ตัวแปรxจะเริ่มต้นด้วยสำเนาของค่าพารามิเตอร์value(เช่น ตัวเลข1000)
  2. ค่าของตัวแปร เป็นสามเท่า - ตอน นี้xเท่ากับ 3000อย่างไรก็ตาม ค่าของตัวแปร ยัง คงvalueเท่ากับ1000
  3. ตัวสร้างสิ้นสุดและตัวแปรxจะไม่ถูกใช้อีกต่อไป
ในตัวอย่างต่อไปนี้ เงินเดือนของพนักงานจะเพิ่มเป็นสามเท่าได้สำเร็จ เนื่องจากค่าของการอ้างอิงออบเจ็กต์ถูกส่งผ่านเป็นพารามิเตอร์ไปยังเมธอด ไฟล์Salary2.java
class Salary2
{
 int value = 1000;
 Salary2()
 {
 }
 Salary2(Salary2 x)
 {
  x.value = x.value * 3;
 }
 public static void main(String args[])
 {
  Salary2 s1 = new Salary2();
  Salary2 s2 = new Salary2(s1);
  System.out.println("s1.value=" +s1.value);
  System.out.println("s2.value="+s2.value);
 }
}
ผลลัพธ์ของโปรแกรมคือ:
s1.value=3000
s2.value=1000
ค่าของการอ้างอิงวัตถุถูกใช้เป็นพารามิเตอร์ เมื่อดำเนินการบรรทัดSalary2 s2 = new Salary2(s1); ตัวสร้างSalary2(Salary x)จะถูกส่งผ่านค่าของการอ้างอิงไปยังวัตถุตัวแปรs1และตัวสร้างจะเพิ่มเงินเดือนเป็นสามเท่าอย่างมีประสิทธิภาพs1.valueเนื่องจากแม้แต่สำเนา(Salary x)ที่สร้างขึ้นภายในตัวสร้างก็ชี้ไปที่วัตถุs1ตัวแปร

ตัวสร้างกำหนดพารามิเตอร์โดยดั้งเดิม

หากพารามิเตอร์ของคอนสตรัคเตอร์ที่โอเวอร์โหลดใช้แบบดั้งเดิมที่สามารถจำกัดให้แคบลงได้ (เช่นint <- double) ดังนั้นการเรียกเมธอดที่มีค่าที่แคบลงก็เป็นไปได้ แม้ว่าจะไม่มีวิธีการใดที่โอเวอร์โหลดด้วยพารามิเตอร์ดังกล่าวก็ตาม ตัวอย่างเช่น: ไฟล์Primitive.java
class Primitive
{
 Primitive(double d)
 {
  d = d + 10;
  System.out.println("d="+d);
 }
 public static void main(String args[])
 {
  int i = 20;
  Primitive s1 = new Primitive(i);
 }
}
ผลลัพธ์ของโปรแกรมคือ:
d=30.0
แม้ว่าคลาสPrimitiveจะไม่มีคอนสตรัคเตอร์ที่มีพารามิเตอร์ประเภทintแต่คอนสตรัคเตอร์ที่มีพารามิเตอร์อินพุตจะทำงานdoubleได้ ก่อนที่จะเรียก Constructor ตัวแปรiจะถูกขยายจากประเภทหนึ่งintไปอีกประเภทdoubleหนึ่ง ตัวเลือกตรงกันข้าม เมื่อตัวแปรiเป็นประเภทdoubleและตัวสร้างจะมีเพียงพารามิเตอร์intในสถานการณ์นี้อาจทำให้เกิดข้อผิดพลาดในการคอมไพล์

การเรียกตัวสร้างและตัวดำเนินการnew

ตัวสร้างจะถูกเรียกโดยตัวดำเนินการnewเสมอ เมื่อตัวสร้างถูกเรียกพร้อมกับตัวดำเนินการnewตัวสร้างจะสร้างการอ้างอิงไปยังวัตถุใหม่เสมอ เป็นไปไม่ได้ที่จะบังคับให้ Constructor สร้างการอ้างอิงถึงวัตถุที่มีอยู่แล้ว แทนที่จะอ้างอิงถึงวัตถุใหม่ ยกเว้นโดยการแทนที่วัตถุที่ถูกดีซีเรียลไลซ์ และด้วยตัวดำเนินการใหม่ แทนที่จะอ้างอิงถึงออบเจ็กต์ใหม่ มันเป็นไปไม่ได้ที่จะสร้างการอ้างอิงถึงออบเจ็กต์ที่มีอยู่แล้ว ตัวอย่างเช่น: ไฟล์Salary3.java
class Salary3
{
 int value = 1000;
 Salary3()
 {
 }
 Salary3(Salary3 x)
 {
  x.value = x.value * 3;
 }
 public static void main(String args[])
 {
  Salary3 s1 = new Salary3();
  System.out.println("First object creation: "+s1.value);

  Salary3 s2 = new Salary3(s1);
  System.out.println("Second object creation: "+s2.value);
  System.out.println("What's happend with first object?:"+s1.value);

  Salary3 s3 = new Salary3(s1);
  System.out.println("Third object creation: "+s3.value);
  System.out.println("What's happend with first object?:"+s1.value);
 }
}
ผลลัพธ์ของโปรแกรมคือ:
First object creation: 1000
Second object creation: 1000
What's happend with first object?: 3000
Third object creation: 1000
What's happend with first object?: 9000
ขั้นแรกให้ใช้เส้นSalary3 s1 = new Salary3(); มีการสร้างวัตถุใหม่ ต่อไปถ้าใช้เส้นSalary3 s2 = new Salary3(s1); หรือสตริงSalary3 s3 = new Salary3(s1); มันเป็นไปได้ที่จะสร้างลิงค์ไปยังออบเจ็กต์ที่มีอยู่แล้ว จากนั้นs1.value s2.valueพวกเขาs3.valueจะเก็บค่า1000เดียวกัน จริงๆแล้วอยู่ในบรรทัดSalary3 s2 = new Salary3(s1); ออบเจ็กต์ใหม่สำหรับตัวแปรจะถูกสร้างขึ้นs2และสถานะของออบเจ็กต์สำหรับตัวแปรจะเปลี่ยนs1โดยส่งค่าอ้างอิงไปยังออบเจ็กต์ในพารามิเตอร์ตัวสร้าง ซึ่งสามารถตรวจสอบได้ด้วยผลลัพธ์ที่ออกมา และเมื่อดำเนินการบรรทัดSalary3 s3 = new Salary3(s1); ออบเจ็กต์ใหม่สำหรับตัวแปรจะถูกสร้างขึ้นs3และสถานะของออบเจ็กต์สำหรับตัวแปรจะเปลี่ยนอีกs1ครั้ง

ตัวสร้างและบล็อกการเริ่มต้น ลำดับของการดำเนินการเมื่อเรียกตัวสร้าง

ส่วนการสร้างวัตถุและตัวสร้างแสดงรายการการดำเนินการทั่วไปที่ดำเนินการเมื่อสร้างวัตถุ หนึ่งในนั้นคือกระบวนการในการเริ่มต้นฟิลด์คลาสและการทำงานกับตัวสร้างคลาส ซึ่งในทางกลับกันก็มีคำสั่งภายในด้วย:
  1. ช่องข้อมูลทั้งหมดจะเริ่มต้นเป็นค่าเริ่มต้น (0, false หรือ null)
  2. ตัวเริ่มต้นฟิลด์และบล็อกการเริ่มต้นทั้งหมดจะดำเนินการตามลำดับที่ระบุไว้ในการประกาศคลาส
  3. ถ้าคอนสตรัคเตอร์อื่นถูกเรียกในบรรทัดแรกของคอนสตรัคเตอร์ ดังนั้นคอนสตรัคเตอร์ที่ถูกเรียกจะถูกดำเนินการ
  4. เนื้อความของตัวสร้างถูกดำเนินการ
Constructor เกี่ยวข้องกับการกำหนดค่าเริ่มต้น เนื่องจากใน Java มีสามวิธีในการเริ่มต้นฟิลด์ในคลาส:
  • กำหนดค่าในการประกาศ
  • กำหนดค่าในบล็อกการเริ่มต้น
  • ตั้งค่าในตัวสร้าง
โดยปกติแล้ว คุณจะต้องจัดระเบียบโค้ดเริ่มต้นเพื่อให้เข้าใจได้ง่าย คลาสต่อไปนี้เป็นตัวอย่าง:
class Initialization
{
 int i;
 short z = 10;
 static int x;
 static float y;
 static
 {
  x = 2000;
  y = 3.141;
 }
 Initialization()
 {
  System.out.println("i="+i);
  System.out.println("z="+z);
  z = 20;
  System.out.println("z="+z);
 }
}
ในตัวอย่างข้างต้น ตัวแปรจะถูกเตรียมใช้งานตามลำดับต่อไปนี้: ตัวแปรคงที่จะถูกเตรียมใช้งานก่อนxด้วยyค่าเริ่มต้น ถัดไป บล็อกการเริ่มต้นแบบคงที่จะถูกดำเนินการ จากนั้นตัวแปรจะถูกเตรียมใช้งานiเป็นค่าเริ่มต้นและตัวแปรจะถูกเตรียมใช้zงาน จากนั้นนักออกแบบก็เริ่มทำงาน การเรียกตัวสร้างคลาสไม่ควรขึ้นอยู่กับลำดับในการประกาศฟิลด์ สิ่งนี้อาจนำไปสู่ข้อผิดพลาด

ผู้สร้างและมรดก

ตัวสร้างไม่ได้รับการสืบทอด ตัวอย่างเช่น:
public class Example
{
 Example()
 {
 }
 public void sayHi()
 {
  system.out.println("Hi");
 }
}

public class SubClass extends Example
{
}
คลาสSubClassจะสืบทอดวิธีการsayHi()ที่กำหนดไว้ในคลาสพาเรนต์ โดยอัตโนมัติ ในเวลาเดียวกัน ตัวสร้างของ Example()คลาสพาเรนต์ไม่ได้รับการสืบทอดโดยผู้สืบทอดSubClass

คำสำคัญthisในตัวสร้าง

ตัวสร้างใช้thisเพื่ออ้างถึงตัวสร้างอื่นในคลาสเดียวกัน แต่มีรายการพารามิเตอร์ที่แตกต่างกัน หาก Constructor ใช้คีย์เวิร์ด คีย์เวิร์ดthisนั้นจะต้องอยู่ในบรรทัดแรก การเพิกเฉยต่อกฎนี้จะส่งผลให้เกิดข้อผิดพลาดของคอมไพเลอร์ ตัวอย่างเช่น: ไฟล์ThisDemo.java
public class ThisDemo
{
 String name;
 ThisDemo(String s)
 {
  name = s;
     System.out.println(name);
 }
 ThisDemo()
 {
  this("John");
 }
 public static void main(String args[])
 {
  ThisDemo td1 = new ThisDemo("Mary");
  ThisDemo td2 = new ThisDemo();
 }
}
ผลลัพธ์ของโปรแกรมคือ:
Mary
John
ในตัวอย่างนี้ มีตัวสร้างสองตัว อันแรกได้รับอาร์กิวเมนต์สตริง อันที่สองไม่ได้รับอาร์กิวเมนต์ใด ๆ เพียงแค่เรียกตัวสร้างตัวแรกโดยใช้ชื่อเริ่มต้น "John" ดังนั้นคุณสามารถใช้ตัวสร้างเพื่อเริ่มต้นค่าฟิลด์อย่างชัดเจนและเป็นค่าเริ่มต้นซึ่งมักจำเป็นในโปรแกรม

คำสำคัญsuperในตัวสร้าง

ตัวสร้างใช้superเพื่อเรียกตัวสร้างคลาสพิเศษ หาก Constructor ใช้superการเรียกนี้จะต้องอยู่ในบรรทัดแรก มิฉะนั้นคอมไพเลอร์จะเกิดข้อผิดพลาด ด้านล่างนี้เป็นตัวอย่าง: ไฟล์SuperClassDemo.java
public class SuperClassDemo
{
 SuperClassDemo()
 {
 }
}

class Child extends SuperClassDemo
{
 Child()
 {
  super();
 }
}
ในตัวอย่างง่ายๆ นี้ Constructor Child()มีการเรียกsuper()ที่สร้างอินสแตนซ์ของคลาสSuperClassDemoนอกเหนือจากChildคลาส เนื่องจากsuperจะต้องเป็นคำสั่งแรกที่ดำเนินการในตัวสร้างคลาสย่อย ลำดับนี้จึงเหมือนกันเสมอและไม่ขึ้นอยู่กับsuper()ว่า หากไม่ได้ใช้ ตัวสร้างดีฟอลต์ (ไม่มีพารามิเตอร์) ของแต่ละซูเปอร์คลาส เริ่มต้นด้วยคลาสพื้นฐานจะถูกดำเนินการก่อน โปรแกรมต่อไปนี้สาธิตเมื่อตัวสร้างถูกดำเนินการ ไฟล์Call.java
//Создать суперкласс A
class A
{
 A()
 {
  System.out.println("Inside A constructor.");
 }
}

//Создать подкласс B, расширяющий класс A
class B extends A
{
 B()
 {
  System.out.println("Inside B constructor.");
 }
}

//Создать класс (C), расширяющий класс В
class C extends B
{
 C()
 {
  System.out.println("Inside C constructor.");
 }
}

class Call
{
 public static void main(String args[])
 {
  C c = new C();
 }
}
ผลลัพธ์จากโปรแกรมนี้:
Inside A constructor.
Inside B constructor.
Inside C constructor.
คอนสตรัคเตอร์ถูกเรียกตามลำดับการอยู่ใต้บังคับบัญชาของคลาส สิ่งนี้สมเหตุสมผล เนื่องจากซูเปอร์คลาสไม่มีความรู้เกี่ยวกับคลาสย่อยใดๆ การกำหนดค่าเริ่มต้นใดๆ ที่จำเป็นต้องดำเนินการจึงแยกจากกัน หากเป็นไปได้ ควรนำหน้าการเริ่มต้นใดๆ ที่ดำเนินการโดยคลาสย่อย นั่นเป็นเหตุผลที่ควรทำก่อน

ตัวสร้างที่ปรับแต่งได้

กลไกการระบุประเภทรันไทม์เป็นหนึ่งในหลักการหลักที่มีประสิทธิภาพของภาษา Java ที่ใช้ความหลากหลาย อย่างไรก็ตาม กลไกดังกล่าวไม่ได้ป้องกันผู้พัฒนาจากการแคสต์ประเภทที่เข้ากันไม่ได้ในบางกรณี กรณีที่พบบ่อยที่สุดคือการจัดการกลุ่มของอ็อบเจ็กต์ ประเภทต่างๆ ซึ่งไม่ทราบล่วงหน้าและถูกกำหนด ณ รันไทม์ เนื่องจากข้อผิดพลาดที่เกี่ยวข้องกับความไม่เข้ากันของประเภทจะปรากฏเฉพาะในขั้นตอนรันไทม์เท่านั้น จึงทำให้ค้นหาและกำจัดได้ยาก การแนะนำประเภทแบบกำหนดเองใน Java 2 5.0 จะย้ายข้อผิดพลาดเหล่านี้บางส่วนจากรันไทม์ไปจนถึงเวลาคอมไพล์ และจัดเตรียมความปลอดภัยของประเภทที่ขาดหายไปบางส่วน ไม่จำเป็นต้องหล่อแบบที่ชัดเจนเมื่อย้ายจากแบบไปObjectเป็นแบบคอนกรีต ควรจำไว้ว่าเครื่องมือปรับแต่งประเภทใช้งานได้กับอ็อบเจ็กต์เท่านั้น และไม่ใช้กับชนิดข้อมูลดั้งเดิมที่อยู่นอกแผนผังการสืบทอดคลาส ด้วยประเภทที่กำหนดเอง การแคสต์ทั้งหมดจะดำเนินการโดยอัตโนมัติและอยู่เบื้องหลัง วิธีนี้ช่วยให้คุณป้องกันประเภทที่ไม่ตรงกันและนำโค้ดกลับมาใช้ใหม่ได้บ่อยขึ้นมาก ประเภทที่กำหนดเองสามารถใช้ในตัวสร้างได้ ตัวสร้างสามารถกำหนดเองได้แม้ว่าคลาสของพวกเขาจะไม่ใช่ประเภทแบบกำหนดเองก็ตาม ตัวอย่างเช่น:
class GenConstructor
{
 private double val;
 <T extends Number> GenConstructor(T arg)
 {
   val = arg.doubleValue();
 }

 void printValue()
 {
  System.out.println("val: "+val);
 }
}

class GenConstructorDemo
{
 public static void main(String args[])
 {
  GenConstructor gc1 = new GenConstructor(100);
  GenConstructor gc2 = new GenConstructor(123.5F);

  gc1.printValue();
  gc2.printValue();
 }
}
เนื่องจาก Constructor GenConstructorระบุพารามิเตอร์ประเภทแบบกำหนดเองที่ต้องเป็นคลาสที่ได้รับมาจาก class Numberจึงสามารถเรียกจากคลาสใดก็ได้
ความคิดเห็น
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION