1、Java 模块化
1.1、概念介绍
Java模块化相比是大家听到的最多的,也是JDK9的重大更新之一,关于什么是Java 模块系统?官方是这么解释的:一个命名的、自我描述的代码和数据集合。该模块系统包含了:
- 一个新的可选阶段,链接时间,它位于编译时间和运行时间之间,在这个阶段,一组模块可以被组装和优化成一个定制的运行时映像;请参阅 Java Platform, Standard Edition Tools Reference 中的 jlink 工具。
- 往javac jlink java 命令中增加了选项,可以在这些选项中指定模块路径,用于定位模块的定义。
- 引入了模块化JAR文件,它是一个在其根目录中包含一个文件的JAR文件.module-info.class
- 引入了JMOD格式,这是一种类似于JAR的打包格式,除了可以包含本地代码和配置文件之外;请参阅jmod工具。
- 能够将 JDK 的模块组合到各种配置中,包括:
- 对应于 JRE 和 JDK 的配置。
- 配置在内容上大致等同于 Java SE 8 中定义的每个 Compact Profile。
- 仅包含一组指定模块及其所需模块的自定义配置。
- 重新构造了JDK和JRE运行时映像,以适应模块并改善性能、安全性和可维护性。
- 为在运行时映像中命名模块、类和资源定义了一个新的URI方案,而不会揭示映像的内部结构或格式。
-
删除了 endorsed-standards override(认可标准覆盖)机制和 extension (扩展)机制。
-
从Java运行时映像中移除了 rt.jar 和 tools.jar
-
默认情况下,使 JDK 的大多数内部 API 无法访问,但保留一些关键的、广泛使用的内部 API 可访问,直到它们的所有或大部分功能都存在受支持的替代品。运行 jdeps -jdkinternals 命令以确定您的代码是否使用内部 JDK API。
上面这部分大多数人看描述也是一头雾水,接下来让我们通过下面的例子来揭开Java模块化的面纱。
实战
使用IDEA,创建一个基于jdk9的maven项目再创建两个module,一个叫做modulea,另外一个叫做moduleb。
在modulea中,创建一个类,名字叫做ModuleA
moduleb中,也创建一个叫做ModuleB的类
此时,假如我们想要早ModuleB这个类中,调用ModuleA的方法,按照jdk9之前的方式,肯定是先设置好pom版本依赖
然后在ModuleA中直接引用就行了。
运行后打印:
看起来好像没啥差别对吧? 确实,因为JDK的版本升级(最大化)保证了低版本的兼容,在JDK9版本下,这种方式定义的module叫做无名模块,用来兼容老版本升级新jdk时,未定义的情况。
无名模块
什么是无名模块呢?无名模块有一些默认的行为和限制。它无法显示地声明依赖关系,也无法导出任何包。但是,它仍然可以访问其他模块的公共 API。那怎么样可以把一个module定义为JDK9的module呢?
从 JDK 9 开始,Java 引入了模块化系统,每个模块都需要一个 module-info.java 文件来声明模块的信息。module-info.java 文件是模块的入口点,它包含了模块的名称、依赖关系和导出的包等信息。如果你想在 JDK 9 或更高版本中使用模块化特性,你必须在每个模块中声明一个 module-info.java 文件。
module-info.java结构和限制
module 模块名称 { // 导出的包声明 // 依赖关系声明 // 其他模块特定的声明 }各个声明定义:
- 导出的包声明(exports):使用 exports 关键字声明模块导出的包,其他模块可以访问这些包下的公共类和接口。
- 依赖关系声明(requires):使用 requires 关键字声明模块的依赖关系,指定该模块依赖的其他模块。
- 其他模块特定的声明:还可以在 module-info.java 文件中添加其他模块特定的声明,如使用 provides 和 uses 关键字来实现服务提供者接口。
module-info.java
同时 module-info.java 也是有一些其他限制在一个模块中,只能有一个 module-info.java 文件。这个文件定义了模块的名称、依赖关系和其他模块特定的信息。如果在同一个模块中出现多个 module-info.java 文件,编译器将会报错。因此,每个模块只能有一个 module-info.java 文件。 module-info.java 文件应该放在一个名为 "module-name/src/main/java/module-info.java" 的目录中,其中 "module-name" 是模块的名称。 module-info.java还有一些其他的关键字: 1. requires:用于声明一个模块依赖的其他模块。 2. exports:用于声明一个模块导出的包,使其他模块可以访问该包下的公共类和接口。 3. opens:用于声明一个模块开放(暴露)指定的包,允许其他模块访问该包中的类。 4. provides:用于声明一个模块提供的服务接口的实现。 5. uses:用于声明一个模块使用的服务接口。 6. transitive:用于声明一个模块的依赖是传递性的,使依赖当前模块的其他模块也自动依赖当前模块所依赖的模块。
改造
根据定义和限制,我们定义好modulea和moduleb的package-info.java文件
至此,我们就把module和moduleb定义为一个java 模块了。 但此时,再尝试运行ModuleB的main方法,就无法正常运行了
可以看到,报错信息提示虽然 com.gonzo.study.jdk9.modulea 已经定义在模块 study.modulea中,但study.moduleb无法读到它。 当然,idea也很贴心的给出了解决建议,按照建议执行后,回到moduleb的package-info.java,看到加了这么一条。
requires study.modulea; 随后继续运行ModuleB的main方法成功:
思考
通过这个例子,想必大家已经初步理解了模块化的概念,同时也会新增很多疑问,例如:- 如果不定义package-info.java,能不能引入定义了package-info.java的模块?
- 这么定义有什么作用?
模块 | modulea | moduleb | 是否可以引用到 |
是否定义了 package-info.java 文件 | 有 | 无 | 可以 |
无 | 有 | 不可以,报错: Package 'com.gonzo.study.jdk9.modulea' is declared in the unnamed module, but module 'study.moduleb' does not read it | |
无 | 无 | 可以 | |
有 | 有 | 可以,只要在moduleb中生命对modulea的requires |
1. 更好的封装和隔离:模块化系统通过将代码组织成模块,强制实施模块间的明确边界和依赖关系。这样可以更好地封装和隔离代码,减少模块之间的耦合,提高代码的可维护性和可重用性。 2. 显式的依赖管理:模块化系统要求在每个模块中明确声明其依赖关系。这样可以更清晰地了解代码的依赖关系,减少意外的依赖问题,并提供更好的版本管理和冲突解决机制。 3. 更小的运行时环境:模块化系统允许在构建应用程序时只包含所需的模块,从而减少了运行时环境的大小。这可以减少应用程序的启动时间和内存占用,并简化应用程序的部署和分发。 4. 安全性增强:模块化系统通过明确的模块边界和访问控制,提供了更好的安全性。只有明确导出的包才能被其他模块访问,其他模块无法直接访问未导出的包,从而减少了潜在的安全漏洞。 5. 更好的可维护性和可读性:模块化系统鼓励开发者将代码组织成独立的模块,每个模块都有明确的目的和职责。这样可以提高代码的可读性和可维护性,使开发者更容易理解和修改代码。抛开哪些轱辘话,我觉得第三点是最大的进步。 Java早些时候一直被诟病吃内存,其中一个原因就是因为一个大型项目要依赖的第二、第三方包是不计其数的。但实际上引入一个包可能只需要里面的某一小部分类,在JDK9之前只能全部导入。 但有了模块化之后,可以指定需要的包的导入,这样依赖,针对不需要的包就排除在外了。编译运行时,这些被排除的包节省下来的时间和内存也是很客观的。
新版本字符串方案
JDK 9 引入了一种新的字符串实现方案,称为Compact Strings(紧凑字符串),与 JDK 8 的字符串实现方案有一些区别。 在 JDK 8 中,Java 字符串由 char 数组和一个 int 字段(offset)组成,用于存储字符串的字符数据和偏移量。这种实现方式在大多数情况下效果很好,但对于包含大量ASCII字符的字符串,会浪费一些内存空间,因为每个字符都需要占用两个字节。 JDK 9 中的 Compact Strings 方案旨在减少这种内存浪费。在 Compact Strings 中,Java 字符串使用 byte 数组来存储字符数据,而不是 char 数组。对于只包含ASCII字符的字符串,每个字符只需要占用一个字节,这样就可以节省一半的内存空间。 这种改进对于许多应用程序来说是透明的,因为它只是在内部对字符串的实现进行了优化。大多数情况下,应用程序的行为和性能不会受到影响。但对于那些处理大量ASCII字符的应用程序,Compact Strings 可能会带来显著的内存节省和性能提升。 需要注意的是,Compact Strings 方案只适用于默认的编码方案(如UTF-16),对于其他编码方案(如UTF-8),仍然使用传统的 char 数组实现。此外,Compact Strings 的具体实现细节可能因不同的JVM实现而有所不同。 总的来说,JDK 9 的 Compact Strings 方案通过优化字符串的内存占用,提供了更高效的字符串表示方式,特别是对于包含大量ASCII字符的字符串。 JDK8的String源码: JDK9的String源码:
Java Shell
简介: 一个交互式命令行工具。 它允许开发人员在命令行中直接执行和测试Java代码片段,而无需编写完整的Java程序。 Java Shell提供了一个交互式的环境,类似于Python的交互式解释器或Ruby的IRB。它可以用于快速验证代码、尝试新的API功能、学习和教学等场景。 使用Java Shell,你可以逐行输入和执行Java代码,立即查看结果。它支持Java的语法和特性,包括变量定义、表达式计算、方法调用等。你还可以在Java Shell中定义和操作变量,以便在后续的代码片段中使用。 使用方式: 打开命令行,输入 jshell,就进入了java shell的运行环境
简单调试下
关于其他更多的交互,可以参考java shell的官方文档。
JVM 部分
- 将 G1 设为默认垃圾回收器
- 弃用并发标记扫描 (CMS) 垃圾回收器
- 删除 JDK 8 中不推荐使用的 GC 组合
- DefNew + CMS
- ParNew + SerialOld
- Incremental CMS
-
并发标记扫描 (CMS) 的“前台”模式也已被删除。删除了以下命令行标志:
- -Xincgc
- -XX:+CMSIncrementalMode
- -XX:+UseCMSCompactAtFullCollection
- -XX:+CMSFullGCsBeforeCompaction
- -XX:+UseCMSCollectionPassing
命令行标志不再起作用。ParNew 只能与 CMS 一起使用,而 CMS 需要 ParNew。因此,该标志已被弃用,可能会在将来的版本中删除。-XX:+UseParNewGC-XX:+UseParNewGC
以上就是JDK9新增的主要功能,当然还有很多其他功能,没有一一列出来,具体可以参考官方文档查看更多:
Java Platform, Standard Edition What’s New in Oracle JDK 9, Release 9
标签:info,java,JDK,module,说明,JDK9,特性,模块,Java From: https://www.cnblogs.com/gonzo/p/17863170.html