JavaRush /จาวาบล็อก /Random-TH /ตัวสร้างใน Java

ตัวสร้างใน Java

เผยแพร่ในกลุ่ม
สวัสดี! วันนี้เราจะดูหัวข้อที่สำคัญมากที่เกี่ยวข้องกับวัตถุของเรา ที่นี่เราสามารถพูดได้ว่าคุณจะได้ใช้ความรู้นี้ในการทำงานจริงทุกวัน! เราจะพูดถึงตัวสร้าง คุณอาจได้ยินคำนี้เป็นครั้งแรก แต่จริงๆ แล้วคุณอาจเคยใช้ Constructor มาก่อน แต่คุณไม่ได้สังเกตด้วยตัวเอง :) เราจะเห็นสิ่งนี้ในภายหลัง

Constructor ใน Java คืออะไร และเหตุใดจึงจำเป็น

ลองดูสองตัวอย่าง
public class Car {

   String model;
   int maxSpeed;

   public static void main(String[] args) {

       Car bugatti = new Car();
       bugatti.model = "Bugatti Veyron";
       bugatti.maxSpeed = 407;

   }
}
เราสร้างรถของเราและกำหนดรุ่นและความเร็วสูงสุด อย่างไรก็ตาม ในโครงการจริง วัตถุCarจะมีมากกว่า 2 ฟิลด์อย่างชัดเจน และเช่น 16 ฟิลด์!
public class Car {

   String model;//model
   int maxSpeed;//max speed
   int wheels;// disk width
   double engineVolume;//engine capacity
   String color;//color
   int yearOfIssue;//year of issue
   String ownerFirstName;//Owner's name
   String ownerLastName;//owner's last name
   long price;//price
   boolean isNew;//new or not
   int placesInTheSalon;//number of seats in the cabin
   String salonMaterial;// interior material
   boolean insurance;//is it insured
   String manufacturerCountry;//manufacturer country
   int trunkVolume;// trunk volume
   int accelerationTo100km;//acceleration to 100 km/h in seconds


   public static void main(String[] args) {
       Car bugatti = new Car();

       bugatti.color = "blue";
       bugatti.accelerationTo100km = 3;
       bugatti.engineVolume = 6.3;
       bugatti.manufacturerCountry = "Italy";
       bugatti.ownerFirstName = "Amigo";
       bugatti.yearOfIssue = 2016;
       bugatti.insurance = true;
       bugatti.price = 2000000;
       bugatti.isNew = false;
       bugatti.placesInTheSalon = 2;
       bugatti.maxSpeed = 407;
       bugatti.model = "Bugatti Veyron";

   }

}
เราได้สร้างวัตถุรถยนต์ ใหม่ ปัญหาหนึ่ง: เรามี 16 ฟิลด์ แต่เราเริ่มต้นได้เพียง 12 เท่านั้น ! ลองใช้รหัสเพื่อค้นหาสิ่งที่เราลืม! ไม่ง่ายเลยใช่ไหม? ในสถานการณ์เช่นนี้ โปรแกรมเมอร์สามารถทำผิดพลาดได้อย่างง่ายดายและข้ามการเริ่มต้นบางฟิลด์ไป ผลที่ได้คือพฤติกรรมของโปรแกรมจะผิดพลาด:
public class Car {

   String model;//model
   int maxSpeed;//max speed
   int wheels;// disk width
   double engineVolume;//engine capacity
   String color;//color
   int yearOfIssue;//year of issue
   String ownerFirstName;//Owner's name
   String ownerLastName;//owner's last name
   long price;//price
   boolean isNew;//new or not
   int placesInTheSalon;//number of seats in the cabin
   String salonMaterial;// interior material
   boolean insurance;//is it insured
   String manufacturerCountry;//manufacturer country
   int trunkVolume;// trunk volume
   int accelerationTo100km;//acceleration to 100 km/h in seconds


   public static void main(String[] args) {
       Car bugatti = new Car();

       bugatti.color = "blue";
       bugatti.accelerationTo100km = 3;
       bugatti.engineVolume = 6.3;
       bugatti.manufacturerCountry = "Italy";
       bugatti.ownerFirstName = "Amigo";
       bugatti.yearOfIssue = 2016;
       bugatti.insurance = true;
       bugatti.price = 2000000;
       bugatti.isNew = false;
       bugatti.placesInTheSalon = 2;
       bugatti.maxSpeed = 407;
       bugatti.model = "Bugatti Veyron";

       System.out.println("Model Bugatti Veyron. Engine size - " + bugatti.engineVolume + ", trunk - " + bugatti.trunkVolume + ", salon is made of" + bugatti.salonMaterial +
       ", disc width - " + bugatti.wheels + ". Was acquired in 2018 by Mr. " + bugatti.ownerLastName);

   }

}
เอาต์พุตคอนโซล:
รุ่นบูกัตติ เวย์รอน การกระจัดของเครื่องยนต์ - 6.3, ท้ายรถ - 0, ภายในเป็นโมฆะ, ความกว้างขอบล้อ - 0 ถูกซื้อในปี 2018 โดย Mr. null
ผู้ซื้อของคุณซึ่งจ่ายเงิน 2 ล้านเหรียญสหรัฐเพื่อซื้อรถยนต์คันหนึ่งจะไม่ชอบการถูกเรียกว่า “มิสเตอร์นัล” อย่างแน่นอน! แต่จริงๆ แล้วในท้ายที่สุด โปรแกรมของเราจบลงด้วยวัตถุที่สร้างขึ้นอย่างไม่ถูกต้อง - รถที่มีขอบล้อกว้าง 0 (นั่นคือไม่มีขอบเลย) ท้ายรถหายไป ภายในทำจากวัสดุที่ไม่รู้จัก และแม้กระทั่งเป็นของคนที่ไม่รู้จัก . เราคงจินตนาการได้ว่าข้อผิดพลาดดังกล่าวเกิดขึ้นได้อย่างไรในขณะที่โปรแกรมกำลังทำงานอยู่! เราจำเป็นต้องหลีกเลี่ยงสถานการณ์ดังกล่าว เราต้องการให้โปรแกรมของเรามีข้อจำกัด เช่น เมื่อสร้างวัตถุยานพาหนะใหม่ จะต้องระบุรุ่นและความเร็วสูงสุดเสมอ มิฉะนั้น ไม่อนุญาตให้สร้างวัตถุ ฟังก์ชั่นตัวสร้างสามารถรับมือกับงานนี้ได้อย่างง่ายดาย พวกเขาได้รับชื่อด้วยเหตุผล Constructor จะสร้าง "โครงกระดูก" ของคลาส ซึ่งแต่ละอ็อบเจ็กต์ใหม่ของคลาสจะต้องสอดคล้องกัน เพื่อความสะดวก เราจะกลับไปใช้คลาสCar เวอร์ชันที่เรียบง่ายกว่า ซึ่งมีสองฟิลด์ จากข้อกำหนดของเรา Constructor สำหรับ คลาส Carจะมีลักษณะดังนี้:
public Car(String model, int maxSpeed) {
   this.model = model;
   this.maxSpeed = maxSpeed;
}
และการสร้างวัตถุตอนนี้มีลักษณะดังนี้:
public static void main(String[] args) {
   Car bugatti = new Car("Bugatti Veyron", 407);
}
ใส่ใจคอนสตรัคเตอร์ถูกสร้างขึ้นอย่างไร คล้ายกับวิธีทั่วไป แต่ไม่มีประเภทการคืนสินค้า ในกรณีนี้ ชื่อคลาสจะถูกระบุในตัวสร้าง พร้อมด้วยตัวพิมพ์ใหญ่ด้วย ในกรณีของเรา - รถยนต์ . นอกจากนี้ ตัวสร้างยังใช้คีย์เวิร์ดใหม่สำหรับคุณthis “นี่” ในภาษาอังกฤษ แปลว่า “นี่, นี้” คำนี้หมายถึงวัตถุเฉพาะ รหัสในตัวสร้าง:
public Car(String model, int maxSpeed) {
   this.model = model;
   this.maxSpeed = maxSpeed;
}
สามารถแปลได้เกือบตามตัวอักษร: " modelสำหรับเครื่องนี้ (ซึ่งเรากำลังสร้างอยู่) =อาร์กิวเมนต์modelซึ่งระบุไว้ใน Constructor maxSpeed ​​​​สำหรับเครื่องนี้ (ซึ่งเรากำลังสร้าง) =อาร์กิวเมนต์maxSpeed ​​​​ซึ่ง ระบุไว้ในตัวสร้าง” นี่คือสิ่งที่เกิดขึ้น:
public class Car {

   String model;
   int maxSpeed;

   public Car(String model, int maxSpeed) {
       this.model = model;
       this.maxSpeed = maxSpeed;
   }

   public static void main(String[] args) {
       Car bugatti = new Car("Bugatti Veyron", 407);
       System.out.println(bugatti.model);
       System.out.println(bugatti.maxSpeed);
   }

}
เอาต์พุตคอนโซล:
บูกัตติ เวย์รอน 407
ตัวสร้างกำหนดค่าที่ต้องการสำเร็จแล้ว คุณอาจสังเกตเห็นว่า Constructor นั้นคล้ายกับวิธีปกติมาก! มันเป็นอย่างนั้น: Constructor เป็นวิธีการ เฉพาะเจาะจงเพียงเล็กน้อย :) เช่นเดียวกับในวิธีการ เราส่งพารามิเตอร์ไปยัง Constructor ของเรา และเหมือนกับการเรียก method การเรียก Constructor จะไม่ทำงานถ้าคุณไม่ระบุ:
public class Car {

   String model;
   int maxSpeed;

   public Car(String model, int maxSpeed) {
       this.model = model;
       this.maxSpeed = maxSpeed;
   }

   public static void main(String[] args) {
       Car bugatti = new Car(); //error!
   }

}
คุณเห็นไหมว่านักออกแบบทำสิ่งที่เราพยายามทำให้สำเร็จ ตอนนี้คุณไม่สามารถสร้างรถที่ไม่มีความเร็วหรือไม่มีโมเดลได้! ความคล้ายคลึงกันระหว่างตัวสร้างและวิธีการไม่ได้จบเพียงแค่นั้น เช่นเดียวกับวิธีการ ตัวสร้างสามารถโอเวอร์โหลดได้ ลองนึกภาพคุณมีแมว 2 ตัวที่บ้าน คุณเอาตัวหนึ่งมาเป็นลูกแมว และนำตัวที่สองกลับบ้านจากถนนเมื่อโตเป็นผู้ใหญ่ และคุณไม่รู้ว่ามันอายุเท่าไหร่ ซึ่งหมายความว่าโปรแกรมของเราควรจะสามารถสร้างแมวได้สองประเภท - พร้อมชื่อและอายุสำหรับแมวตัวแรก และด้วยชื่อเท่านั้น - สำหรับแมวตัวที่สอง เมื่อต้องการทำเช่นนี้ เราจะโอเวอร์โหลดคอนสตรัคเตอร์:
public class Cat {

   String name;
   int age;

   //for the first cat
   public Cat(String name, int age) {
       this.name = name;
       this.age = age;
   }

   //for the second cat
   public Cat(String name) {
       this.name = name;
   }

   public static void main(String[] args) {

       Cat barsik = new Cat("Barsik", 5);
       Cat streetCatNamedBob = new Cat("Bob");
   }

}
สำหรับตัวสร้างดั้งเดิมที่มีพารามิเตอร์ "ชื่อ" และ "อายุ" เราได้เพิ่มอีกหนึ่งตัวด้วยชื่อเท่านั้น เราใช้วิธีการมากเกินไปในลักษณะเดียวกันในบทเรียนก่อนหน้านี้ ตอนนี้เราสามารถสร้างแมวทั้งสองเวอร์ชันได้สำเร็จแล้ว :) เหตุใดจึงจำเป็นต้องมีตัวสร้าง?  - 2คุณจำตอนเริ่มการบรรยายที่เราบอกว่าคุณเคยใช้คอนสตรัคเตอร์มาแล้ว แต่คุณไม่ได้สังเกตเลย นี่เป็นเรื่องจริง ความจริงก็คือทุกคลาสใน Java มีสิ่งที่เรียกว่าตัวสร้างเริ่มต้น ไม่มีข้อโต้แย้งใด ๆ แต่จะเริ่มทำงานทุกครั้งที่มีการสร้างวัตถุของคลาสใด ๆ
public class Cat {

   public static void main(String[] args) {

       Cat barsik = new Cat(); //this is where the default constructor worked
   }
}
เมื่อมองแวบแรกสิ่งนี้จะไม่สังเกตเห็นได้ชัดเจน เราสร้างวัตถุแล้วสร้างมันขึ้นมา งานของนักออกแบบอยู่ที่ไหน? หากต้องการดูสิ่งนี้ ให้เขียน Constructor เปล่าสำหรับ คลาส Cat ด้วยมือของเราเอง และภายในนั้นเราจะพิมพ์วลีบางส่วนลงในคอนโซล หากแสดงขึ้น แสดงว่าคอนสตรัคเตอร์ทำงานแล้ว
public class Cat {

   public Cat() {
       System.out.println("Created a cat!");
   }

   public static void main(String[] args) {

       Cat barsik = new Cat(); //this is where the default constructor worked
   }
}
เอาต์พุตคอนโซล:
พวกเขาสร้างแมว!
นี่คือคำยืนยัน! Constructor เริ่มต้นมักจะปรากฏอยู่ในคลาสของคุณอย่างมองไม่เห็นเสมอ แต่คุณจำเป็นต้องรู้คุณสมบัติอีกอย่างหนึ่งของมัน Constructor เริ่มต้นจะหายไปจากคลาสเมื่อคุณสร้าง Constructor บางตัวพร้อมอาร์กิวเมนต์ ข้อพิสูจน์นี้ในความเป็นจริงเราได้เห็นแล้วข้างต้น ที่นี่ในรหัสนี้:
public class Cat {

   String name;
   int age;

   public Cat(String name, int age) {
       this.name = name;
       this.age = age;
   }

   public static void main(String[] args) {

       Cat barsik = new Cat(); //error!
   }
}
เราไม่สามารถสร้าง cat โดยไม่มีชื่อและอายุได้เนื่องจากเรากำหนด Constructor สำหรับCat : string + number ตัวสร้างเริ่มต้นหายไปจากคลาสทันทีหลังจากนี้ ดังนั้น อย่าลืมว่า: หากคุณต้องการ Constructor หลายตัวในคลาสของคุณ รวมถึง Constructor ว่างด้วย คุณจะต้องสร้างมันแยกกัน เช่น เรากำลังสร้างโปรแกรมสำหรับคลินิกสัตวแพทย์ คลินิกของเราต้องการทำความดีและช่วยเหลือแมวจรจัดซึ่งเราไม่ทราบชื่อหรืออายุ จากนั้นโค้ดของเราควรมีลักษณะดังนี้:
public class Cat {

   String name;
   int age;

   //for domestic cats
   public Cat(String name, int age) {
       this.name = name;
       this.age = age;
   }

   //for street cats
   public Cat() {
   }

   public static void main(String[] args) {

       Cat barsik = new Cat("Barsik", 5);
       Cat streetCat = new Cat();
   }
}
ตอนนี้เราได้เขียนคอนสตรัคเตอร์เริ่มต้นอย่างชัดเจนแล้ว เราสามารถสร้าง cat ทั้งสองประเภทได้ :) สำหรับคอนสตรัคเตอร์ (สำหรับวิธีใดๆ ก็ตาม) ลำดับของอาร์กิวเมนต์มีความสำคัญมาก มาสลับอาร์กิวเมนต์ชื่อและอายุในตัวสร้างของเรากัน
public class Cat {

   String name;
   int age;

   public Cat(int age, String name) {
       this.name = name;
       this.age = age;
   }

   public static void main(String[] args) {

       Cat barsik = new Cat("Barsik", 10); //error!
   }
}
ข้อผิดพลาด! Constructor ระบุไว้อย่างชัดเจนว่าเมื่อวัตถุ Cat ถูกสร้างขึ้น จะต้องส่งผ่านตัวเลขและสตริงตามลำดับนั้น นั่นเป็นสาเหตุที่รหัสของเราใช้งานไม่ได้ อย่าลืมจำสิ่งนี้ไว้และคำนึงถึงสิ่งนี้เมื่อสร้างชั้นเรียนของคุณเอง:
public Cat(String name, int age) {
   this.name = name;
   this.age = age;
}

public Cat(int age, String name) {
   this.age = age;
   this.name = name;
}
นี่คือนักออกแบบสองคนที่แตกต่างกันอย่างสิ้นเชิง! หากเราแสดงคำตอบสำหรับคำถาม “ทำไมเราถึงต้องการตัวสร้าง” ในประโยคเดียว เราสามารถพูดได้ว่า: เพื่อให้วัตถุอยู่ในสถานะที่ถูกต้องเสมอ เมื่อคุณใช้คอนสตรัคเตอร์ ตัวแปรทั้งหมดของคุณจะถูกเตรียมใช้งานอย่างถูกต้อง และจะไม่มีรถที่มีความเร็ว 0 หรือวัตถุที่ "ไม่ถูกต้อง" อื่นๆ ในโปรแกรม การใช้งานของพวกเขามีประโยชน์มากประการแรกสำหรับโปรแกรมเมอร์เอง หากคุณเริ่มต้นฟิลด์ต่างๆ ด้วยตนเอง มีความเสี่ยงสูงที่จะพลาดบางสิ่งบางอย่างและทำผิดพลาด แต่สิ่งนี้จะไม่เกิดขึ้นกับคอนสตรัคเตอร์: หากคุณไม่ผ่านอาร์กิวเมนต์ที่จำเป็นทั้งหมดหรือผสมประเภทเข้าด้วยกัน คอมไพเลอร์จะส่งข้อผิดพลาดทันที เป็นมูลค่าการกล่าวขวัญแยกต่างหากว่าคุณไม่ควรใส่ตรรกะของโปรแกรมของคุณไว้ในตัวสร้าง ในการทำเช่นนี้ คุณมีวิธีต่างๆ ที่คุณสามารถอธิบายฟังก์ชันการทำงานทั้งหมดที่คุณต้องการได้ มาดูกันว่าเหตุใดตรรกะของคอนสตรัคเตอร์จึงเป็นความคิดที่ไม่ดี:
public class CarFactory {

   String name;
   int age;
   int carsCount;

   public CarFactory(String name, int age, int carsCount) {
   this.name = name;
   this.age = age;
   this.carsCount = carsCount;

   System.out.println("Our car factory is called" + this.name);
   System.out.println("She was founded" + this.age + " years ago" );
   System.out.println("During this time it was produced" + this.carsCount +  "cars");
   System.out.println("On average she produces" + (this.carsCount/this.age) + "cars per year");
}

   public static void main(String[] args) {

       CarFactory ford = new CarFactory("Ford", 115 , 50000000);
   }
}
เรามี คลาส CarFactoryที่อธิบายโรงงานผลิตรถยนต์ ภายใน Constructor เราเริ่มต้นฟิลด์ทั้งหมดและวางตรรกะไว้ที่นี่: เราแสดงข้อมูลบางอย่างเกี่ยวกับโรงงานบนคอนโซล ดูเหมือนว่าจะไม่มีอะไรผิดปกติกับสิ่งนี้ โปรแกรมทำงานได้อย่างสมบูรณ์ เอาต์พุตคอนโซล:
โรงงานผลิตรถยนต์ของเราชื่อฟอร์ด ก่อตั้งเมื่อ 115 ปีที่แล้ว ปัจจุบันผลิตรถยนต์ได้ 50,000,000 คัน โดยเฉลี่ยผลิตได้ 434,782 คันต่อปี
แต่ในความเป็นจริงแล้ว เราได้วางระเบิดเวลาไว้ และโค้ดดังกล่าวสามารถนำไปสู่ข้อผิดพลาดได้ง่ายมาก ลองจินตนาการว่าตอนนี้เราไม่ได้พูดถึงฟอร์ด แต่เกี่ยวกับโรงงานใหม่ "Amigo Motors" ซึ่งเปิดดำเนินการมาไม่ถึงหนึ่งปีและผลิตรถยนต์ได้ 1,000 คัน:
public class CarFactory {

   String name;
   int age;
   int carsCount;

   public CarFactory(String name, int age, int carsCount) {
   this.name = name;
   this.age = age;
   this.carsCount = carsCount;

   System.out.println("Our car factory is called" + this.name);
   System.out.println("She was founded" + this.age + " years ago" );
   System.out.println("During this time it was produced" + this.carsCount +  "cars");
   System.out.println("On average she produces" + (this.carsCount/this.age) + "cars per year");
}


   public static void main(String[] args) {

       CarFactory ford = new CarFactory("Amigo Motors", 0 , 1000);
   }
}
เอาต์พุตคอนโซล:
โรงงานผลิตรถยนต์ของเราเรียกว่า Amigo Motors Exception ในเธรด "main" java.lang.ArithmeticException: / โดยศูนย์ ก่อตั้งขึ้นเมื่อ 0 ปีที่แล้ว ในช่วงเวลานี้ บริษัทผลิตรถยนต์ได้ 1,000 คันที่ CarFactory<init>(CarFactory.java:15) ที่ CarFactory.main(CarFactory.java:23) กระบวนการเสร็จสิ้นด้วยรหัสทางออก 1</init>
เรามาแล้ว! โปรแกรมจบลงด้วยข้อผิดพลาดแปลกๆ ลองเดาดูว่าสาเหตุคืออะไร? เหตุผลก็คือตรรกะที่เราวางไว้ในตัวสร้าง โดยเฉพาะในบรรทัดนี้:
System.out.println("On average she produces" + (this.carsCount/this.age) + "cars per year");
ที่นี่เราจะคำนวณและหารจำนวนรถยนต์ที่ผลิตตามอายุของโรงงาน และเนื่องจากโรงงานของเราเป็นโรงงานแห่งใหม่ (นั่นคือ 0 ปี) ผลลัพธ์จึงถูกหารด้วย 0 ซึ่งเป็นสิ่งต้องห้ามในทางคณิตศาสตร์ เป็นผลให้โปรแกรมยุติการทำงานโดยมีข้อผิดพลาด เราควรทำอย่างไร? ย้ายตรรกะทั้งหมดไปยังเมธอดที่แยกจากกัน และเรียกมันว่าprintFactoryInfo( ) คุณสามารถส่งผ่านออบเจ็กต์ CarFactoryเป็นพารามิเตอร์ได้ นอกจากนี้คุณยังสามารถใส่ตรรกะทั้งหมดไว้ที่นั่นและในเวลาเดียวกัน - ประมวลผลข้อผิดพลาดที่เป็นไปได้เช่นเดียวกับของเราที่มีศูนย์ปี ให้กับแต่ละคนของเขาเอง คอนสตรัคเตอร์จำเป็นต้องตั้งค่าสถานะของวัตถุอย่างถูกต้อง สำหรับตรรกะทางธุรกิจ เรามีวิธีการ คุณไม่ควรผสมอันหนึ่งกับอันอื่น
ความคิดเห็น
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION