【问题标题】:Java backward compatibility doesn't workJava 向后兼容性不起作用
【发布时间】:2013-09-12 12:16:39
【问题描述】:

我创建了用于编码/解码属性文件的库。图书馆有两个主要用途:

  1. 对属性文件进行编码并将其保存到另一个文件中。
  2. 从编码文件返回键值(解码文件,将结果作为字符串存储在内存中,将字符串加载到属性对象并从属性对象返回结果)。

一切似乎都运行良好,但今天我注意到该库在 java 1.5 上不起作用。我注意到解码端出现了问题,所以让我们关注这段代码。假设负责解码的代码如下所示:

String props = "key1=val1\nkey2=val2";
Properties p = new Properties();
p.load(new StringReader(props));
p.list(System.out);

经过几次测试,我发现问题出在这一行:

p.load(new StringReader(props));

我发现 java 1.5 中的 Properties 类没有 load(Reader) 声明。为了满足 java 1.5 API 的要求,我将此行更改为 load(InputStream)。现在一切正常,但问题是。

我使用 gradle 编译项目,我知道这个库应该在 java 1.5+ 上运行(我的计算机上安装了 java 1.7)所以我在 build.gradle 中添加了这两行

sourceCompatibility = '1.5'
targetCompatibility = '1.5'

我认为 java 编译器会知道我要编译与 java 1.5 兼容的代码,并会显示适当的错误。为了确保这不是 gradle 问题,我从命令行编译了 java 代码,但结果相同(编译器没有显示任何错误)。那么为什么编译器在编译时不显示任何错误呢?

Java 1.5 属性类 API:http://docs.oracle.com/javase/1.5.0/docs/api/java/util/Properties.html

Java 1.6 属性类 API:http://docs.oracle.com/javase/6/docs/api/java/util/Properties.html

[更新]

-source-target 都不会检查 API 兼容性。如果是这样,我如何在 gradle 中检查它? 正如 millimoose 所写,maven 有这个插件 (http://mojo.codehaus.org/animal-sniffer-maven-plugin/index.html) 但是 gradle 呢?

【问题讨论】:

  • -source-target 都不检查您是否使用给定 JDK 版本上不存在的 API 方法。不过有一个 Maven 插件:mojo.codehaus.org/animal-sniffer-maven-plugin/index.html
  • 好的,但是如何防止呢?编译时需要使用不同的jdk版本吗?
  • 要么使用那个 Maven 插件,要么使用不同的 JDK,是的。
  • 谢谢,我用的是 gradle,所以我会尝试为它找到合适的插件。顺便说一句,使用-source-target 是什么意思?
  • 目标:因为每个新的JDK版本都会引入一个新的字节码版本。旧 JVM 无法以新格式运行代码。来源:因为偶尔会引入新的语言特性,而新的语言特性无法在旧的 JVM 上运行。源选项会阻止您使用更新的语言功能。

标签: java gradle


【解决方案1】:

请参阅 javac 文档中名为“交叉编译”和“交叉编译示例”的部分。

http://docs.oracle.com/javase/7/docs/technotes/tools/solaris/javac.html#crosscomp-options

具体这部分:

在以下情况下使用 -bootclasspath 和 -extdirs 很重要 交叉编译;请参阅下面的交叉编译示例.......如果你不 指定引导类的正确版本,编译器将 使用旧的语言规则(在本例中,它将使用 1.6 版 Java 编程语言)与新的引导程序相结合 类,这可能导致类文件不适用于旧版本 平台(在本例中为 Java SE 6),因为对不存在的引用 方法可以包含在内。

【讨论】:

  • +1:这是一个很棒的发现,我什至不知道/忘记了这些东西的存在
【解决方案2】:

-source 开关仅指示编译器在您使用指定版本不支持的语言结构时给出编译错误。例如,将 try-with-resources 与 -source 1.6 一起使用会导致编译错误,因为它仅在 Java 7 及更高版本中受支持。它的用途更像是一种健全性检查(即:我的代码是否仍然与 Java 1.x 版兼容)

-target 开关指示编译器发出与指定版本兼容的字节码。即:编译后的代码可以在指定版本的虚拟机上运行。

但是,这些开关都不会使编译器检查与早期 Java 版本的 Java 库的兼容性。这就是为什么从 Java 7 开始,如果您使用 -target 1.6(或更早版本),编译器会发出警告,您还应该指定 -bootclasspath 指向该 Java 版本的 Java 运行时库集,以便它可以检查是否您的代码仅使用该 Java 版本的类和方法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多