คลาสที่ปิดผนึกใน Java 17
ที่มา: Codippa ในโพสต์นี้ เราจะดูคลาสที่ปิดผนึก คุณลักษณะใหม่ที่แนะนำใน Java 17 และวิธีการประกาศและใช้งานคลาสเหล่านั้นพร้อมตัวอย่าง คลาสที่ปิดผนึกปรากฏครั้งแรกใน Java 15 เป็นคุณสมบัติแสดงตัวอย่าง และต่อมาใน Java 16 ที่มีสถานะเดียวกัน คุณลักษณะนี้ทำงานได้อย่างสมบูรณ์ด้วยการเปิดตัว Java 17 ( JEP 409 )คลาสที่ปิดสนิทคืออะไร?
คลาสที่ปิดผนึกช่วยให้คุณสามารถจำกัดหรือเลือกคลาสย่อยได้ คลาสไม่สามารถขยายคลาสส่วนตัวได้เว้นแต่จะอยู่ในรายชื่อคลาสย่อยที่อนุญาตของคลาสแม่ คลาสถูกปิดผนึกโดยใช้คีย์เวิร์ดที่ปิดผนึก คลาสที่ปิดผนึกจะต้องตามด้วย คำสำคัญอนุญาตพร้อมด้วยรายการคลาสที่สามารถขยายได้ นี่คือตัวอย่าง:public sealed class Device permits Computer, Mobile {
}
การประกาศ นี้ หมายความว่าอุปกรณ์สามารถขยายได้โดย คลาส คอมพิวเตอร์และมือถือ เท่านั้น หากคลาสอื่นพยายามที่จะขยายคลาสนั้น ข้อผิดพลาดของคอมไพลเลอร์จะถูกส่งออกไป คลาสที่ขยายคลาสที่ปิดผนึกจะต้องมีคีย์เวิร์ด สุดท้ายปิดผนึกหรือไม่ปิดผนึกในการประกาศ ดังนั้นเราจึงมีลำดับชั้นคงที่ สิ่งนี้เกี่ยวข้องกับการสร้างคลาสย่อยอย่างไร
-
สุดท้ายหมายความว่าไม่สามารถจัดประเภทย่อยเพิ่มเติมได้
-
ปิดผนึกหมายความว่าเราจำเป็นต้องประกาศชั้นเรียนของเด็กด้วยใบอนุญาต
-
non-sealed หมายความว่าที่ นี่เราสิ้นสุดลำดับ ชั้น parent-child
เป้าหมายหลักของการแนะนำคลาสที่ปิดผนึกคือ:
-
จนถึงขณะนี้ คุณสามารถจำกัดการขยายคลาสโดยใช้ คำหลัก สุดท้ายเท่านั้น คลาสที่ปิดผนึกจะควบคุมคลาสที่สามารถขยายได้โดยรวมไว้ในรายการที่อนุญาต
-
นอกจากนี้ยังอนุญาตให้ชั้นเรียนควบคุมว่าคลาสใดจะเป็นคลาสย่อย
กฎ
กฎบางประการที่ต้องจำเมื่อใช้คลาสที่ปิดสนิท:-
คลาสที่ปิดผนึกจะต้องกำหนดคลาสที่สามารถขยายได้โดยใช้ใบอนุญาต สิ่งนี้ไม่จำเป็นหากคลาสลูกถูกกำหนดไว้ภายในคลาสพาเรนต์เป็นคลาสภายใน
-
คลาสลูกต้องเป็นคลาสสุดท้ายปิดผนึกหรือไม่มีการปิดผนึก
-
ชั้นเรียนย่อยที่ได้รับอนุญาตจะต้องขยายชั้นเรียนที่ปิดผนึกโดยผู้ปกครอง
นั่นคือ ถ้าคลาส A ที่ปิดผนึกอนุญาตให้คลาส B ได้ B จะต้องขยาย A
-
หากคลาสที่ปิดผนึกอยู่ในโมดูล คลาสลูกจะต้องอยู่ในโมดูลเดียวกันด้วย หรือในแพ็คเกจเดียวกันหากคลาสที่ปิดผนึกพาเรนต์อยู่ในโมดูลที่ไม่มีชื่อ
-
เฉพาะคลาสที่ได้รับอนุญาตโดยตรงเท่านั้นที่สามารถขยายคลาสที่ปิดผนึกได้ นั่นคือ ถ้า A เป็นคลาสที่ปิดผนึกที่อนุญาตให้ B ขยายได้ B ก็เป็นคลาสที่ปิดผนึกที่อนุญาตให้ C เช่นกัน
ดังนั้น C สามารถขยายได้เฉพาะ B เท่านั้น แต่ไม่สามารถขยาย A โดยตรงได้
อินเทอร์เฟซที่ปิดผนึก
เช่นเดียวกับคลาสที่ปิดผนึก อินเทอร์เฟซยังสามารถปิดผนึกได้ อินเทอร์เฟซดังกล่าวอาจอนุญาตให้มีการเลือกอินเทอร์เฟซหรือคลาสย่อยซึ่งสามารถขยายได้โดยใช้ใบอนุญาต นี่เป็นตัวอย่างที่ดี:public sealed interface Device permits Electronic, Physical,
DeviceImpl {
}
ที่นี่ อินเทอร์เฟซ อุปกรณ์อนุญาตให้ อินเทอร์เฟซ อิเล็กทรอนิกส์และฟิสิคัลขยายและ คลาส DeviceImplสำหรับการนำไปใช้ในภายหลัง
บันทึกที่ปิดผนึก
คลาสที่ปิดผนึกสามารถใช้กับรายการที่แนะนำใน Java 16 รายการไม่สามารถขยายคลาสปกติได้ ดังนั้นจึงสามารถใช้อินเทอร์เฟซส่วนตัวเท่านั้น นอกจากนี้ สัญกรณ์ยังหมายถึงที่สิ้นสุด ดังนั้นรายการจึงไม่สามารถใช้คีย์เวิร์ดอนุญาตได้เนื่องจากไม่สามารถจัดคลาสย่อยได้ นั่นคือ มีเพียงลำดับชั้นระดับเดียวที่มีเรกคอร์ด นี่คือตัวอย่าง:public sealed interface Device permits Laptop {
}
public record Laptop(String brand) implement Device {
}
รองรับการสะท้อนแสง
Java Reflection ให้การสนับสนุนคลาสที่ปิดผนึก มีการเพิ่มสองวิธีต่อไปนี้ในjava.lang.Class :1.getPermissionSubclasses()
สิ่งนี้จะส่งคืนอาร์เรย์java.lang.Classที่มีคลาสทั้งหมดที่อนุญาตโดยอ็อบเจ็กต์คลาสนี้ ตัวอย่าง:Device c = new Device();
Class<? extends Device> cz = c.getClass();
Class<?>[] permittedSubclasses = cz.getPermittedSubclasses();
for (Class<?> sc : permittedSubclasses){
System.out.println(sc.getName());
}
บทสรุป:
2.ถูกปิดผนึก()
สิ่งนี้จะคืนค่าเป็นจริงหากคลาสหรืออินเทอร์เฟซที่เรียกว่าถูกปิดผนึก ทั้งหมดตอนนี้เกี่ยวกับคลาสที่ปิดผนึกที่เพิ่มใน Java 17 ฉันหวังว่าบทความนี้จะให้ข้อมูล4 วิธีในการนำซิงเกิลตันไปใช้
ที่มา: ปานกลาง วันนี้คุณจะได้เรียนรู้หลายวิธีในการใช้รูปแบบการออกแบบซิงเกิลตัน รูปแบบการออกแบบซิงเกิลตันถูกนำมาใช้กันอย่างแพร่หลายในโปรเจ็กต์ Java โดยให้การควบคุมการเข้าถึงทรัพยากร เช่น ซ็อกเก็ตหรือการเชื่อมต่อฐานข้อมูล ครั้งหนึ่งฉันถูกขอให้ใช้ซิงเกิลตันในระหว่างการสัมภาษณ์ตำแหน่งนักพัฒนาเว็บในบริษัทชิปขนาดใหญ่ นี่เป็นครั้งแรกของฉันในการสัมภาษณ์ตำแหน่งงานบนเว็บ และฉันไม่ได้เตรียมอะไรมากนัก ดังนั้นฉันจึงเลือกวิธีแก้ปัญหาที่ยากที่สุด: การสร้างอินสแตนซ์แบบ Lazy รหัสของฉันถูกต้องเพียง 90% และไม่มีประสิทธิภาพเพียงพอ ฉันแพ้ในรอบสุดท้าย... ดังนั้นฉันหวังว่าบทความของฉันจะเป็นประโยชน์กับคุณการสร้างอินสแตนซ์ในช่วงต้น
class Singleton {
private Singleton() {}
private static Singleton instance = new Singleton();
public static Singleton getInstance() {
return instance;
}
}
เนื่องจากวัตถุถูกสร้างขึ้นแล้วเมื่อเริ่มต้น จึงไม่มีปัญหาด้านความปลอดภัยของเธรด แต่จะสิ้นเปลืองทรัพยากรหน่วยความจำหากไม่มีใครใช้งาน
การดำเนินการที่ขี้เกียจ
class Singleton {
private static Singleton instance = null;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
เมื่อใช้รูปแบบการเริ่มต้นแบบ Lazy ออบเจ็กต์จะถูกสร้างขึ้นตามความต้องการ อย่างไรก็ตาม วิธีการนี้มีปัญหาด้านความปลอดภัยของเธรด: หากมีการสตาร์ทสองเธรดบนบรรทัดที่ 5 พร้อมกัน จะสร้างอินสแตนซ์ Singleton สองรายการ เพื่อหลีกเลี่ยงปัญหานี้ เราต้องเพิ่มการล็อค:
class Singleton {
private Singleton() {}
private static Singleton instance = null;
public static Singleton getInstance() {
if (instance == null) {
synchronized(Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
Double-Checked Locking (DCL): ไม่มีการล็อคบนบรรทัดที่ 6 ดังนั้นบรรทัดนี้จะทำงานเร็วมากหากวัตถุถูกสร้างขึ้นแล้ว ทำไมเราต้องตรวจสอบอินสแตนซ์ == nullอีก ครั้ง เนื่องจากบางทีอาจมีสองเธรดที่แนะนำในบรรทัด ที่7: เธรดแรกเริ่มต้นอ็อบเจ็กต์ ส่วนเธรดที่สองกำลังรอการ ล็อก Singleton.class หากไม่มีการตรวจสอบ เธรดที่สองจะสร้างออบเจ็กต์ซิงเกิลตันขึ้นมาใหม่อีกครั้ง อย่างไรก็ตาม วิธีการนี้ยังคงเป็นอันตรายต่อเธรด บรรทัดที่ 9 สามารถแบ่งออกเป็นรหัสไบต์ได้สามบรรทัด:
- จัดสรรหน่วยความจำ
- เริ่มต้นวัตถุ
- กำหนดวัตถุให้กับการอ้างอิงอินสแตนซ์
class Singleton {
private Singleton() {}
private volatile static Singleton instance = null;
public static Singleton getInstance() {
if (instance == null) {
synchronized(Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
การใช้คลาสภายในแบบคงที่
public class Singleton {
private Singleton() {}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
SingletonHolder เป็นคลาสภายในแบบคงที่ ซึ่งจะเริ่มต้นได้ก็ต่อเมื่อมีการ เรียกใช้เมธอด getInstance คลาส init ใน JVM จะรัน<clinit> cmdจากนั้น JVM เองจะตรวจสอบให้แน่ใจว่ามีเพียงเธรดเดียวเท่านั้นที่สามารถเรียก<clinit>บนคลาสเป้าหมายได้ ส่วนเธรดอื่นจะรอ
Enum เป็นซิงเกิลตัน
public enum EnumSingleton {
INSTANCE;
int value;
public int getValue() {
return value;
}
public int setValue(int v) {
this.value = v;
}
}
ตาม ค่าเริ่มต้น อินสแตน ซ์ enumจะปลอดภัยสำหรับเธรด ดังนั้นจึงไม่จำเป็นต้องกังวลเกี่ยวกับการล็อคที่มีการตรวจสอบซ้ำ และเขียนได้ค่อนข้างง่าย
GO TO FULL VERSION