คลาสที่ปิดผนึกใน Java 17

ที่มา: Codippa ในโพสต์นี้ เราจะดูคลาสที่ปิดผนึก คุณลักษณะใหม่ที่แนะนำใน Java 17 และวิธีการประกาศและใช้งานคลาสเหล่านั้นพร้อมตัวอย่าง คอฟฟี่เบรค #143.  คลาสที่ปิดผนึกใน Java 17 4 วิธีในการใช้งาน Singleton - 1คลาสที่ปิดผนึกปรากฏครั้งแรกใน Java 15 เป็นคุณสมบัติแสดงตัวอย่าง และต่อมาใน Java 16 ที่มีสถานะเดียวกัน คุณลักษณะนี้ทำงานได้อย่างสมบูรณ์ด้วยการเปิดตัว Java 17 ( JEP 409 )

คลาสที่ปิดสนิทคืออะไร?

คลาสที่ปิดผนึกช่วยให้คุณสามารถจำกัดหรือเลือกคลาสย่อยได้ คลาสไม่สามารถขยายคลาสส่วนตัวได้เว้นแต่จะอยู่ในรายชื่อคลาสย่อยที่อนุญาตของคลาสแม่ คลาสถูกปิดผนึกโดยใช้คีย์เวิร์ดที่ปิดผนึก คลาสที่ปิดผนึกจะต้องตามด้วย คำสำคัญอนุญาตพร้อมด้วยรายการคลาสที่สามารถขยายได้ นี่คือตัวอย่าง:
public sealed class Device permits Computer, Mobile {
}
การประกาศ นี้ หมายความว่าอุปกรณ์สามารถขยายได้โดย คลาส คอมพิวเตอร์และมือถือ เท่านั้น หากคลาสอื่นพยายามที่จะขยายคลาสนั้น ข้อผิดพลาดของคอมไพลเลอร์จะถูกส่งออกไป คลาสที่ขยายคลาสที่ปิดผนึกจะต้องมีคีย์เวิร์ด สุดท้ายปิดผนึกหรือไม่ปิดผนึกในการประกาศ ดังนั้นเราจึงมีลำดับชั้นคงที่ สิ่งนี้เกี่ยวข้องกับการสร้างคลาสย่อยอย่างไร
  1. สุดท้ายหมายความว่าไม่สามารถจัดประเภทย่อยเพิ่มเติมได้

  2. ปิดผนึกหมายความว่าเราจำเป็นต้องประกาศชั้นเรียนของเด็กด้วยใบอนุญาต

  3. non-sealed หมายความว่าที่ นี่เราสิ้นสุดลำดับ ชั้น parent-child

ตัวอย่างเช่นคอมพิวเตอร์ อนุญาตให้ มีคลาสLaptopและDesktopตราบใดที่Laptopยังไม่ปิดผนึก ซึ่งหมายความว่าแล็ปท็อปสามารถขยายออกไปตามคลาสต่างๆ เช่นApple , Dell , HPและอื่นๆ

เป้าหมายหลักของการแนะนำคลาสที่ปิดผนึกคือ:

  1. จนถึงขณะนี้ คุณสามารถจำกัดการขยายคลาสโดยใช้ คำหลัก สุดท้ายเท่านั้น คลาสที่ปิดผนึกจะควบคุมคลาสที่สามารถขยายได้โดยรวมไว้ในรายการที่อนุญาต

  2. นอกจากนี้ยังอนุญาตให้ชั้นเรียนควบคุมว่าคลาสใดจะเป็นคลาสย่อย

กฎ

กฎบางประการที่ต้องจำเมื่อใช้คลาสที่ปิดสนิท:
  1. คลาสที่ปิดผนึกจะต้องกำหนดคลาสที่สามารถขยายได้โดยใช้ใบอนุญาต สิ่งนี้ไม่จำเป็นหากคลาสลูกถูกกำหนดไว้ภายในคลาสพาเรนต์เป็นคลาสภายใน

  2. คลาสลูกต้องเป็นคลาสสุดท้ายปิดผนึกหรือไม่มีการปิดผนึก

  3. ชั้นเรียนย่อยที่ได้รับอนุญาตจะต้องขยายชั้นเรียนที่ปิดผนึกโดยผู้ปกครอง

    นั่นคือ ถ้าคลาส A ที่ปิดผนึกอนุญาตให้คลาส B ได้ B จะต้องขยาย A

  4. หากคลาสที่ปิดผนึกอยู่ในโมดูล คลาสลูกจะต้องอยู่ในโมดูลเดียวกันด้วย หรือในแพ็คเกจเดียวกันหากคลาสที่ปิดผนึกพาเรนต์อยู่ในโมดูลที่ไม่มีชื่อ

  5. เฉพาะคลาสที่ได้รับอนุญาตโดยตรงเท่านั้นที่สามารถขยายคลาสที่ปิดผนึกได้ นั่นคือ ถ้า 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 สามารถแบ่งออกเป็นรหัสไบต์ได้สามบรรทัด:
  1. จัดสรรหน่วยความจำ
  2. เริ่มต้นวัตถุ
  3. กำหนดวัตถุให้กับการอ้างอิงอินสแตนซ์
เนื่องจาก JVM สามารถทำงานผิดปกติได้ เครื่องเสมือนอาจกำหนดอ็อบเจ็กต์ให้กับการอ้างอิงอินสแตนซ์ก่อนที่จะเริ่มต้น เธรดอื่นเห็น!= null instance แล้ว มันจะเริ่มใช้งานและทำให้เกิดปัญหา ดังนั้นเราจึงจำเป็นต้องเพิ่มความผันผวนให้กับอินสแตนซ์ จากนั้นโค้ดจะกลายเป็นดังนี้:
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จะปลอดภัยสำหรับเธรด ดังนั้นจึงไม่จำเป็นต้องกังวลเกี่ยวกับการล็อคที่มีการตรวจสอบซ้ำ และเขียนได้ค่อนข้างง่าย