Java 中的二进制兼容性是啥?

Posted

技术标签:

【中文标题】Java 中的二进制兼容性是啥?【英文标题】:What is binary compatibility in Java?Java 中的二进制兼容性是什么? 【发布时间】:2013-02-05 01:44:06 【问题描述】:

我正在阅读 Joshua Bloch 的 Effective Java。

在第 17 条:“仅使用接口来定义类型”中,我遇到了不建议使用接口来存储常量的解释。我把解释放在下面。

“更糟糕的是,它代表了一种承诺:如果在未来的版本中修改了类,以便它 不再需要使用常量,它仍然必须实现接口以确保二进制 兼容性。”

这里的二进制兼容性是什么意思?

谁能指导我用 Java 中的示例来说明代码是二进制兼容的。

【问题讨论】:

docs.oracle.com/javase/specs/jls/se7/html/jls-13.html 在考虑可能破坏二进制兼容性的更改时必不可少的参考:wiki.eclipse.org/Evolving_Java-based_APIs_2 【参考方案1】:

简而言之,二进制兼容性意味着当你改变你的类时,你不需要重新编译使用它的类。例如,您从此类中删除或重命名了公共或受保护方法

public class Logger implements Constants 
   public Logger getLogger(String name) 
         return LogManager.getLogger(name);
   

从您的 log-1.jar 库中发布一个新版本作为 log-2.jar。当您的 log-1.jar 的用户下载新版本时,当他们尝试使用缺少的 getLogger(String name) 方法时,它将破坏他们的应用程序。

如果你删除常量接口(第 17 项),这也会破坏二进制兼容性,原因相同。

但是您可以删除/重命名此类的私有或包私有成员而不会破坏二进制兼容性,因为外部应用程序不能(或不应该)使用它。

【讨论】:

令我惊讶的是,官方文档无法用这种简单程度来解释他们的东西。我不确定他们为什么要使用律师通常在条款和协议中使用的类似语言。 Richard E. Little 在他的blog 上有一个非常简单、很好的例子。它真的显示了二进制兼容性而不是源代码兼容性的问题,如this answer所示(使用重命名的方法)。 @Tarik 你尝试过哪些官方文档可以做出如此大胆的声明?【参考方案2】:

为了更好地理解这个概念,有趣的是,二进制兼容性并不意味着 API 兼容性,反之亦然。

API 兼容但不兼容二进制:静态删除

库版本 1:

public class Lib 
    public static final int i = 1;

客户端代码:

public class Main 
    public static void main(String[] args) 
        if ((new Lib()).i != 1) throw null;
    

使用版本 1 编译客户端代码:

javac Main.java

用版本2替换版本1:删除static

public class Lib 
    public final int i = 1;

重新编译只是版本2,不是客户端代码,然后运行java Main:

javac Lib.java
java Main

我们得到:

Exception in thread "main" java.lang.IncompatibleClassChangeError: Expected static field Lib.i
        at Main.main(Main.java:3)

发生这种情况是因为即使我们可以在 Java 中为 static 和成员方法编写 (new Lib()).i,它也会根据 Lib 编译成两个不同的 VM 指令:getstaticgetfield。 JLS 7 13.4.10 提到了这个休息时间:

如果未声明为私有的字段未声明为静态并且更改为声明为静态,反之亦然,如果该字段被预先存在的二进制文件使用,则会导致链接错误,特别是 IncompatibleClassChangeError期望其他类型的字段。

我们需要重新编译 Mainjavac Main.java 才能使其与新版本一起使用。

注意事项:

从像 (new Lib()).i 这样的类实例调用静态成员是不好的风格,会引发警告,并且永远不应该这样做 这个例子是人为设计的,因为非静态final 原语是无用的:总是使用static final 作为原语:private final static attribute vs private final attribute 反射可以使用to see the difference。但是反射也可以看到私有字段,这显然会导致不应该算作中断的中断,所以不算。

二进制兼容但不兼容 API:空前置条件强化

版本 1:

public class Lib 
    /** o can be null */
    public static void method(Object o) 
        if (o != null) o.hashCode();
    

版本 2:

public class Lib 
    /** o cannot be null */
    public static void method(Object o) 
        o.hashCode();
    

客户:

public class Main 
    public static void main(String[] args) 
        Lib.method(null);
    

这一次,即使更新Lib后重新编译Main,第二次调用也会抛出,但不会第一次。

这是因为我们以一种 Java 在编译时无法检查的方式更改了 method 的合同:在它可以占用 null 之前,之后不再。

注意事项:

Eclipse wiki 是一个很好的来源:https://wiki.eclipse.org/Evolving_Java-based_APIs 制作接受 null 值的 API 是有问题的做法 进行破坏 API 兼容性但不破坏二进制的更改比反之更容易,因为更改方法的内部逻辑很容易

C 二进制兼容性示例

What is an application binary interface (ABI)?

【讨论】:

恕我直言,第一个示例不正确,因为您删除了static,它现在是 API 不兼容(即,字节码中的 Lib.i 不再起作用)。推理某些代码的 API 兼容性不取决于客户端是否使用此 API,而是取决于 API 兼容性的标准(或规范)。虽然我同意 API 兼容性并不意味着二进制兼容性。【参考方案3】:

二进制兼容性

Java 二进制兼容性规定了以下条件 对类进行哪些修改和重新编译 不需要重新编译其他类 import- 修改后的类。二进制兼容性是新奇的 语言设计的概念。

Java language specication [7] 描述二进制 com- 兼容的变化如下:

对类型的更改是二进制兼容的 (等效地,不破坏兼容性) 预先存在的二进制文件(如果是预先存在的二进制文件) 以前没有错误的链接将CON- 继续链接没有错误。

【讨论】:

【参考方案4】:

如果将来我们希望更改某些类正在实现的接口(例如,添加一些新方法)。

如果我们添加抽象方法(附加方法),那么类(实现接口)必须实现附加方法创建依赖约束和执行相同的成本开销。

为了克服这个问题,我们可以在界面中添加默认方法。

这将删除实现附加方法的依赖关系。

我们不需要修改实现类来合并更改。 这称为二进制兼容性。

请参考以下示例:

我们要使用的界面

    //Interface       
    interface SampleInterface
            
                // abstract method
                public void abstractMethod(int side);

                // default method
                default void defaultMethod() 
                   System.out.println("Default Method Block");
                

                // static method
                static void staticMethod() 
                    System.out.println("Static Method Block");
                
            


//The Class that implements the above interface.

    class SampleClass implements SampleInterface
    
        /* implementation of abstractMethod abstract method, if not implemented 
        will throw compiler error. */
        public void abstractMethod(int side)
        System.out.println(side*side);

        public static void main(String args[])
        
            SampleClass sc = new SampleClass();
            sc.abstractMethod(4);

            // default method executed
            sc.defaultMethod();

            // Static method executed
            SampleInterface.staticMethod();

        
    

注意:更多详细信息请参考default methods

【讨论】:

Java 版本 8 或更高版本中提供了默认和静态方法。【参考方案5】:

为了让事情看起来简单:

可以运行旨在在另一台计算机上运行的相同二进制代码的计算机被称为二进制兼容的。这与源代码兼容性不同,后者可能需要重新编译。

在开发要在多个操作系统上运行的计算机程序时,二进制兼容性是一个主要优势。

【讨论】:

我相信你所说的与 OP 所要求的不是同一个方面 问题是关于 Java 中的二进制兼容性。您刚刚复制并粘贴了 Wikipedia 中“二进制兼容性”定义的一部分。

以上是关于Java 中的二进制兼容性是啥?的主要内容,如果未能解决你的问题,请参考以下文章

java 中的<<是啥意思?byte a= 64 ,i=a<<2,为啥i 就等于256?

JAVA中 x=10035 x值是多少,计算步骤是啥?

Java中&是啥意思?

java中的“char”指的是啥?

Java二进制兼容性原理

java中的String的默认编码是啥