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