Java版本之间存在哪些具体的不兼容性示例?

49
在Java不同版本之间,是否存在不兼容性问题,即Java源代码/Java类文件针对Java版本X编写的代码在较高版本Y(其中Y>X)下无法编译或运行?
“Java版本”指的是以下版本:
JDK 1.0 (1996年1月) JDK 1.1 (1997年2月) J2SE 1.2 (1998年12月) J2SE 1.3 (2000年5月) J2SE 1.4 (2002年2月) J2SE 5.0 (2004年9月) Java SE 6 (2006年12月)
注意事项: 请尽可能提供参考资料和代码示例。 请在回答中尽量具体和具体化。 被标记为@Deprecated的类不算是向后不兼容。

2
这应该明确地成为社区维基。 - dfa
4
为什么?这是一个完全合法的技术问题... - bdonlan
3
Stephen C:我确实需要知道答案。 - knorv
knorv,不知道我问一下为什么?如果有特定的领域需要考虑向后兼容性,也许我们可以提供更多帮助。 - Sean Reilly
2
@Sean:需要知道这个答案的一个好理由是,在某些必须保持向后兼容性的项目中,为了支持Java而不是.NET、PHP或其他竞争技术进行选择。 - ZJR
显示剩余2条评论
14个回答

25

各版本兼容性说明:

我记得的第一个重大问题是在 Java 1.4 中引入了 assert。这影响了很多 JUnit 代码。


7
引入新的保留关键词应该受到严厉惩罚!他们再次用enum这个关键词。 - Thorbjørn Ravn Andersen
3
然而,他们从未将goto作为保留字删除! - Duncan McGregor
2
有趣的是,在官方注释中没有关于JDBC包不兼容性的评论。还是我错过了? - tangens
@tangens - 可能没有注意到,因为 JDBC 客户端代码不需要更改以针对新 API 进行工作; Java 应用程序通常不会实现这些接口; 数据库供应商会。您可以在 javadoc 的 @since 标签中找到特定信息,JDBC 规范中可能有更多详细信息: http://java.sun.com/products/jdbc/download.html - McDowell

19

首先,Sun实际上认为您提到的所有发布版本(当然不包括1.0)都是“小版本”而非重大版本。

在那个时期,我不知道有任何二进制不兼容的例子。但是,有一些源代码不兼容的例子:

  • 在Java 5中,“enum”成为了保留字;在此之前并不是。因此,在Java 1.4中可以编译的使用枚举作为标识符的源文件,在Java 5.0中将无法编译。不过,您可以使用-source 1.4编译来避免这种情况。

  • 向接口添加方法也可能会破坏源代码的兼容性。如果您实现了一个接口,然后尝试用添加了新方法的JDK编译该实现,则源文件将不再成功编译,因为它未实现接口的所有成员。这经常发生在java.sql.Statement和其他jdbc接口中。这些“无效”实现的已编译形式仍将起作用,除非您调用了不存在的某个方法;如果这样做,将会抛出MissingMethodException异常。

这些只是我能想起来的几个例子,还可能有其他情况。


3
最糟糕的是 JDBC 接口中添加的一些方法依赖于早期版本中不存在的新类型。这意味着无法制作可以在多个 JDK 版本中编译的实现。 - Laurence Gonsalves
3
我在上周的工作中遇到了这个问题。我们最终采用编写动态代理的方式,以获得既能在Java 5又能在Java 6中编译的实现。 - Sean Reilly

15

java.sql.Connection 接口从 Java 1.5 扩展到 Java 1.6,导致所有实现该接口的类的编译失败。


我认为所有的JDBC版本都广泛地做到了这一点。这应该不会让人感到惊讶! - Tom Hawtin - tackline
再次使用JDK 1.8。 - Menachem

11

Swing的每个版本都会改变我们的某些东西,从1.3到1.6。

JDBC问题已经提到了,但现有的代码可以继续使用。

从1.5到1.6,Socket的行为发生了变化,这破坏了Cisco客户端。

当然,还引入了新的保留关键字。

我认为Sun公司最不可原谅的是System.getenv()。它在1.0中有效,然后被弃用并更改为在所有平台上引发错误,理由是Mac没有系统环境变量。然后,Mac得到了系统环境变量,所以在1.5中,它被取消弃用,并且可以正常工作。这没有任何合理的理由。在Mac上返回一个空集合(如果您想关注跨平台一致性的那个级别,Swing存在更大的跨平台问题),甚至在所有平台上都是如此。

我从来没有同意他们关闭该功能,但将其更改为抛出错误只是纯粹的破坏性变化,如果他们要这样做,他们应该将该方法完全删除。

但实际上,在1.0到1.1之间,他们对向后兼容性的关注较少。例如,他们放弃了“private protected”作为修饰符。

因此,总的来说,每个版本都会发生足够的变化,需要仔细评估,这就是为什么您仍然在SO上看到许多1.4问题的原因。


10

我能想到的主要原因是引入了新的保留字:

Java 1.3: strictfp
Java 1.4: assert
Java 5.0: enum

如果先前的任何代码使用这些值作为标识符,那么在更新版本下它们将无法编译。

我还记得在一个我参与的项目中引起问题的另一个问题是,在1.2和1.3之间更改了JInternalFrames的默认可见性。它们默认情况下是可见的,但当我们升级到1.3时,它们似乎全部消失了。


也许他们保留一些未来可能使用的关键字是明智的选择,即使现在还不确定。像“struct”和“function”这样的单词目前是有效的标识符。 - Bart van Heukelom

8

在1.3和1.4之间,Long.parseLong(String)方法对空字符串的处理方式有所不同。1.3返回0值,而1.4会抛出NumberFormatException异常。

不需要重新编译代码,但如果代码依赖于1.3的行为,则停止工作。


7

这是一个变化,但是从1.4转到1.5会导致哪些代码出现问题呢?在我看来,它应该是向后兼容的。 - Yishai

4
以下代码可在Java 1.4下编译,但在Java 1.5或更高版本中不会编译。(Java 5引入了'enum'关键字。注意:如果提供了“-source 1.4”选项,则它将在Java 5中编译。)
public class Example {
    public static void main(String[] args) {
        String enum = "hello";
    }
}

4
显然,发布名称的命名约定是不向后兼容的
  • JDK 1.0(1996年1月23日)
  • JDK 1.1(1997年2月19日)
  • J2SE 1.2(1998年12月8日)
  • J2SE 1.3(2000年5月8日)
  • J2SE 1.4(2002年2月6日)
  • J2SE 5.0(2004年9月30日)
  • Java SE 6(2006年12月11日)
  • Java SE 6更新10、更新12、更新14、更新16
  • Java SE 7 ??? JDK7?

列表来自维基百科。)


3

Java.sql又一次破坏了兼容性:

在1.5版本中,java.sql.Timestamp添加了compareTo(Date)方法。该方法会抛出ClassCastException异常,如果提供的Date不是java.sql.Timestamp的实例。当然,java.sql.Timestamp扩展自Date,而Date已经有一个与所有日期一起工作的compareTo(Date)方法,因此这意味着将Timestamp与(非Timestamp)Date进行比较的代码将在1.5运行时中断。

有趣的是,似乎1.6修复了这个问题。虽然java.sql.Timestamp.compareTo(Date)的文档仍然说“如果参数不是Timestamp对象,则此方法会抛出ClassCastException对象”,但实际实现却不是这样。我猜这是一个文档错误。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接