public sealed interface Shape permits Circle, Rectangle, Triangle {}
final class Circle implements Shape { /* ... */ }
final class Rectangle implements Shape { /* ... */ }
non-sealed class Triangle implements Shape { /* ... */ }
sealed 修饰符后面必须使用 permits 关键字列出所有允许的子类型。
子类型可以是 final、non-sealed 或再次 sealed。
2.2 适用场景
枚举风格的类层次(类似状态机、命令模式)
必须在编译时确定可继承的子类
设计者想要防止不受控制的继承导致的错误
3. 结合实例:订单系统
下面用一个订单系统的示例,演示如何同时使用记录类和密封类。
// 订单类型
public sealed class Order permits PhysicalOrder, DigitalOrder {}
public final record PhysicalOrder(String id, String product, int quantity, String address)
implements Order {}
public final record DigitalOrder(String id, String product, int quantity, String downloadUrl)
implements Order {}
Order 是密封类,只允许 PhysicalOrder 和 DigitalOrder 继承。
两个子类都是记录类,自动生成必要的构造器和方法。
3.1 处理订单的策略
public interface OrderProcessor {
void process(Order order);
}
public class PhysicalOrderProcessor implements OrderProcessor {
@Override
public void process(Order order) {
PhysicalOrder po = (PhysicalOrder) order; // 需要安全转换
// 物流发货逻辑
}
}
public class DigitalOrderProcessor implements OrderProcessor {
@Override
public void process(Order order) {
DigitalOrder doOrder = (DigitalOrder) order;
// 发送下载链接
}
}
3.2 优点
类型安全:编译器会检查 permits 列表,避免意外继承。
简洁代码:记录类减少样板代码,易于阅读。
不可变性:订单对象在创建后不再改变,避免并发问题。
4. 性能与最佳实践
4.1 性能考虑
记录类使用 final 字段,JVM 能做更好优化。
密封类在模式匹配(Java 21+)或 switch 时,编译器可进行更完整的类型检查,提升性能。
4.2 代码组织
把密封类放在单独包或模块,防止外部随意扩展。
对记录类做细粒度分包,避免类膨胀。
4.3 与 Lombok 的对比
Lombok 通过注解生成样板代码,但会隐藏底层实现,导致 IDE 代码提示不完整。记录类直接用语言特性实现,IDE 能更好地理解代码结构。
5. 结语
Java 17 的 sealed classes 与 records 为 Java 开发者提供了更严谨、更简洁的代码结构。通过合理组合使用,可以构建出既安全又可维护的系统架构。建议从下一个项目起,尝试将这两大特性融入代码库,亲自感受它们带来的优势。祝编码愉快!
public class Singleton {
private static final Singleton instance = new Singleton();
private Singleton() { }
public static Singleton getInstance() {
return instance;
}
}
优点:代码简洁,线程安全,无需同步。
缺点:实例在类加载时就创建,可能导致资源浪费,尤其在实例初始化成本高、实际使用可能很少的情况下。
4. 静态内部类(Initialization-on-demand Holder)
public class Singleton {
private Singleton() { }
private static class Holder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return Holder.INSTANCE;
}
}
优点:利用类加载机制,既实现了懒加载,又天然线程安全,性能最优。
缺点:对静态内部类的概念和类加载机制需要一定了解。
5. Enum实现单例
public enum Singleton {
INSTANCE;
public void doSomething() {
// 业务逻辑
}
}
优点:Java的枚举类型天然实现了单例,且对反序列化、反射攻击都有防护,最为简洁安全。
缺点:不适用于需要继承的场景,且与传统单例类相比可读性略逊。
6. 使用 java.util.concurrent 包的 AtomicReference
import java.util.concurrent.atomic.AtomicReference;
public class Singleton {
private static final AtomicReference
<Singleton> INSTANCE = new AtomicReference<>();
private Singleton() { }
public static Singleton getInstance() {
Singleton current = INSTANCE.get();
if (current == null) {
Singleton newInstance = new Singleton();
if (INSTANCE.compareAndSet(null, newInstance)) {
return newInstance;
} else {
return INSTANCE.get(); // 其它线程已完成创建
}
}
return current;
}
}
interface A {
default void foo() { System.out.println("A.foo"); }
}
interface B {
default void foo() { System.out.println("B.foo"); }
}
class C implements A, B { }
此时编译错误:C 必须重写 foo()。解决办法是:
在 C 中显式实现 foo() 并决定使用 A 或 B 的实现:A.super.foo(); 或 B.super.foo();
public interface ListOps
<T> extends Iterable<T> {
// 抽象方法
List
<T> asList();
// 默认方法:过滤
default ListOps
<T> filter(Predicate<? super T> predicate) {
List
<T> result = new ArrayList<>();
for (T item : asList()) {
if (predicate.test(item)) result.add(item);
}
return new SimpleListOps<>(result);
}
// 默认方法:映射
default
<R> ListOps<R> map(Function<? super T, ? extends R> mapper) {
List
<R> result = new ArrayList<>();
for (T item : asList()) {
result.add(mapper.apply(item));
}
return new SimpleListOps<>(result);
}
}