当前位置: 首页 > news >正文

Java密封类(Sealed Classes)增强详解

Java密封类(Sealed Classes)增强详解

Java 17引入了一个重要的新特性——密封类(Sealed Classes),这一特性旨在增强Java编程语言的能力,提供了一种机制来限制哪些类可以继承一个给定的类或者实现一个给定的接口。通过sealed和permits关键字,密封类不仅增强了类型安全性,还简化了代码结构,并在编译时进行了更精确的检查。以下是对Java密封类的深入解析。

一、密封类的定义与目的

密封类是通过sealed修饰符声明的类,它限制了哪些类可以继承它。同时,使用permits关键字来指定哪些子类是被允许的。这一特性的主要目的是防止类的无序扩展,确保类的继承体系在可控范围内,减少潜在的错误和异常逻辑。

在Java 17之前,一个类要么是可以被extends的,要么是final的,只有这两种选项。如果需要控制哪些类可以继承,通常只能通过改变类的访问级别,比如去掉类的public,将访问级别设为默认(包私有)。然而,这种方法并不能完全满足对类继承关系的细粒度控制需求。密封类的引入,正是为了解决这一问题。

二、密封类的语法与用法

密封类的语法如下:

public sealed class 父类名 permits 子类1, 子类2, ... {// 类成员
}public final class 子类1 extends 父类名 {// 子类1成员
}public sealed class 子类2 extends 父类名 permits 子类2_1, ... {// 子类2成员
}// 或者
public non-sealed class 子类3 extends 父类名 {// 子类3成员,子类3可以被任意类继承
}

在上面的语法中,父类被声明为sealed,并通过permits关键字指定了允许继承它的子类。子类可以是final的(禁止继承),也可以是sealed的(限制继承),还可以是non-sealed的(恢复开放继承)。

例如,假设我们有一个表示形状的类Shape,我们希望只有Circle、Rectangle和Square这三个类可以继承它,那么我们可以这样定义:

public sealed interface Shape permits Circle, Rectangle, Square {// Shape接口的成员
}public final class Circle implements Shape {// Circle类的成员
}public sealed class Rectangle implements Shape permits Square {// Rectangle类的成员
}public final class Square extends Rectangle {// Square类的成员
}

在这个例子中,Shape是一个密封接口,它只允许Circle、Rectangle和Square这三个类实现。同时,Rectangle也被声明为密封类,它只允许Square作为其子类。这样的设计使得类的继承体系更加清晰和可控。

三、密封类的优势与特点
  1. 增强类型安全性

密封类通过限制类的继承关系,可以减少类型转换的错误和潜在的运行时异常,提高程序的稳定性。例如,在上面的Shape例子中,由于我们限制了哪些类可以实现Shape接口,因此在编译时就可以检查到任何非法的继承关系,从而避免运行时错误。

  1. 简化代码结构

密封类可以减少需要显式声明的类和接口数量,简化代码结构。例如,在上面的例子中,我们不需要为Shape接口编写额外的防御性代码来防止未知的子类实现它,因为密封机制已经保证了只有指定的子类可以实现它。

  1. 提高代码的可读性和可维护性

在框架和库的设计中,使用密封类可以更好地控制类的继承体系,减少代码耦合性,提高代码的可读性和可维护性。例如,在图形库中,类的作者可能希望只有特定的类可以扩展Shape,因为库的大部分工作涉及以合适的方式处理每种形状。通过使用密封类,作者可以明确地指定哪些类可以扩展Shape,从而简化库的设计和维护。

  1. 支持模式匹配的未来发展方向

密封类还为模式的详尽分析提供了基础,支持模式匹配的未来发展方向。在Java 14中引入的模式匹配(Pattern Matching for instanceof)特性中,密封类可以作为其基础之一,使得编译器能够在编译时检查到更多的类型信息,从而提供更精确的错误检查和代码优化。

四、密封类的限制与注意事项
  1. 不支持匿名类和函数式接口的继承

由于密封类需要明确的继承关系,因此不支持匿名类和函数式接口的继承。这意味着,如果一个类被声明为密封类,那么它不能被用作匿名类的父类,也不能被函数式接口所实现。

  1. 子类必须位于同一模块或同一包中

在密封类的声明中,permits指定的子类必须位于同一模块中(如果超类在命名模块中)或在同一包中(如果超类在未命名模块中)。这一限制确保了密封类的继承关系在编译时是可控的,避免了跨模块或跨包的非法继承。

  1. 子类可以是final、sealed或non-sealed

在密封类的继承体系中,子类可以是final的(禁止继承),也可以是sealed的(限制继承),还可以是non-sealed的(恢复开放继承)。然而,需要注意的是,如果一个子类被声明为non-sealed,那么它就可以被任意类继承,这可能会破坏密封类所带来的类型安全性和代码简化性。因此,在使用non-sealed子类时需要谨慎考虑。

  1. 编译时检查

密封类提供了编译时的类型检查,确保不会出现意外的继承关系。然而,这也意味着在编写密封类时需要注意编译器的错误提示,并根据提示进行相应的修改。

五、密封类的应用场景与示例

密封类在Java中有着广泛的应用场景,特别是在框架和库的设计中。以下是一些常见的应用场景和示例:

  1. 状态机模式

在状态机模式中,系统状态的变化是有限的且有明确的定义。使用密封类可以确保只有预定义的状态类可以被创建,避免了意外的状态添加。例如,一个有限状态机(FSM)可以使用密封类来定义其状态集合,并通过permits关键字来指定哪些状态是合法的转换目标。

  1. 图形库

在图形库中,类的作者可能希望只有特定的类可以扩展某些基类或接口。例如,在上面的Shape例子中,作者可能希望只有Circle、Rectangle和Square这三个类可以扩展Shape接口。通过使用密封类,作者可以明确地指定哪些类可以扩展Shape接口,从而简化库的设计和维护。

  1. API设计

在API设计中,有时需要限制某些类的继承关系以避免潜在的错误和异常逻辑。例如,一个用于处理网络通信的API可能希望只有特定的类可以扩展其基类或实现其接口。通过使用密封类,API设计者可以明确地指定哪些类可以扩展或实现这些基类或接口,从而确保API的稳定性和安全性。

六、总结与展望

Java 17中的密封类是一个重要的语言特性,它提供了对类继承关系的更细粒度控制。通过限制哪些类可以继承一个给定的类或者实现一个给定的接口,密封类增强了类型安全性、简化了代码结构,并在编译时进行了更精确的检查。这对于框架和库的设计者来说尤其有用,因为它可以更好地控制类的继承体系、减少代码耦合性、提高代码的可读性和可维护性。

未来,随着Java语言的不断发展和完善,密封类可能会得到更多的应用和优化。例如,可能会引入更多的语法和特性来支持密封类的使用;可能会与其他语言特性(如模式匹配)进行更深入的集成;可能会提供更多的工具和库来帮助开发者更好地理解和使用密封类。总之,密封类作为Java 17的一个重要特性,将为Java语言的发展和应用带来更多的可能性和机遇。

相关文章:

Java密封类(Sealed Classes)增强详解

Java密封类(Sealed Classes)增强详解 Java 17引入了一个重要的新特性——密封类(Sealed Classes),这一特性旨在增强Java编程语言的能力,提供了一种机制来限制哪些类可以继承一个给定的类或者实现一个给定的…...

鸿蒙如何自动生成二维码?QRCode组件

QRCode 用于显示单个二维码的组件。 说明: 该组件从API Version 7开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。 二维码组件的像素点数量与内容有关,当组件尺寸过小时,可能出现无法展示内容的情况&…...

【分布式知识】MapReduce详细介绍

文章目录 MapReduce概述1. MapReduce编程模型Map阶段Reduce阶段 2. Shuffle和Sort阶段3. MapReduce作业的执行流程4. MapReduce的优化和特性5. MapReduce的配置和调优 MapReduce局限性相关文献 MapReduce概述 MapReduce是一个分布式计算框架,它允许用户编写可以在大…...

JAVA八股

快速失败(fail-fast) 设计的目的是为了避免在遍历时对集合进行并发修改,从而引发潜在的不可预料的错误。 通过迭代器遍历集合时修改集合: 如果你使用Iterator遍历集合,然后直接使用集合的修改方法(如add(…...

关于武汉芯景科技有限公司的限流开关芯片XJ6288开发指南(兼容SY6288)

一、芯片引脚介绍 1.芯片引脚 二、系统结构图 三、功能描述 1.EN引脚控制IN和OUT引脚的通断 2.OCB引脚指示状态 3.过流自动断开...

指令:计算机的语言(五)

2.9 人机交互 ASCII与二进制 对应表略 字节转移指令 lbu:加载无符号字节,从内存中加载1个字节,放在寄存器最右边8位。 sb:存储字节指令,从寄存器的最右边取1个字节并将其写入内存。 复制1个字节顺序如下&#xf…...

C#笔记(1)

解决方案: 【1】组织项目:把项目放在放在一个解决方案中,统一开发,统一编译。 【2】管理项目:开发中的任何问题,在统一编译过程中,都能随时发现。也可以添加第三方的库文件。 命名空间: 命名空…...

SSDF攻击、防御与展望

摘要: 随着无线通信业务的不断发展,频域也越来越成为了一种珍贵的稀缺资源,与此同时,相应的无线电安全问题层出不穷,为无线通信造成了十分恶劣的影响,本文从深入理解认知无线电安全开始,对一些典…...

MedMamba代码解释及用于糖尿病视网膜病变分类

MedMamba原理和用于糖尿病视网膜病变检测尝试 1.MedMamba原理 MedMamba发表于2024.9.28,是构建在Vision Mamba基础之上,融合了卷积神经网的架构,结构如下图: 原理简述就是图片输入后按通道输入后切分为两部分,一部分走…...

单点登录的要点

单点登录(SSO)是一种身份验证服务,它允许用户使用一组凭据登录一次,然后在多个应用程序中访问其他应用程序而无需重新进行身份验证。这样,用户只需一次登录即可访问整个应用生态系统,提高了用户体验并简化了…...

linux线程 | 一点通你的互斥锁 | 同步与互斥

前言:本篇文章主要讲述linux线程的互斥的知识。 讲解流程为先讲解锁的工作原理, 再自己封装一下锁并且使用一下。 做完这些就要输出一堆理论性的东西, 但博主会总结两条结论!!最后就是讲一下死锁。 那么, 废…...

全栈开发小项目

用到的技术栈: nodejswebpackknockoutmongodbPM2rabbitmq 以下是一个综合指南,展示如何将 Node.js、Webpack、Knockout.js、MongoDB、PM2 和 RabbitMQ 集成到一个项目中。 我们将在这一项目中添加 RabbitMQ,用于处理消息队列。这对于任务分…...

批处理一键创建扫描仪桌面打开快捷方式图标 简单直接有效 扫描文档图片的应急策略

办公生活中,我们在安装完多功能一体机的打印驱动之后,找不到扫描文件的地方,如果驱动程序安装正确,我们可以用系统自带的扫描仪程序调用这种打印机或复印机的扫描程序即可,它在电脑系统中的位置一般是:C:\W…...

【服务器知识】Tomcat简单入门

文章目录 概述Apache Tomcat 介绍主要特性版本历史使用场景 核心架构Valve机制详细说明请求处理过程 Tomcat安装Windows系统下Tomcat的安装与配置:步骤1:安装JDK步骤2:下载Tomcat步骤3:解压Tomcat步骤4:配置环境变量&a…...

【前端】Matter:过滤与高级碰撞检测

在物理引擎中,控制物体的碰撞行为是物理模拟的核心之一。Matter.js 提供了强大的碰撞检测机制和碰撞过滤功能,让开发者可以控制哪些物体能够相互碰撞,如何处理复杂的碰撞情况。本文将详细介绍 碰撞过滤 (Collision Filtering) 与 高级碰撞检测…...

wps图标没有坐标轴标题怎么办?wps表格不能用enter下怎么办?

目录 wps图标没有坐标轴标题怎么办 一、在WPS PPT中添加坐标轴标题 二、在WPS Excel中添加坐标轴标题 wps表格不能用enter下怎么办 一、检查并修改设置 二、检查单元格保护状态 三、使用快捷键实现换行 wps图标没有坐标轴标题怎么办 一、在WPS PPT中添加坐标轴标题 插入…...

在ESP-IDF环境中如何进行多文件中的数据流转-FreeRTOS实时操作系统_流缓存区“xMessageBuffer”

一、建立三个源文件和对应的头文件 建立文件名,如图所示 图 1-1 二、包含相应的头文件 main.h 图 2-1 mess_send.h mess_rece.h和这个中类似,不明白的大家看我最后面的源码分享 图2-2 三、声明消息缓存区的句柄 大家注意,在main.c中定义的是全局变…...

ConcurrentLinkedQueue适合什么样的使用场景?

ConcurrentLinkedQueue 是 Java 中一种无界线程安全的队列,适合多线程环境中的高并发场景。以下是一些它特别适合的使用场景: 1. 高频读操作,低频写操作 ConcurrentLinkedQueue 对于实际应用中读操作相对频繁,写操作较少的场景非…...

C语言 | Leetcode C语言题解之第480题滑动窗口中位数

题目: 题解: struct Heap {int* heap;int heapSize;int realSize;bool (*cmp)(int, int); };void init(struct Heap* obj, int n, bool (*cmp)(int, int)) {obj->heap malloc(sizeof(int) * (n 1));obj->heapSize 0;obj->cmp cmp; }bool c…...

LabVIEW开发如何实现降维打击

在LabVIEW开发中实现“降维打击”可以理解为利用软件优势和高效工具来解决复杂的问题,将多维度、多层次的技术简化为容易操作和管理的单一维度,达到出其不意的效果。以下是几种关键策略: 1. 模块化设计与封装 将复杂系统分解为若干模块&…...

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…...

浅谈 React Hooks

React Hooks 是 React 16.8 引入的一组 API,用于在函数组件中使用 state 和其他 React 特性(例如生命周期方法、context 等)。Hooks 通过简洁的函数接口,解决了状态与 UI 的高度解耦,通过函数式编程范式实现更灵活 Rea…...

谷歌浏览器插件

项目中有时候会用到插件 sync-cookie-extension1.0.0:开发环境同步测试 cookie 至 localhost,便于本地请求服务携带 cookie 参考地址:https://juejin.cn/post/7139354571712757767 里面有源码下载下来,加在到扩展即可使用FeHelp…...

Ubuntu系统下交叉编译openssl

一、参考资料 OpenSSL&&libcurl库的交叉编译 - hesetone - 博客园 二、准备工作 1. 编译环境 宿主机:Ubuntu 20.04.6 LTSHost:ARM32位交叉编译器:arm-linux-gnueabihf-gcc-11.1.0 2. 设置交叉编译工具链 在交叉编译之前&#x…...

C++初阶-list的底层

目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...

大话软工笔记—需求分析概述

需求分析,就是要对需求调研收集到的资料信息逐个地进行拆分、研究,从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要,后续设计的依据主要来自于需求分析的成果,包括: 项目的目的…...

椭圆曲线密码学(ECC)

一、ECC算法概述 椭圆曲线密码学(Elliptic Curve Cryptography)是基于椭圆曲线数学理论的公钥密码系统,由Neal Koblitz和Victor Miller在1985年独立提出。相比RSA,ECC在相同安全强度下密钥更短(256位ECC ≈ 3072位RSA…...

R语言AI模型部署方案:精准离线运行详解

R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...

如何在看板中体现优先级变化

在看板中有效体现优先级变化的关键措施包括:采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中,设置任务排序规则尤其重要,因为它让看板视觉上直观地体…...

Docker 运行 Kafka 带 SASL 认证教程

Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明:server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...