JavaRush /Java 博客 /Random-ZH /喝咖啡休息#143。Java 17 中的密封类。实现 Singleton 的 4 种方法

喝咖啡休息#143。Java 17 中的密封类。实现 Singleton 的 4 种方法

已在 Random-ZH 群组中发布

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