JavaRush /Java Blog /Random-TW /喝咖啡休息#143。Java 17 中的密封類別。實作 Singleton 的 4 種方法

喝咖啡休息#143。Java 17 中的密封類別。實作 Singleton 的 4 種方法

在 Random-TW 群組發布

Java 17 中的密封類

來源:Codippa 在這篇文章中,我們將研究密封類別(Java 17 中引入的一項新功能),以及如何透過範例聲明和使用它們。 喝咖啡休息#143。 Java 17 中的密封類別。實作 Singleton 的 4 種方法 - 1密封類別首先作為預覽功能出現在 Java 15 中,後來在 Java 16 中具有相同的地位。隨著 Java 17 ( JEP 409 )的發布,此功能變得完全可用。

什麼是密封類別?

密封類別允許您限製或選擇子類別。類別不能擴充私有類,除非它位於父類別的允許子類別清單中。使用seal關鍵字密封該類別。密封類別後面必須跟permits關鍵字,以及可以擴充它的類別列表。這是一個例子:
public sealed class Device permits Computer, Mobile {
}
此聲明意味著Device只能由Computer 類別Mobile類別擴充。如果任何其他類別嘗試擴展它,則會引發編譯器錯誤。擴展密封類別的類別必須在其聲明中包含finalsealednon-sealed關鍵字。因此我們有一個固定的類別層次結構。這與創建子類別有何關係?
  1. Final意味著它不能進一步子類化。

  2. seal意味著我們需要聲明帶有許可證的子類別。

  3. 非密封意味著我們在這裡結束了父子層次結構。

例如,只要筆記型電腦本身保持非密封狀態,電腦就允許筆記型電腦桌上型電腦類別。這意味著筆記型電腦可以擴展為AppleDellHP等類別。

引入密封類別的主要目標是:

  1. 到目前為止,您只能使用final關鍵字來限制類別的擴展。密封類別透過將其包含在允許清單中來控制哪些類別可以擴展它。

  2. 它還允許類別控制其中哪些將是其子類別。

規則

使用密封類別時要記住的一些規則:
  1. 密封類別必須定義可以使用Permit來擴展它的類別。如果子類在父類中定義為內部類,則不需要這樣做。

  2. 子類別必須是finalsealednon-sealed

  3. 允許的子類別必須擴展其父密封類別。

    也就是說,如果密封類 A 允許類 B,則 B 必須擴展 A。

  4. 如果密封類別位於模組中,子類別也必須位於同一模組中,或者如果父密封類別位於未命名模組中,則子類別也必須位於同一套件中。

  5. 只有直接允許的類別才能擴展密封類別。也就是說,如果 A 是允許 B 擴展它的密封類,那麼 B 也是允許 C 的密封類。

    那麼C只能擴充B,而不能直接擴充A。

密封介面

與密封類別一樣,介面也可以被密封。這樣的介面可以允許選擇其子介面或類別,這些子介面或類別可以使用Permit來擴展它。這是一個很好的例子:
public sealed interface Device permits Electronic, Physical,
DeviceImpl {
}
這裡,Device介面允許ElectronicPhysical介面擴展它和DeviceImpl類以進行後續實作。

密封記錄

密封類別可以與 Java 16 中引入的條目一起使用。條目不能擴展常規類,因此它只能實作私有介面。此外,該符號暗示了最終的。因此,條目不能使用permits關鍵字,因為它不能被子類化。也就是說,只有一個包含記錄的單級層次結構。這是一個例子:
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.isSealed()

如果呼叫它的類別或介面是密封的,則傳回true 。這就是關於 Java 17 中添加的密封類別的全部內容。我希望這篇文章能夠提供有用的信息。

實作單例的 4 種方法

來源:Medium 今天您將學習幾種實現單例設計模式的方法。 單例設計模式在Java專案中廣泛使用。它提供對資源的存取控制,例如套接字或資料庫連接。我曾經在面試一家大型晶片公司的 Web 開發人員職位時被要求實現單例。這是我第一次面試Web職位,我沒有做太多準備,所以我選擇了最困難的解決方案:延遲實例化。我的程式碼只有 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;
    }
}
當使用惰性初始化模式時,物件是按需建立的。然而,這種方法有一個線程安全性問題:如果在第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;
    }
}
雙重檢查鎖定 (DCL):第 6 行沒有鎖定,因此如果物件已經創建,則該行將很快工作。為什麼我們需要仔細檢查instance == null?因為第 7 行可能引入了兩個執行緒:第一個執行緒啟動了對象,第二個執行緒正在等待 Singleton.class。如果沒有檢查,那麼第二個執行緒將再次重新建立單例物件。然而,這種方法對於線程來說仍然是危險的。第9行可以分為三行字節碼:
  1. 分配記憶體。
  2. 初始化物件。
  3. 將物件指派給實例引用。
由於 JVM 可能會亂序運行,因此虛擬機器可能會在初始化之前將物件指派給實例參考。另一個線程已經看到!= null實例,它將開始使用它並導致問題。所以我們需要為實例 加上volatile ,那麼程式碼就變成這樣:
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方法時才會被初始化。JVM 中的 init 類別將執行<clinit> cmd,然後 JVM 本身會確保只有一個執行緒可以對目標類別 呼叫<clinit> ,其他執行緒將會等待。

枚舉作為單例

public enum EnumSingleton {
    INSTANCE;
    int value;
    public int getValue() {
        return value;
    }
    public int setValue(int v) {
        this.value = v;
    }
}
預設情況下,枚舉實例是線程安全的,因此無需擔心雙重檢查鎖定,並且編寫起來相當容易。
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION