Java 教程
- Java - 首页
- Java - 概述
- Java - 历史
- Java - 特性
- Java 与 C++
- JVM - Java 虚拟机
- Java - JDK vs JRE vs JVM
- Java - Hello World 程序
- Java - 环境搭建
- Java - 基本语法
- Java - 变量类型
- Java - 数据类型
- Java - 类型转换
- Java - Unicode 系统
- Java - 基本运算符
- Java - 注释
- Java - 用户输入
- Java - 日期和时间
Java 控制语句
- Java - 循环控制
- Java - 决策制定
- Java - If-else
- Java - Switch
- Java - For 循环
- Java - For-Each 循环
- Java - While 循环
- Java - do-while 循环
- Java - Break
- Java - Continue
面向对象编程
- Java - OOPs 概念
- Java - 对象和类
- Java - 类属性
- Java - 类方法
- Java - 方法
- Java - 变量作用域
- Java - 构造函数
- Java - 访问修饰符
- Java - 继承
- Java - 聚合
- Java - 多态
- Java - 重写
- Java - 方法重载
- Java - 动态绑定
- Java - 静态绑定
- Java - 实例初始化块
- Java - 抽象
- Java - 封装
- Java - 接口
- Java - 包
- Java - 内部类
- Java - 静态类
- Java - 匿名类
- Java - 单例类
- Java - 包装类
- Java - 枚举
- Java - 枚举构造函数
- Java - 枚举字符串
Java 内置类
Java 文件处理
Java 错误和异常
- Java - 异常
- Java - try-catch 块
- Java - try-with-resources
- Java - 多重 catch 块
- Java - 嵌套 try 块
- Java - Finally 块
- Java - throw 异常
- Java - 异常传播
- Java - 内置异常
- Java - 自定义异常
Java 多线程
- Java - 多线程
- Java - 线程生命周期
- Java - 创建线程
- Java - 启动线程
- Java - 线程连接
- Java - 线程命名
- Java - 线程调度器
- Java - 线程池
- Java - 主线程
- Java - 线程优先级
- Java - 守护线程
- Java - 线程组
- Java - 关闭钩子
Java 同步
Java 网络
- Java - 网络
- Java - 套接字编程
- Java - URL 处理
- Java - URL 类
- Java - URLConnection 类
- Java - HttpURLConnection 类
- Java - Socket 类
- Java - 泛型
Java 集合
Java 接口
Java 数据结构
Java 集合算法
高级 Java
- Java - 命令行参数
- Java - Lambda 表达式
- Java - 发送邮件
- Java - Applet 基础
- Java - Javadoc 注释
- Java - 自动装箱和拆箱
- Java - 文件不匹配方法
- Java - REPL (JShell)
- Java - 多版本 Jar 文件
- Java - 私有接口方法
- Java - 内部类菱形运算符
- Java - 多分辨率图像 API
- Java - 集合工厂方法
- Java - 模块系统
- Java - Nashorn JavaScript
- Java - Optional 类
- Java - 方法引用
- Java - 函数式接口
- Java - 默认方法
- Java - Base64 编码解码
- Java - Switch 表达式
- Java - Teeing 收集器
- Java - 微基准测试
- Java - 文本块
- Java - 动态 CDS 存档
- Java - Z 垃圾收集器 (ZGC)
- Java - 空指针异常
- Java - 打包工具
- Java - 密封类
- Java - 记录类
- Java - 隐藏类
- Java - 模式匹配
- Java - 简洁数字格式化
- Java - 垃圾回收
- Java - JIT 编译器
Java 杂项
- Java - 递归
- Java - 正则表达式
- Java - 序列化
- Java - 字符串
- Java - 进程 API 改进
- Java - 流 API 改进
- Java - 增强的 @Deprecated 注解
- Java - CompletableFuture API 改进
- Java - 流
- Java - 日期时间 API
- Java 8 - 新特性
- Java 9 - 新特性
- Java 10 - 新特性
- Java 11 - 新特性
- Java 12 - 新特性
- Java 13 - 新特性
- Java 14 - 新特性
- Java 15 - 新特性
- Java 16 - 新特性
Java API 和框架
Java 类引用
- Java - Scanner
- Java - 数组
- Java - 字符串
- Java - Date
- Java - ArrayList
- Java - Vector
- Java - Stack
- Java - PriorityQueue
- Java - LinkedList
- Java - ArrayDeque
- Java - HashMap
- Java - LinkedHashMap
- Java - WeakHashMap
- Java - EnumMap
- Java - TreeMap
- Java - IdentityHashMap
- Java - HashSet
- Java - EnumSet
- Java - LinkedHashSet
- Java - TreeSet
- Java - BitSet
- Java - Dictionary
- Java - Hashtable
- Java - Properties
- Java - Collection
- Java - Array
Java 有用资源
Java - 多版本 Jar 文件
多版本 JAR 功能在 Java 9 中引入。它允许使用与多个 Java 版本相关的多个版本的类。例如,许多第三方库或框架使用 Java。由于 Java 作为一门语言不断发展,并且每个主要版本都会向该语言添加许多新功能。由于第三方库/框架必须重写其代码库以适应新功能,因此团队极不情愿使用新功能。这阻碍了他们迁移到新的Java 版本。
为了解决维护同一文件的多源代码或特定于平台的文件版本的问题,引入了多版本 JAR 功能。请考虑以下示例
一个典型的 jar 文件将所有类都放在根级别。
jar root - Calculator.class - Util.class - Math.class - Service.class
如果我们有 Java 9 功能特定的Util 类,则该 jar 无法在 JRE 8 或更低版本中使用。
在多版本 Jar 中,格式已增强,可以维护和使用特定于平台的不同版本的Java 类 或资源。在 JAR 中,文件 MANIFEST.MF 文件在其主部分中有一个条目 Multi-Release: true。META-INF 目录还包含一个 versions 子目录,其子目录(从 Java 9 开始为 9)存储特定于版本的类和资源文件。
使用 MANIFEST.MF,我们可以将特定于 Java 9 或更高版本的类指定在不同的位置,如下所示
Java 多版本 Jar 文件目录结构
jar root - Calculator.class - Util.class - Math.class - Service.class META-INF - versions - 9 - Util.class - Math.class - 10 - Util.class - Math.class
现在,如果 JRE 不支持多版本 jar,则它将选择根级别的类进行加载和执行,否则将加载特定于版本的类。例如,如果上述 jar 在 Java 8 中使用,则将使用根级别的 Util.class。如果同一 jar 由 Java 9 执行,则将选择 Java 9 特定版本的类,依此类推。这样,第三方库/框架可以支持新功能,而无需更改针对较低版本编写的源代码。
在 Java 中创建和使用多版本 Jar 文件
在此示例中,我们将创建和使用一个多版本 jar,以使 Tester.java 文件有两个版本,一个用于 jdk 7,一个用于 jdk 9,并在不同的 jdk 版本上运行它。
以下是在 Java 中创建和使用多版本 jar 文件的步骤:
步骤 1:创建 Java 7 特定的 Java 类
让我们创建一个 Java 类,该类可能包含 Java 7 特定的代码和功能,这些功能在 Java 9 之前不可用。在此示例中,我们只是简单地打印一条消息以展示此功能的使用。
创建一个文件夹 c:/test/java7/com/tutorialspoint。创建包含以下内容的 Tester.java:
Tester.java
这是一个简单的代码,当程序执行时,我们将打印一条 Java 7 特定的消息。
package com.tutorialspoint; public class Tester { public static void main(String[] args) { System.out.println("Inside java 7"); } }
步骤 2:创建 Java 9 特定的 Java 类
让我们创建一个 Java 类,该类可能包含特定于 Java 9 增强的代码和功能,这些功能在 Java 9 之前不可用。在此示例中,我们只是简单地打印一条消息以展示此功能的使用。
创建文件夹 c:/test/java9/com/tutorialspoint。创建 Tester.java 文件,内容如下:
Tester.java
这与上面的代码类似,在程序执行时打印 Java 9 特定的消息。
package com.tutorialspoint; public class Tester { public static void main(String[] args) { System.out.println("Inside java 9"); } }
为了使用多版本 jar 功能,必须确保两个类的签名相同。公共接口(例如方法签名)在两个类中应该相同。
步骤 3:使用目标版本编译
C:\test > javac --release 9 java9/com/tutorialspoint/Tester.java C:\JAVA > javac --release 7 java7/com/tutorialspoint/Tester.java
步骤 4:创建多版本 JAR
C:\JAVA > jar -c -f test.jar -C java7 . --release 9 -C java9. Warning: entry META-INF/versions/9/com/tutorialspoint/Tester.java, multiple resources with same name
步骤 5:使用 JDK 7 运行 JAR
C:\JAVA > java -cp test.jar com.tutorialspoint.Tester Inside Java 7
步骤 6:使用 JDK 9 运行 JAR
C:\JAVA > java -cp test.jar com.tutorialspoint.Tester Inside Java 9
结论
我们可以看到,使用多版本 Jar 功能,可以在不影响向后兼容性的情况下创建类的多个版本。我们展示了可以使用相同的命令来执行一个类。根据 META-INF 文件中列出的 JRE 版本,会选择相应的类。如果 JRE 版本较低,不支持多版本 jar,则会选择根级别的类,而不是版本特定的类。最后,版本化类的公共方法的签名应该相同,以便此功能能够完美运行。