JAVA之编译期和运行期区别
編譯期:檢查是否有語法錯誤,如果沒有就將其翻譯成字節(jié)碼文件。即.class文件。
運(yùn)行期:java虛擬機(jī)分配內(nèi)存,解釋執(zhí)行字節(jié)碼文件。
從以下代碼開始說明,歡迎大家指正
可以思考下,第一行跟第二行在編譯時期有什么區(qū)別?
java編譯時會做一些優(yōu)化操作。第一行,因?yàn)槭莾蓚€常量做運(yùn)算,那么他們的結(jié)果就是確定的,即num1的值是確定的。所以在編譯時,編譯器就會直接算出num1的值。第二行則不會,java在運(yùn)行時期才為變量分配內(nèi)存空間。
所以Eclipse編譯得到.class文件,打開class反編譯后可以得到如下代碼:
PS:使用Android Studio 編譯器結(jié)果有點(diǎn)不一樣,但不影響說明。
1、方法重載是在編譯時執(zhí)行的。因?yàn)樵诰幾g的時候,如果調(diào)用了一個重載的方法,那么編譯時必須確定他調(diào)用的方法是哪個。如:當(dāng)調(diào)用evaluate(“hello”)時候,我們在編譯時就可以確定他調(diào)用的method #1.
2、方法的重寫是在運(yùn)行時進(jìn)行的。這個也常被稱為運(yùn)行時多態(tài)的體現(xiàn)。編譯器是沒有辦法知道它調(diào)用的到底是那個方法,相反的,只有在jvm執(zhí)行過程中,才知曉到底是父子類中的哪個方法被調(diào)用了。如下:
試想,當(dāng)有如下一個接口的時候,我們是無法確定到底是調(diào)用父類還是子類的方法
3、泛型(類型檢測),這個發(fā)生在編譯時。
這也正是泛型的好處之一,可以提前暴露問題,而不是等到運(yùn)行時出現(xiàn)ClassCastException。編譯器會在編譯時對泛型類型進(jìn)行檢測,并把它重寫成實(shí)際的對象類型(非泛型代碼),這樣就可以被JVM執(zhí)行了。這個過程被稱為"類型擦除"。
類型擦除的關(guān)鍵在于從泛型類型中清除類型參數(shù)的相關(guān)信息,并且再必要的時候添加類型檢查和類型轉(zhuǎn)換的方法。
類型擦除可以簡單的理解為將泛型java代碼轉(zhuǎn)換為普通java代碼,只不過編譯器更直接點(diǎn),將泛型java代碼直接轉(zhuǎn)換成普通java字節(jié)碼。類型擦除的主要過程如下:
1). 將所有的泛型參數(shù)用其最左邊界(最頂級的父類型)類型替換。
2). 移除所有的類型參數(shù)。
4. 注解。注解即有可能是運(yùn)行時也有可能是編譯時。
如java中的@Override注解就是典型的編譯時注解,他會在編譯時會檢查一些簡單的如拼寫的錯誤(與父類方法不相同)等
同樣的@Test注解是junit框架的注解,他是一個運(yùn)行時注解,他可以在運(yùn)行時動態(tài)的配置相關(guān)信息如timeout等。
5. 異常。異常即有可能是運(yùn)行時異常,也有可能是編譯時異常。
RuntimeException是一個用于指示編譯器不需要檢查的異常。RuntimeException 是在jvm運(yùn)行過程中拋出異常的父類。對于運(yùn)行時異常是不需要再方法中顯示的捕獲或者處理的,如NullPointerException,ArrayIndexOutOfBoundsException
已檢查的異常是被編譯器在編譯時候已經(jīng)檢查過的異常,這些異常需要在try/catch塊中處理的異常。
6. AOP. Aspects能夠在編譯時,預(yù)編譯時以及運(yùn)行時使用。
1). 編譯時:當(dāng)你擁有源碼的時候,AOP編譯器(AspectJ編譯器)能夠編譯源碼并生成編織后的class。這些編織進(jìn)入的額外功能是在編譯時放進(jìn)去的。
2). 預(yù)編譯時:織入過程有時候也叫二進(jìn)制織入,它是用來織入到哪些已經(jīng)存在的class文件或者jar中的。
3). 運(yùn)行時:當(dāng)被織入的對象已經(jīng)被加載如jvm中后,可以動態(tài)的織入到這些類中一些信息。
7、繼承:繼承是編譯時執(zhí)行的,它是靜態(tài)的。這個過程編譯后就已經(jīng)確定
8、代理(delegate):也稱動態(tài)代理,是在運(yùn)行時執(zhí)行。
如何理解"組合優(yōu)于繼承"這句話?
繼承是一個多態(tài)的工具,而非重用工具。在沒有多態(tài)關(guān)聯(lián)關(guān)系的對象間,一些程序員傾向于使用繼承來保持重用。但事實(shí)是,只有當(dāng)子類和父類的關(guān)系為"is a"的關(guān)系時候,繼承才會使用。
這也就是為什么GoF設(shè)計(jì)模式中常說"組合優(yōu)于繼承"的原因。
你能區(qū)分編譯時繼承和運(yùn)行時繼承的區(qū)別嗎?請列舉例子說明
實(shí)際上在java中只支持編譯時繼承。java語言原生是不支持運(yùn)行時時繼承的。一般情況下所謂編譯時繼承如下:
如上有兩個類,其中Child為Parent的子類。當(dāng)我們創(chuàng)建一個Parent實(shí)例的時候(無論實(shí)際對象為Parent還是Child),編譯器在編譯期間會將其替換成實(shí)際類型。所以繼承實(shí)際上在編譯時就已經(jīng)確定了。
而在java中,可以設(shè)計(jì)通過組合模式來嘗試模擬下所謂的運(yùn)行時繼承。
在Child類中,其中有一個Parent實(shí)例。通過這種方式,我們動態(tài)的child類中代理了parent的相關(guān)功能。
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎總結(jié)
以上是生活随笔為你收集整理的JAVA之编译期和运行期区别的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: rust(47)-key-value哈希
- 下一篇: rust(49)-图像(2)