JAVA之編譯期和執行期區別

lvxiangan發表於2018-10-10

編譯期:檢查是否有語法錯誤,如果沒有就將其翻譯成位元組碼檔案。即.class檔案。
執行期:java虛擬機器分配記憶體,解釋執行位元組碼檔案。

 

從以下程式碼開始說明,歡迎大家指正

可以思考下,第一行跟第二行在編譯時期有什麼區別?

java編譯時會做一些優化操作。第一行,因為是兩個常量做運算,那麼他們的結果就是確定的,即num1的值是確定的。所以在編譯時,編譯器就會直接算出num1的值。第二行則不會,java在執行時期才為變數分配記憶體空間。

所以Eclipse編譯得到.class檔案,開啟class反編譯後可以得到如下程式碼:

 

PS:使用Android Studio 編譯器結果有點不一樣,但不影響說明。

 

1、方法過載是在編譯時執行的。因為在編譯的時候,如果呼叫了一個過載的方法,那麼編譯時必須確定他呼叫的方法是哪個。如:當呼叫evaluate("hello")時候,我們在編譯時就可以確定他呼叫的method #1.

2、方法的重寫是在執行時進行的。這個也常被稱為執行時多型的體現。編譯器是沒有辦法知道它呼叫的到底是那個方法,相反的,只有在jvm執行過程中,才知曉到底是父子類中的哪個方法被呼叫了。如下:

試想,當有如下一個介面的時候,我們是無法確定到底是呼叫父類還是子類的方法

3、泛型(型別檢測),這個發生在編譯時。
這也正是泛型的好處之一,可以提前暴露問題,而不是等到執行時出現ClassCastException。編譯器會在編譯時對泛型型別進行檢測,並把它重寫成實際的物件型別(非泛型程式碼),這樣就可以被JVM執行了。這個過程被稱為"型別擦除"。

型別擦除的關鍵在於從泛型型別中清除型別引數的相關資訊,並且再必要的時候新增型別檢查和型別轉換的方法。
型別擦除可以簡單的理解為將泛型java程式碼轉換為普通java程式碼,只不過編譯器更直接點,將泛型java程式碼直接轉換成普通java位元組碼。型別擦除的主要過程如下:
    1). 將所有的泛型引數用其最左邊界(最頂級的父型別)型別替換。
    2). 移除所有的型別引數。
 

4. 註解。註解即有可能是執行時也有可能是編譯時。
如java中的@Override註解就是典型的編譯時註解,他會在編譯時會檢查一些簡單的如拼寫的錯誤(與父類方法不相同)等
同樣的@Test註解是junit框架的註解,他是一個執行時註解,他可以在執行時動態的配置相關資訊如timeout等。

5. 異常。異常即有可能是執行時異常,也有可能是編譯時異常。
RuntimeException是一個用於指示編譯器不需要檢查的異常。RuntimeException 是在jvm執行過程中丟擲異常的父類。對於執行時異常是不需要再方法中顯示的捕獲或者處理的,如NullPointerException,ArrayIndexOutOfBoundsException
已檢查的異常是被編譯器在編譯時候已經檢查過的異常,這些異常需要在try/catch塊中處理的異常。

6. AOP. Aspects能夠在編譯時,預編譯時以及執行時使用。
    1). 編譯時:當你擁有原始碼的時候,AOP編譯器(AspectJ編譯器)能夠編譯原始碼並生成編織後的class。這些編織進入的額外功能是在編譯時放進去的。
     2). 預編譯時:織入過程有時候也叫二進位制織入,它是用來織入到哪些已經存在的class檔案或者jar中的。
     3). 執行時:當被織入的物件已經被載入如jvm中後,可以動態的織入到這些類中一些資訊。

7、繼承:繼承是編譯時執行的,它是靜態的。這個過程編譯後就已經確定

8、代理(delegate):也稱動態代理,是在執行時執行。


如何理解"組合優於繼承"這句話?
繼承是一個多型的工具,而非重用工具。在沒有多型關聯關係的物件間,一些程式設計師傾向於使用繼承來保持重用。但事實是,只有當子類和父類的關係為"is a"的關係時候,繼承才會使用。

      1. 不要使用繼承來實現程式碼的重用。如果兩者之間沒有"is a"的關係,那麼使用組合來實現重用。當父類的某個方法修改後,子類的相關實現也有可能會被更改。
      2. 不要為了多型而使用繼承。如果你只是為了實現多型而採用繼承模式,那麼實際上組合模式更加適合你,而且更加簡潔和靈活。
這也就是為什麼GoF設計模式中常說"組合優於繼承"的原因。

 

你能區分編譯時繼承和執行時繼承的區別嗎?請列舉例子說明
實際上在java中只支援編譯時繼承。java語言原生是不支援執行時時繼承的。一般情況下所謂編譯時繼承如下:
如上有兩個類,其中Child為Parent的子類。當我們建立一個Parent例項的時候(無論實際物件為Parent還是Child),編譯器在編譯期間會將其替換成實際型別。所以繼承實際上在編譯時就已經確定了。

而在java中,可以設計通過組合模式來嘗試模擬下所謂的執行時繼承。

在Child類中,其中有一個Parent例項。通過這種方式,我們動態的child類中代理了parent的相關功能。

相關文章