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