Java虛擬機(jī)的字節(jié)碼指令集的數(shù)目從面世以來饼丘,只在JDK的發(fā)布的時(shí)候新增過一條(invokedynamic指令)。這條新增的指令是JDK7的項(xiàng)目目標(biāo):實(shí)現(xiàn)動(dòng)態(tài)類型語言(Dynamically Typed Language)支持而進(jìn)行的改進(jìn)之一辽话,也是為JDK8里可以順利實(shí)現(xiàn)Lambda表達(dá)式而做的技術(shù)儲備肄鸽。
動(dòng)態(tài)類型語言
動(dòng)態(tài)類型語言的關(guān)鍵特征是它的類型檢查的主體過程是在運(yùn)行期而不是編譯期進(jìn)行的,滿足這個(gè)特征的語言有很多油啤,常用的包括:APL典徘、Clojure、Erlang益咬、Groovy逮诲、JavaScript、Liso幽告、Lua梅鹦、PHP、Prolog冗锁、Ruby齐唆、Smalltalk、Tcl等等冻河。相對的箍邮,在編譯期就進(jìn)行類型檢查過程的語言,如C++和Java等就是常用的靜態(tài)類型語言叨叙。
通過兩個(gè)例子來說明什么是“類型檢查”和是什么叫“在編譯期還是在運(yùn)行期進(jìn)行”锭弊?
public static void main(String[] args){
int[][][] array = new int[1][0][-1];
}
上面這段Java代碼能正常編譯,但運(yùn)行時(shí)會出現(xiàn)NegativeArraySizeException異常擂错。在《Java虛擬機(jī)規(guī)范》中明確規(guī)定了NegativeArraySizeException是運(yùn)行時(shí)異常(Runtime Exception)味滞,即運(yùn)行時(shí)異常指只要代碼不執(zhí)行到這一行就不會出現(xiàn)問題。與運(yùn)行時(shí)異常相對應(yīng)的概念就是連接時(shí)異常钮呀,如常見的NoClassDefFoundError桃犬,即導(dǎo)致連接時(shí)異常的代碼放在一條根本無法被執(zhí)行的路徑分支上,類加載時(shí)也照樣會拋出異常行楞。
但在C語言里攒暇,語義相同的代碼就會在編譯期就直接報(bào)錯(cuò),而不會等到運(yùn)行時(shí):
int main(void){
int i[1][0][-1];//GCC拒絕編譯子房,報(bào)“size of array is negative”
return 0形用;
}
由此可看出就轧,一門語言的哪一種檢查行為要在運(yùn)行期進(jìn)行,哪一種檢查要在編譯期進(jìn)行并沒有什么必然的因果邏輯關(guān)系田度,關(guān)鍵是在語言規(guī)范中認(rèn)為設(shè)定的約定妒御。
obj.printIn("hello world");
先假設(shè)這行代碼是在Java語言中,且變量obj的靜態(tài)類型為java.io.PrintStream.那變量obj的實(shí)際類型就必須是PrintStream的子類(實(shí)現(xiàn)了PrintStream接口的類)才是合法的镇饺。否則就算obj屬于一個(gè)確實(shí)包含了printIn(String)方法相同簽名的類型乎莉,但只要它與PrintStream接口沒有繼承關(guān)系,代碼依然不可能運(yùn)行——因?yàn)轭愋蜋z查不合法奸笤。
但相同的代碼在ECMAScript(JavaScript)中情況不一樣惋啃,無論obj具體是何種類型,無論其繼承關(guān)系如何监右,只要這種類型的方法定義中確實(shí)包含有printIn(String)方法边灭,能夠找到相同簽名的方法,調(diào)用便可成功健盒。
產(chǎn)生這種差別的主要原因是Java語言在編譯期就已將printIn(String)方法完整的符號引用(本例為一項(xiàng)CONSTANT_Methodref_info常量)生成出來绒瘦,并作為方法調(diào)用指令的參數(shù)存儲到Class文件中,如下:
invokevirtual #4;//Method java/io/PrintStream.printIn:(Ljava/lang/String;)v
這個(gè)符號引用包含了該方法定義在哪個(gè)具體類型之中扣癣、方法的名字以及參數(shù)順序惰帽、參數(shù)類型和方法返回值等信息,通過這個(gè)符號引用父虑,Java虛擬機(jī)就可以翻譯出該方法的直接引用该酗。而ECMAScript等動(dòng)態(tài)型語言與Java有一個(gè)核心的差異就是變量obj本身并沒有類型,變量obj的值才有類型频轿,所以編譯器在編譯時(shí)最多只能確定方法名稱、參數(shù)烁焙、返回值這些信息航邢,而不會去確定方法所在的具體類型(即方法接收者不固定)〗居“變量無類型而變量值才有類型”這個(gè)特點(diǎn)也是動(dòng)態(tài)語言的一個(gè)核心特征
靜態(tài)類型語言能夠在編譯期確定變量類型膳殷,最顯著的好處是編譯器可以提供全面嚴(yán)謹(jǐn)?shù)念愋蜋z查,這樣與數(shù)據(jù)類型相關(guān)的潛在問題就能在編碼時(shí)被及時(shí)發(fā)現(xiàn)九火,利于穩(wěn)定性及讓項(xiàng)目容易達(dá)到更大的規(guī)模赚窃。而動(dòng)態(tài)類型語言在運(yùn)行期才確定類型,這可為開發(fā)人員提供極大的靈活性岔激,某些在靜態(tài)類型語言中要花大量臃腫代碼來實(shí)現(xiàn)的功能勒极,由動(dòng)態(tài)類型語言去做可能會很清晰簡潔,也就意味著開發(fā)效率的提高虑鼎。
《深入理解Java虛擬機(jī)》第三版 學(xué)習(xí)