Java 17 中的密封類
來源:Codippa 在這篇文章中,我們將研究密封類別(Java 17 中引入的一項新功能),以及如何透過範例聲明和使用它們。 密封類別首先作為預覽功能出現在 Java 15 中,後來在 Java 16 中具有相同的地位。隨著 Java 17 ( JEP 409 )的發布,此功能變得完全可用。什麼是密封類別?
密封類別允許您限製或選擇子類別。類別不能擴充私有類,除非它位於父類別的允許子類別清單中。使用seal關鍵字密封該類別。密封類別後面必須跟permits關鍵字,以及可以擴充它的類別列表。這是一個例子:public sealed class Device permits Computer, Mobile {
}
此聲明意味著Device只能由Computer 類別和Mobile類別擴充。如果任何其他類別嘗試擴展它,則會引發編譯器錯誤。擴展密封類別的類別必須在其聲明中包含final、sealed或non-sealed關鍵字。因此我們有一個固定的類別層次結構。這與創建子類別有何關係?
-
Final意味著它不能進一步子類化。
-
seal意味著我們需要聲明帶有許可證的子類別。
-
非密封意味著我們在這裡結束了父子層次結構。
引入密封類別的主要目標是:
-
到目前為止,您只能使用final關鍵字來限制類別的擴展。密封類別透過將其包含在允許清單中來控制哪些類別可以擴展它。
-
它還允許類別控制其中哪些將是其子類別。
規則
使用密封類別時要記住的一些規則:-
密封類別必須定義可以使用Permit來擴展它的類別。如果子類在父類中定義為內部類,則不需要這樣做。
-
子類別必須是final、sealed或non-sealed。
-
允許的子類別必須擴展其父密封類別。
也就是說,如果密封類 A 允許類 B,則 B 必須擴展 A。
-
如果密封類別位於模組中,子類別也必須位於同一模組中,或者如果父密封類別位於未命名模組中,則子類別也必須位於同一套件中。
-
只有直接允許的類別才能擴展密封類別。也就是說,如果 A 是允許 B 擴展它的密封類,那麼 B 也是允許 C 的密封類。
那麼C只能擴充B,而不能直接擴充A。
密封介面
與密封類別一樣,介面也可以被密封。這樣的介面可以允許選擇其子介面或類別,這些子介面或類別可以使用Permit來擴展它。這是一個很好的例子:public sealed interface Device permits Electronic, Physical,
DeviceImpl {
}
這裡,Device介面允許Electronic和Physical介面擴展它和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行可以分為三行字節碼:
- 分配記憶體。
- 初始化物件。
- 將物件指派給實例引用。
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;
}
}
預設情況下,枚舉實例是線程安全的,因此無需擔心雙重檢查鎖定,並且編寫起來相當容易。
GO TO FULL VERSION