CodeGym /จาวาบล็อก /สุ่ม /ตัวสร้าง Java
John Squirrels
ระดับ
San Francisco

ตัวสร้าง Java

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

ตัวสร้างอะไรในโลกนี้และเหตุใดจึงจำเป็น

ลองพิจารณาสองตัวอย่าง

public class Car {

   String model;
   int maxSpeed;

   public static void main(String[] args) {

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

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

public class Car {

   String model;// model
   int maxSpeed;// maximum speed
   int wheels;// wheel width
   double engineVolume;// engine volume
   String color;// color
   int productionYear;// production year
   String ownerFirstName;// first name of owner
   String ownerLastName;// last name of owner
   long price;// price
   boolean isNew;// flag indicating whether car is new
   int seatsInTheCar;// number of seats in the car
   String cabinMaterial;// interior material
   boolean insurance;// flag indicating whether car is insured
   String manufacturerCountry;// manufacturer country
   int trunkVolume;// size of the trunk
   int accelerationTo100km;// how long it takes to accelerate 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.productionYear = 2016;
       bugatti.insurance = true;
       bugatti.price = 2000000;
       bugatti.isNew = false;
       bugatti.seatsInTheCar = 2;
       bugatti.maxSpeed = 378;
       bugatti.model = "Bugatti Veyron";

   }

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

public class Car {

   String model;// model
   int maxSpeed;// maximum speed
   int wheels;// wheel width
   double engineVolume;// engine volume
   String color;// color
   int productionYear;// production year
   String ownerFirstName;// first name of owner
   String ownerLastName;// last name of owner
   long price;// price
   boolean isNew;// flag indicating whether car is new
   int seatsInTheCar;// number of seats in the car
   String cabinMaterial;// interior material
   boolean insurance;// flag indicating whether car is insured
   String manufacturerCountry;// manufacturer country
   int trunkVolume;// size of the trunk
   int accelerationTo100km;// how long it takes to accelerate 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.productionYear = 2016;
       bugatti.insurance = true;
       bugatti.price = 2000000;
       bugatti.isNew = false;
       bugatti.seatsInTheCar = 2;
       bugatti.maxSpeed = 378;
       bugatti.model = "Bugatti Veyron";

       System.out.println("Model: Bugatti Veyron. Engine volume: " + bugatti.engineVolume + ". Trunk volume: " + bugatti.trunkVolume + ". Cabin material: " + bugatti.cabinMaterial +
       ". Wheel width: " + bugatti.wheels + ". Purchased in 2018 by Mr. " + bugatti.ownerLastName);

   }

}
เอาต์พุตคอนโซล: รุ่น: Bugatti Veyron ปริมาณเครื่องยนต์: 6.3. ปริมาตรสัมภาระท้ายรถ: 0 วัสดุห้องโดยสาร: ไม่มี ความกว้างล้อ: 0 ซื้อในปี 2018 โดย Mr. null ผู้ซื้อของคุณที่สละเงิน 2 ล้านเหรียญเพื่อซื้อรถ แน่นอนว่าจะไม่ชอบให้ถูกเรียกว่า " Mr. null "! แต่อย่างจริงจัง สิ่งที่สำคัญที่สุดคือโปรแกรมของเราสร้างวัตถุอย่างไม่ถูกต้อง: รถยนต์ที่มีความกว้างของล้อเป็น 0 (เช่น ไม่มีล้อเลย) กระโปรงท้ายรถหายไป ห้องโดยสารทำจากวัสดุที่ไม่รู้จัก และเหนือสิ่งอื่นใด เจ้าของที่ไม่ได้ระบุ . คุณสามารถจินตนาการได้ว่าข้อผิดพลาดดังกล่าวสามารถ "ปิด" ได้อย่างไรเมื่อโปรแกรมกำลังทำงาน! เราจำเป็นต้องหลีกเลี่ยงสถานการณ์ดังกล่าวไม่ทางใดก็ทางหนึ่ง เราจำเป็นต้องจำกัดโปรแกรมของเรา: เมื่อสร้างรถ ใหม่เราต้องการระบุฟิลด์ เช่น รุ่นและความเร็วสูงสุดเสมอ มิฉะนั้น เราต้องการป้องกันการสร้างวัตถุ คอนสตรัคจัดการงานนี้ได้อย่างง่ายดาย พวกเขามีชื่อด้วยเหตุผล ตัวสร้างสร้างประเภทของคลาส "โครงกระดูก" ที่แต่ละวัตถุใหม่ต้องตรงกัน เพื่อความสะดวก กลับไปที่คลาสCarเวอร์ชันที่เรียบง่ายซึ่งมีสองฟิลด์ เมื่อพิจารณาจากข้อกำหนดของเรา ตัวสร้างคลาส Carจะมีลักษณะดังนี้:

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

// And creating an object now looks like this:

public static void main(String[] args) {
   Car bugatti = new Car("Bugatti Veyron", 378);
}
สังเกตวิธีการประกาศตัวสร้าง คล้ายกับวิธีปกติ แต่ไม่มีประเภทการส่งคืน นอกจากนี้ ตัวสร้างยังระบุชื่อคลาส ( Car ) ที่ขึ้นต้นด้วยอักษรตัวพิมพ์ใหญ่ นอกจากนี้ ตัวสร้างยังใช้กับคำ หลักที่ใหม่สำหรับคุณ: this คำหลักนี้ใช้สำหรับระบุวัตถุเฉพาะ รหัสในตัวสร้าง

public Car(String model, int maxSpeed) {
   this.model = model;
   this.maxSpeed = maxSpeed;
}
สามารถตีความได้เกือบเป็นคำต่อคำ: "แบบจำลองสำหรับรถคันนี้ (รุ่นที่เรากำลังสร้างตอนนี้) คือ อาร์กิวเมนต์ แบบจำลองที่ส่งไปยังตัวสร้าง ส่วน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", 378);
       System.out.println(bugatti.model);
       System.out.println(bugatti.maxSpeed);
   }

}
เอาต์พุตคอนโซล: Bugatti Veyron 378 ตัวสร้างกำหนดค่าที่ต้องการอย่างถูกต้อง คุณอาจสังเกตเห็นว่าตัวสร้างนั้นคล้ายกับวิธีการทั่วไปมาก! ดังนั้นมันจึงเป็น ตัวสร้างเป็นเมธอดจริง ๆ แต่มีคุณสมบัติเฉพาะ :) เช่นเดียวกับเมธอด เราส่งอาร์กิวเมนต์ไปยังตัวสร้างของเรา และเช่นเดียวกับการเรียกเมธอด การเรียกตัวสร้างจะไม่ทำงานเว้นแต่คุณจะระบุ:

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 smudge = new Cat("Smudge", 5);
       Cat streetCatNamedBob = new Cat("Bob");
   }

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

public class Cat {

   public static void main(String[] args) {

       Cat smudge = new Cat(); // The default constructor is invoked here
   }
}
เมื่อมองแวบแรกก็มองไม่เห็น เราสร้างวัตถุ แล้วอะไรล่ะ? ตัวสร้างกำลังทำอะไรอยู่ที่นี่? หากต้องการดู ให้เขียนตัวสร้างว่างสำหรับคลาสCat อย่างชัดเจน เราจะแสดงวลีบางอย่างในนั้น หากวลีปรากฏขึ้นแสดงว่าตัวสร้างถูกเรียกใช้

public class Cat {

   public Cat() {
       System.out.println("A cat has been created!");
   }

   public static void main(String[] args) {

       Cat smudge = new Cat(); // The default constructor is invoked here
   }
}
เอาต์พุตคอนโซล: แมวถูกสร้างขึ้นแล้ว! มีคำยืนยัน! คอนสตรัคเตอร์ดีฟอลต์มักจะมองไม่เห็นในคลาสของคุณ แต่คุณต้องรู้อีกสิ่งหนึ่งเกี่ยวกับเรื่องนี้ ตัวสร้างเริ่มต้นจะถูกตัดออกจากคลาสเมื่อคุณสร้างตัวสร้างที่มีอาร์กิวเมนต์ อันที่จริง เราได้เห็นข้อพิสูจน์ข้างต้นแล้ว มันอยู่ในรหัสนี้:

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 smudge = new Cat(); //Error!
   }
}
เราไม่สามารถสร้างCatได้หากไม่มีชื่อและอายุ เนื่องจากเราประกาศ ตัวสร้าง Catด้วยพารามิเตอร์stringและint สิ่งนี้ทำให้ตัวสร้างเริ่มต้นหายไปจากคลาสทันที ดังนั้นอย่าลืมว่าหากคุณต้องการตัวสร้างหลายตัวในชั้นเรียนของคุณ รวมถึงตัวสร้างที่ไม่มีข้อโต้แย้ง คุณจะต้องประกาศแยกต่างหาก ตัวอย่างเช่น สมมติว่าเรากำลังสร้างโปรแกรมสำหรับคลินิกสัตวแพทย์ คลินิกของเราต้องการทำความดีและช่วยเหลือลูกแมวจรจัดที่ไม่ทราบชื่อและอายุ จากนั้นรหัสของเราควรมีลักษณะดังนี้:

public class Cat {

   String name;
   int age;

   // For cats with owners
   public Cat(String name, int age) {
       this.name = name;
       this.age = age;
   }

   // For street cats
   public Cat() {
   }

   public static void main(String[] args) {

       Cat smudge = new Cat("Smudge", 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 smudge = new Cat("Smudge", 10); // Error!
   }
}
เกิดข้อผิดพลาด! ตัวสร้างกำหนดเงื่อนไขอย่างชัดเจนว่าเมื่อ สร้างวัตถุ 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("It was founded " + this.age + " years ago" );
   System.out.println("Since that time, it has produced " + this.carsCount +  " cars");
   System.out.println("On average, it produces " + (this.carsCount/this.age) + " cars per year");
}

   public static void main(String[] args) {

       CarFactory ford = new CarFactory("Ford", 115 , 50000000);
   }
}
เรามี คลาส CarFactoryที่อธิบายถึงโรงงานผลิตรถยนต์ ภายในตัวสร้าง เราเริ่มต้นฟิลด์ทั้งหมดและรวมตรรกะบางอย่าง: เราแสดงข้อมูลบางอย่างเกี่ยวกับโรงงาน ดูเหมือนว่าไม่มีอะไรเลวร้ายเกี่ยวกับเรื่องนี้ โปรแกรมทำงานได้ดี เอาต์พุตคอนโซล: โรงงานผลิตรถยนต์ของเรามีชื่อว่า Ford ก่อตั้งเมื่อ 115 ปีที่แล้ว ตั้งแต่นั้นเป็นต้นมาก็ผลิตรถยนต์ได้ 50000000 คัน โดยเฉลี่ยแล้วผลิตรถยนต์ได้ 434782 คันต่อปี แต่จริงๆ แล้วเราได้วางเหมืองแบบหน่วงเวลาไว้ และรหัสประเภทนี้สามารถนำไปสู่ข้อผิดพลาดได้ง่ายมาก สมมติว่าตอนนี้เราไม่ได้พูดถึง Ford แต่เกี่ยวกับโรงงานแห่งใหม่ชื่อ "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 factor is called " + this.name);
   System.out.println("It was founded " + this.age + " years ago" );
   System.out.println("Since that time, it has produced " + this.carsCount +  " cars");
   System.out.println("On average, it 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: / by zero ก่อตั้งขึ้นเมื่อ 0 ปีที่แล้ว ตั้งแต่นั้นเป็นต้นมา บริษัทได้ผลิตรถยนต์ 1,000 คัน ที่ CarFactory (CarFactory.java:15) ที่ CarFactory.main(CarFactory.java:23) ดำเนินการเสร็จสิ้นด้วยรหัสออก 1 บูม! โปรแกรมจบลงด้วยข้อผิดพลาดบางอย่างที่เข้าใจยาก คุณลองเดาสาเหตุได้ไหม ปัญหาอยู่ในตรรกะที่เราใส่เข้าไปในตัวสร้าง โดยเฉพาะอย่างยิ่ง บรรทัดนี้:

System.out.println("On average, it produces " + (this.carsCount/this.age) + " cars per year");
คุณกำลังคำนวณและหารจำนวนรถยนต์ที่ผลิตตามอายุของโรงงาน และเนื่องจากโรงงานของเราเป็นโรงงานใหม่ (เช่น โรงงานมีอายุ 0 ปี) เราจึงหารด้วย 0 ซึ่งเราทำไม่ได้ในวิชาคณิตศาสตร์ ดังนั้น โปรแกรมจึงหยุดทำงานโดยมีข้อผิดพลาด

เราควรทำอย่างไร?

ใส่ตรรกะทั้งหมดในวิธีแยกต่างหาก เรียกมันว่าprintFactoryInfo( ) คุณสามารถส่ง วัตถุ CarFactoryให้เป็นอาร์กิวเมนต์ได้ คุณสามารถวางตรรกะทั้งหมดไว้ที่นั่น และจัดการข้อผิดพลาดที่อาจเกิดขึ้นได้พร้อมกัน (เช่น ข้อผิดพลาดของเราที่เกี่ยวข้องกับศูนย์ปี) ของแต่ละคนเอง ตัวสร้างจำเป็นต้องตั้งค่าสถานะวัตถุที่ถูกต้อง เรามีวิธีการสำหรับตรรกะทางธุรกิจ อย่าผสมอย่างใดอย่างหนึ่ง เพื่อเสริมสิ่งที่คุณได้เรียนรู้ เราขอแนะนำให้คุณดูบทเรียนวิดีโอจากหลักสูตร Java ของเรา
ความคิดเห็น
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION