2014-10-16 75 views
5

想象一下,我們有一些類A.java。它以某種方式使用B.java,即導入它,調用它的方法,使用它的屬性等。我們編譯這些文件。編譯後,來自導入類的哪些信息存儲在編譯類中?

現在我想深入瞭解我們所得到的結果。

  1. A.class是否存儲關於B的一些信息?什麼信息 - 只是名稱,被調用的方法,使用的最終變量? 如果在編譯後的A.class中沒有太多有關B的信息 - 爲什麼我們不能編譯沒有B.class的A?

  2. 如果我們將bundle中的B.class替換爲另一個B.class - 它何時工作,何時不工作?

  3. 如果A.class與新的B.class運行正常,那麼A.class可以使用來自OLD B.class的一些信息,該信息在編譯期間併入A.class中?即我們最終可能會在我們的項目中有一個混合的 B.class邏輯?

  4. 基於上述問題的答案:我們可以以某種方式編譯依賴於其他類而不知道它們在編譯時的確切實現的類嗎,並且只在運行時提供依賴性嗎?

+3

是不是「編程接口」是關於什麼? – 2014-10-16 19:36:10

回答

3

「導入」關鍵字實際上並沒有導入什麼。基本上它只是一種通過類名引用類的完整路徑的方法。所以當我們說

import java.lang.String; 

然後我們可以在我們的代碼中通過它的名字「String」來引用那個類。在編譯時,任何我們擁有類「String」的地方都會被替換爲「java.lang.String」。而已。

這也是爲什麼,如果你有一個以上的類具有相同的名稱,但在不同的包,你可以輸入最多隻是其中之一。其他課程必須以其全名。

走上問題:

1.確實的A.class存儲有關其自身內部乙的一些信息?只是名稱,被調用的方法,使用的最終變量?如果編譯後的A.class中沒有太多有關B的信息 - 爲什麼我們不能編譯沒有B.class的A?

不是真的。當您調用其他類時,類A將只使用完全限定的類名稱和方法簽名。使用我們的字符串例如,調用

mystring.charAt(0) 

將編譯成字節碼是這樣看的東西:

aload_1 [myString] 
    iconst_0 
    invokevirtual java.lang.String.charAt(int) 

其他類的任何內部狀態被存儲在我們的A類裏面除了內聯可能常量。因此,如果價值可能在未來發生變化,請務必在字段public final上注意。 (參見下面的解決方法)

當我們編譯A類時,我們需要B.class,因此編譯器可以確保類B具有我們要使用的那些方法。

2.如果我們在與B.class包這是另外一個人更換B.class - 當它工作,何時不? 新B.class會工作,如果完全限定域名(包裝等)是相同的,它具有我們正在嘗試使用正確的方法簽名。新的B類可以添加新的方法,甚至可以使用不同的方法實現。但是,它不能修改我們使用的方法的方法簽名。如果舊方法不再存在或者其簽名不同,我們將在編譯時得到一個java.lang.LinkageError

3.如果A.class與新的B.class運行正常,那麼A.class可以使用來自OLD B.class的一些信息,它在編譯過程中併入A.class中?即我們最終是否可以在我們的項目中使用B.class的混合邏輯?從老B.這

唯一的問題可能是內聯常量就是爲什麼Java Coding Guidelines項目31項(第115頁)說:「不要使用公共決賽,其值在以後的版本可能恰克常量」。 相反,做一個getter方法: 例如:

class BadFoo{ 
    //bad, VERSION could be inlined 
    //and later when we change the version, other classes will have the old value! 
    public static final int VERSION =1; 

} 

class BetterFoo{ 
    private static int version =1; 

    //outside classes must call method getVersion() 
    //field version can not be inlined at compile time. 
    //JIT may inline at runtime which is OK 
    public static final int getVersion(){ 
     return version; 
    } 

} 

4.關於上述問題的答案築底:我們能以某種方式編譯類依賴於其他類的不知道他們在編譯時確切的實現,並提供依賴只在運行時?

是,代碼接口。

接口應該包含所有你需要調用的方法。我們的A類應該只能通過接口引用調用者。如果我們要調用的對象實例被傳入,那麼類A不知道(或者需要知道)實際類型是什麼,並且在編譯時不需要知道它。

這是主要目的和Dependency Injection

優勢之一甚至除了依賴注入,編碼到接口方面具有優勢。例如,如果我們使用Map而不是HashMap,稍後我們可以通過更改代碼中的一個位置來更改代碼以使用不同的Map實現,例如ConcurrentHashMap。我們其餘的代碼將會工作,因爲它只知道它是一個Map。

+0

很好的回答,謝謝! – MiamiBeach 2014-10-17 08:34:29

2

這由Java語言規範的Chapter 13. Binary Compatibility來解決。

由於這可能比你想更具體的,我會給出一個簡要總結:

如果類A使用B類的功能fA類文件必須包含f一個象徵性的參考。當加載A類時,但B不提供f,加載類A失敗,出現java.lang.LinkageError,從而阻止使用A

編譯器時間常量表達式是一個例外,編譯器必須內聯,即符號引用必須在編譯時由它們的值替換,而類A將繼續使用原始值,即使類B已更改。

另一個例外是添加或刪除註釋對程序的二進制表示的正確鏈接沒有影響。

B沒有其他信息被併入A在編譯的時候,一切有關B可以改變,而不由已編譯A類影響其使用。