2013-02-20 77 views
25

我讀的是Joshua Bloch的Effective Java什麼是Java中的二進制兼容性?

在條款17:「僅使用接口來定義類型」中,我遇到了不建議使用接口來存儲常量的解釋。我在下面解釋。

「更糟的是,它代表一個承諾:如果在未來版本的類修改,使其 不再需要使用常量,它仍然必須實現的接口,以確保二進制兼容性 。」

這裏的二進制兼容性意味着什麼?

有人可以用Java中的示例指導我,以顯示代碼是二進制兼容的。

+0

的http:// docs.oracle.com/javase/specs/jls/se7/html/jls-13.html – 2013-02-20 06:15:20

+0

考慮可能破壞二進制兼容性的更改時必不可少的參考: HTTPS://wiki.eclipse。組織/ Evolving_Java-based_APIs_2 – jordanpg 2017-11-01 14:33:44

回答

24

總之,二進制兼容性意味着當你改變你的類時,你不需要重新編譯使用它的類。例如,您刪除或從該類

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

從您的登錄1.jar圖書館更名爲公共或受保護的方法,併發布了新的版本數2.jar。當log-1.jar的用戶下載新版本時,它會在嘗試使用缺少的getLogger(String name)方法時中斷他們的應用程序。

如果您刪除了常量接口(第17項),由於相同的原因,這也會破壞二進制兼容性。

但是,您可以在不破壞二進制兼容性的情況下刪除/重命名此類的私有成員或包私有成員,因爲外部應用程序不能(或不應該)使用它。

+2

理查德E.幾乎沒有他的[博客]一個非常簡單的,很好的例子(http://codefhtagn.blogspot.kr/2010/11/java-binary-compatibility-more-than.html)。它真的**顯示了二進制兼容性的問題,而不是源代碼兼容性的問題,如此答案中所示(使用重命名的方法)。 – 2014-05-09 06:43:51

-1

爲了讓事情看起來很簡單:

,可以運行打算在另一臺計算機上運行相同的二進制代碼的計算機被認爲是二進制兼容。這與源代碼兼容性不同,在這種情況下可能需要重新編譯。

在開發要在多個操作系統上運行的計算機程序時,二進制兼容性是一項主要優勢。

+0

我相信你說的是不一樣的方面的OP問 – 2013-02-20 06:20:47

+0

的問題是有關使用Java二進制兼容性。您剛剛複製並粘貼了「二進制比較法」的維基百科定義的一部分。 – sversch 2017-08-16 04:30:08

6

二進制兼容性

的Java二進制兼容性規定 下條件,modication和類的重新編譯不 沒有必要進一步類導入 - 荷蘭國際集團 的modied類重新編譯。二進制兼容性是語言設計的一種新穎的概念。

Java language specication [7]描述二進制COM的 patible變化如下:

到A型的變化是與二進制 (等價地,不中斷與兼容性) 如果預先存在的預二進制兼容 - 先前鏈接無錯誤的現有二進制文件 將確認鏈接沒有錯誤。

13

爲了更好地理解這個概念,有趣的是看到二進制兼容性並不意味着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,但它依據Libgetstaticgetfield編譯爲兩個不同的VM指令。這打破了在JLS 7 13.4.10提到:

如果要宣稱不聲明爲private未聲明靜態的,改變了現場靜,或反之,則鏈接錯誤,具體的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的維基是一個很好的來源:https://wiki.eclipse.org/Evolving_Java-based_APIs
  • 使接受null值的API是一個值得商榷的做法
  • 這是很容易做,打破API的變化兼容性但不是二元的,反之亦然,因爲它很容易改變方法的內部邏輯
+0

恕我直言,第一個例子是不正確的,因爲你刪除'static',現在是不兼容的API(即,'Lib.i'在字節碼不再工作)。推導某些代碼的API兼容性不取決於客戶端是使用此API,而是依據API兼容性的標準(或規範)。雖然我同意API兼容性並不意味着二進制兼容性。 – floating 2016-02-28 14:11:55

0

如果將來我們希望改變e一些類正在實現的接口(例如,添加一些新的方法)。

如果再加上抽象方法(其他方法),那麼類(實現接口)必須實現額外的方法創建依賴約束和成本開銷來執行相同。

爲了克服這個問題,我們可以在界面中添加默認方法。

這將刪除依賴項以實現其他方法。

我們不需要修改實施類以合併更改。 這稱爲二進制兼容性。

請參考下面的例子:

的界面,我們將使用

//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

+0

默認和靜態方法在Java 8或更高版本中可用。 – 2017-10-18 07:19:30