Java 9 的模块(Module)系统
Java 的模块在Java 9中正式实装,一直没时间来研究一下这个东西,今天就和大家一起学习一下这个功能。
Java模块解决了什么问题
最近很多同学问我,胖哥,该怎么学习?该学习什么?这里胖哥也穿插说一下。不管学东西,一定要先搞清楚学了有什么用,是学了马上就能用上还是以后有用。我觉得在时间有限的情况下,一定要学当前立马有用的东西。接下来咱们就一起来看看Java模块到底有啥用。
我觉得模块化最大的意义就是按照功能把代码逻辑分割开来,就像你干前端,我写后端,他做测试,把整体大概念拆成小概念,用的时候自由组合,按需引用。事实上确实有这方面的作用,但是不仅仅就这么多。
简化类库
JDK类库目前太臃肿了,在一些微型设备上可能用不到全部的功能,在目前的情况下却不得不引用全部的类库。Java 9引入模块功能后,JDK、JRE、甚至是JAR都可以把用不到的类库排除掉,大大降低了依赖库的规模。
真正的访问隔离
在之前只要类是public的,可以直接在整个依赖可传递的范围内访问它。但是很多时候我们需要在某个范围去限制一些类的访问,让这些类具有一些封闭性。在引入模块后,我们就可以做到这些,把一些我们不想暴露的内部实现细节安全地隐藏起来。
什么是模块?
Java 9 引入的模块是在Java包(package)的基础上又引入的一个新的抽象层。基于package这一点很重要,这里需要强调一下。
模块的结构
Java 模块可以由一个或者多个在一起的 Java 包组成。结构可以参考这个图:

创建模块
创建模块需要如下几个步骤:
- 创建一个文件夹,通常是一个包名,例如
cn.felord.module。 - 然后在
cn.felord.module下创建一个module-info.java文件,这个文件被称为模块描述符文件。 - 在模块描述符文件的同级别创建Java包。
- 最后在创建的包下编写你的Java类文件即可。
创建模块规则
创建模块也必须遵守下面的规则:
模块名称必须是唯一的。
模块描述符文件
module-info.java必须有。包名称必须是唯一的。即使在不同的模块中,我们也不能有相同的包名。
每个模块将创建一个 jar 文件。对于多个 jar,我们需要创建单独的模块。
一个项目可以由多个模块组成。
模块类型
模块同样还具有类型,一共有四种。
系统模块
来自 JDK 和 JRE 的模块。可以使用 java --list-modules 列出,这里列出了一部分:
❯ .\java.exe --list-modules
java.base@17
java.compiler@17
java.datatransfer@17
java.desktop@17
java.instrument@17
java.logging@17
java.management@17
java.management.rmi@17
# 省略……
应用程序模块
在应用程序中创建以实现功能的所有模块,日常开发如果涉及到模块应该属于这一类。
自动模块
现有的 jar 文件,感觉像兼容旧的类库。它们其实不是模块。当我们将非模块 jar 添加到模块路径时,会创建具有 jar 名称的模块。该模块有以下特性:
- 默认导出所有包。
- 默认情况下可以访问所有其他模块的类。
未命名模块
添加到类路径中的 jar 和类。当我们将 jar 或类添加到类路径时,所有这些类都会添加到未命名的模块中
- 只导出到其他未命名的模块和自动模块。这意味着,应用程序模块无法访问这些类。
- 它可以访问所有模块的类。
模块描述符文件
一个模块只有一个module-info.java,而且它是有格式要求的,我们来了解一下。
声明模块
我们只需要在module-info.java这样做就能声明一个名称为cn.felord的模块:
module cn.felord {
}
模块名称应该是两个单词以上,并用英文句号.隔开,上面是一个空模块。
导出包
默认情况下,模块里下所有包都是私有的,即使被外部依赖也无法访问,一个模块之内的包还遵循之前的规则不受模块影响。我们可以使用 export 关键字公开特定的包,就像这样:
module cn.felord {
exports cn.felord.pkg;
exports cn.felord.util;
}
请注意cn.felord.pkg和exports cn.felord.util不能是空包,导出的包必须声明Java对象。
不能导出具体的Java类。
定向导出包
还有一种是定向导出,该包仅仅向某模块公开。就像什么特供酒、特供烟一样。它的语法是:
exports <包名> to <目标模块1>,<目标模块2>,<目标模块3>,...
我们把上面的cn.felord.util定向导出给com.xxx:
module cn.felord {
exports cn.felord.pkg to com.xxx,com.ooo;
exports cn.felord.util to com.xxx;
}
在上述情况下,所有模块都可以访问 cn.felord.pkg,但只有com.xxx模块能访问 cn.felord.util。
定向导包的作用域是模块。
依赖
如果一个模块要访问从其它模块导出的包,则该模块必须使用requires关键字导入要访问的包所在的模块。就像上面,虽然cn.felord模块向com.ooo开放了cn.felord.pkg包, 即使com.ooo依赖了cn.felord也不能直接使用该包下面的类,需要这样做:
module com.ooo {
exports com.ooo.pkg;
// 注释掉 Pkg就变红了 cn.felord.util下面的类无法使用
requires cn.felord;
}
requires的作用域是模块。
静态依赖
有时我们只在编译时需要一些模块,它们在运行时是可选的。例如,测试或代码生成库。这就需要用到静态导入了,关键字是requires static,例如:
module com.xxx {
// 移除pom 依赖编译不了
requires static cn.felord;
}
在此示例中,cn.felord 在编译时是必需的,但在运行时是可选的,有点类似Maven中的<scope>compile</scope>。
依赖传递
这看起来越来越像Maven了!a模块依赖b模块,b模块依赖c模块,如果a模块想用c模块公开的包的话,按照前面的规则需要再requires模块c。现在借助于requires transitive就可以这样干,因为b承上启下,我们可以这样:
module b {
exports b.pkg;
// 开启依赖传递
requires transitive c;
}
module c {
exports c.pkg
}
module a {
requires b;
}
所有依赖
b的模块将自动依赖c导出的包,export to定向导出的包优先级最高。
使用服务
使用 uses 关键字,我们可以指定我们的模块需要或使用某些服务。这个服务通常是一个接口或抽象类。它不应该是一个实现类。
module com.xxx {
requires com.ooo;
// 移除pom 依赖编译不了
requires static cn.felord;
uses com.ooo.pkg.Read;
}
uses只能从模块自己的包中或者requires、requires static以及requires transitive传递过来的接口或者抽象类。
uses用于指定所需要的服务类或者接口。
给予服务
我们可以通过 provides ... with ...语法,在模块中声明一些服务的实现供其它模块(通过uses)使用。
开放反射
反射 API 的 Java 9 封装和安全性得到了改进。使用反射,我们甚至可以访问对象的私有成员。
从 java 9 开始,默认情况下不打开。我们可以明确地通过open授予其它模块反射权限。
open com.xxx{
}
在这种情况下,com.xxx 模块的所有包都可以使用反射访问。
opens
我们不想全部开放反射访问的话还可以使用opens关键字来指定反射可以访问的包:
module com.xxx{
opens com.xxx.reflect;
}
opens … to
当然我们还可以将特定的包开放给指定的模块来反射访问:
module com.xxx{
opens com.xxx.reflect to com.ooo;
}
com.xxx模块的com.xxx.reflect包将开放给com.ooo模块来反射访问。
总结
模块的东西主要是理解,实际运用主要用来系统瘦身、依赖jar级别的隔离。这个自己用Java 9 以上版本建一个多模块的Maven或者Gradle项目,按照上面实验一下就明白了。
关注公众号:Felordcn 获取更多资讯
Java 9 的模块(Module)系统的更多相关文章
- python学习之算法、自定义模块、系统标准模块(上)
算法.自定义模块.系统标准模块(time .datetime .random .OS .sys .hashlib .json和pickle) 一:算法回顾: 冒泡算法,也叫冒泡排序,其特点如下: 1. ...
- python模块module package
python模块module package module package 常用模块 模块与包的区别 模块分为内置模块.第三方模块,自定义模块 程序会先从内置到第三方再到当前工作目录下去找你导入的 ...
- python2.7入门---模块(Module)
来,这次我们就看下Python 模块(Module).它是一个 Python 文件,以 .py 结尾,包含了 Python 对象定义和Python语句.模块让你能够有逻辑地组织你的 Pytho ...
- Java生鲜电商平台-系统异常状态的设计与架构(APP应用或者生鲜小程序)
Java生鲜电商平台-系统异常状态的设计与架构 说明:在实际开发Java生鲜电商平台的时候,异常状态的设计关系着整体系统的性能问题,架构设计,以及稳定性方面,对此,我根据实际的业务场景,进行了系统设计 ...
- SSM开发基于Java EE在线图书销售系统
SSM(Spring+Spring MVC+MyBatis)开发基于Java EE在线图书销售系统 网站成功建立和运行很大部分取决于网站开发前的规划,因此为了在网站建立过程中避免一些不 ...
- Java实现抽奖模块的相关分享
Java实现抽奖模块的相关分享 最近进行的项目中,有个抽奖的需求,今天就把相关代码给大家分享一下. 一.DAO层 /** * 获取奖品列表 * @param systemVersion 手机系统版本( ...
- Cortex-M3动态加载三(模块调用系统函数)
在我的arm动态加载实验中需要解决一个模块调用系统函数的问题,可以使用以下的一个方法.将系统函数固定在某一段地址空间,然后导出这一块的符号表到符号文件中,要记载的模块link的时候使用这个符号表文件, ...
- [原创]Java应用性能远程监控系统(C/S架构)
Java应用性能远程监控系统(使用C/S架构) 适用于监控所有Java应用,具有堆内存监控.方法区监控.GC监控.类加载监控.类编译监控与线程监控,提供堆快照下载,线程快照下载.体验网址:http:/ ...
- python中根据字符串导入模块module
python中根据字符串导入模块module 需要导入importlib,使用其中的import_module方法 import importlib modname = 'datetime' date ...
随机推荐
- 《PHP程序员面试笔试宝典》——如何应对自己不会回答的问题?
如何巧妙地回答面试官的问题? 本文摘自<PHP程序员面试笔试宝典> 在面试的过程中,对面试官提出的问题求职者并不是都能回答出来,计算机技术博大精深,很少有人能对计算机技术的各个分支学科了如 ...
- 9、架构--动静分离、rewrite、HTTPS
笔记 1.晨考 1.说明反向代理跟负载均衡有哪些区别? 2.负载均衡分配流量的几种方式? 1.轮询 2.权重 3.ip_hash 3.负载均衡部署BBS的步骤 2.昨日问题 3.今日内容 1.资源分离 ...
- Solution -「JOISC 2020」「UOJ #509」迷路的猫
\(\mathcal{Decription}\) Link. 这是一道通信题. 给定一个 \(n\) 个点 \(m\) 条边的连通无向图与两个限制 \(A,B\). 程序 Anthon ...
- 施耐德NOE77101后门漏洞分析
固件下载地址: GitHub - ameng929/NOE77101_Firmware 文件目录结构,这里只列出了一些主要的文件信息: ├── bin ├── ftp ├── fw ├── rdt ├ ...
- [题解]第十一届北航程序设计竞赛预赛——H.高中数学题
题目描述 解题思路 可以求得通项公式:an = 2n + 1,所以问题就变成等差数列求异或和,这个具体为什么对我还不能很好地解释清楚,先挖坑吧. 附:c++代码 1 #include <iost ...
- docker ——从docker容器的内部,连接本机的mysql
所以我有一个Nginx运行在一个docker容器,我有一个mysql运行在localhost,我想连接到我的Nginx内的MySql. MySql在localhost上运行,并且不将端口暴露给外部世界 ...
- windows下cuda、cudnn以及pytorch的安装
一.在anaconda下配置cuda.cudnn以及pytorch环境 1.打开Anaconda Prompt,输入 conda create -n pytorch python=3.8 ...
- selenium+python自动化101-使用execute_script() 方法获取 JavaScript 返回值
前言 之前经常使用 execute_script() 方法执行 JavaScript 的来解决页面上一些 selenium 无法操作的元素,但是一直无法获取执行的返回值. 最近翻文档,发现 execu ...
- linux多进/线程编程(1)—— 基础概念(PCB、MMU、进程状态)
学习大概就是不断迭代.重构的过程,不复习的学习是不负责任的,亦是无用的. 本系列博客主要作为个人记录,主要是贴图和代码,不做详细解释,以后有时间可能会重写:从下一篇开始上代码,代码可以运行是对自己的最 ...
- Tableau绘制K线图、布林线、圆环图、雷达图
Tableau绘制K线图.布林线.圆环图.雷达图 本文首发于博客冰山一树Sankey,去博客浏览效果更好.直接右上角搜索该标题即可 一. K线图 1.1 导入数据源 1.2 拖拽字段 将[日期]托到列 ...