最近各大互聯網公司線上筆試,編程題目里的編譯器只支持C/C++、Java,甚至有的支持javaScrpit和Pascal,就是不支持Python。讓一直以來用慣了Python的我直吐血,於是今天痛定思痛還是熟悉一下Java,免得繼續被虐。學習的過程中,看到這樣一個爭論“Java、Python誰是編譯型語言,誰是解釋性語言?”。我在網上查了很多資料,也結合了自己的理解,下面與大家分享一下。
總的來說,如今編譯型語言、解釋性語言的分界線不再那么明顯,應該避免把語言簡單歸類為“編譯型”和“解釋型”。
我們最開始說C/C++是編譯型語言,原因是相對於Ruby這樣的解釋性語言,C/C++需要通過編譯器,把源代碼編譯成中間文件(.o和.obj),然后通過連接器和匯編器生成機器碼,即一系列基本操作序列,可以直接讓計算機執行。這些機器碼也就是我們通常的exe文件。
我們以Ruby為例談一下解釋型的語言,程序從源代碼到被計算機執行,也要經歷上述步驟。不同的地方在於,C/C++會把那些從源代碼“變”來的機器碼(即exe文件)保存起來,而Ruby直接將這些生成的基本操作序列(Ruby虛擬機)指令丟給Ruby虛擬機執行然后產生動作了。這就是我們所說的解釋型語言。
所以我們看到的現象是,編譯型語言要先編譯再運行,而解釋性語言直接“運行”源代碼。
回到最開始的問題,如果以是“否保存機器碼為exe文件”為區分,那么Java和Python都屬於解釋型語言。
但具體來講,Java和Python是有很大不同的。Java代碼從源程序到執行,要經過的過程是:編譯器(javac)把源代碼轉化為字節碼,然后解釋器(Java.exe)把字節碼轉換為計算機理解的機器碼來執行,其過程中沒有把“機器碼保存為exe”的行為(這樣講也不完全准確,下面會講到)。其中編譯器和解釋器都是Java虛擬機(JVM)的一部分,由於針對不同的硬件與OS,Java解釋器有所不同,因此可以實現“一次編譯、到處執行”。所以JVM是Java跨平台特性的關鍵所在。
Java虛擬機(JVM)一種用於計算機設備的規范,可用不同的方式(軟件或硬件)加以實現。編譯虛擬機的指令集與編譯微處理器的指令集非常類似。Java虛擬機包括一套字節碼指令集、一組寄存器、一個棧、一個垃圾回收堆和一個存儲方法域
對於Python,其源代碼到執行也要經過如下過程:源代碼--->字節碼--->機器碼。跟Java相同的是,其過程中也沒有把“機器碼保存為exe”的行為。與Java不同的是,Python使用的虛擬機是基於其他語言實現的,比如我們一般使用的Python實際為Cpython,也就是其虛擬機由C實現,這個虛擬機負責把Python源碼編譯為字節碼,再解釋執行。另外,還有Jypython、Ironpython等。
那為什么要這樣實現?
因為CPython中很容易為你的Python代碼寫C擴展,因為最終都是由C解釋器執行的。另一方面,Jython則使得和其他java程序共同工作很容易:無需其他工作,你就可導入任何Java類,在你的Jython程序中使用其他Java類。
通過以上分析,顯然,每種語言都有自己的優缺點。
另外需要說明的是Python是種強類型的語言。有人可能會問,在python中,可以這樣寫而不報錯,所以是弱類型的:
i=1 print(i) i="hello world" print(i)
顯然,這樣的同學搞錯了python的實現。實際上述代碼中,i僅僅是一個指向,每次賦值,實際上只是改變了i的指向。
實際上,這里python表現出的特性為,他是一種動態類型語言。動態類型語言是一種在運行期間才去確定數據類型的語言,與靜態類型相反。VBScript 和 Python 是動態類型的,因為它們確定一個變量的類型是在您第一次給它賦值的時候。靜態類型語言是一種在編譯期間就確定數據類型的語言。大多數靜態類型語言是通過要求在使用任一變量之前聲明其數據類型來保證這一點的。Java 和 C 是靜態類型語言。
Python 是強制類型定義的。指的是加入我們有一個整數,如果不明確地進行轉換 ,不能將把它當成一個字符串,所以顯然,C/C++和Java都是強類型語言。 弱類型語言與強類型相反。VBScript 是弱類型的。在 VBScript 中,我們可以將字符串 '12' 和整數 3 進行連接得到字符串'123',然后可以把它看成整數 123 ,所有這些都不需要任何的顯示轉換。
總結起來,Python是一種動態的,強類型語言。
現在回到“關於解釋和編譯的界限也不是特別清晰了”的問題。
- Java需要預先把代碼編譯成虛擬機指令的,然后在運行這些虛擬機指令,有的教科書上會成為混合型或者半編譯型。
- 像Python和lua這樣就更不好分了,可以直接解釋源代碼運行,也可以編譯為虛擬機指令然后再運行。
- php編譯之后的結果可以被Web Server緩存起來,甚至還可以先被翻譯為C++,然后再編譯。
- .NET 的CLR運行時是Windows的組成部分,編譯好的.NET 系列語言的代碼直接生成可執行文件,然后被“直接”執行,看起來跟C沒有什么太大的差別。
- JavaScript可以被V8引擎編譯為機器碼然后執行,如果在node.js下,這個編譯結果被緩存起來了,你說這跟編譯好再執行的C有什么區別
我們再回到”Java編譯、執行過程中沒有把機器碼保存為exe的行為“的話題。
有些答案對JAVA的理解還停留在上古時代或者教科書里。
其實,現在用編譯型、解釋型來分類編程語言已經有點力不從心了。
JAVA的第一道工序是javac編譯,當然目標文件是BYTECODE。后續可能有三種處理方式:
1. 運行時,BYTECODE由JVM逐條解釋執行,
2. 運行時,部分代碼可能由JIT翻譯為目標機器指令(以method為翻譯單位,還會保存起來,第二次執行就不用翻譯了)直接執行;
3. RTSJ。繼JAVAC之后執行AOT二次編譯,生成靜態的目標平台代碼(典型的就是IBM WEBSHPERE REAL TIME)。
有的時候,可能是以上三種方式同時在使用。至少,1和2是同時使用的,3需要程序員手工指定。顯然,生成了靜態的目標平台代碼,按照之前的定義,就屬於編譯型語言了。
所以討論語言得更細化一點了,強類型的、弱類型的,靜態的、動態的,GC-based的、手工管理內存的,有沒有VM..
參考鏈接:
1、程序的編譯與解釋有什么區別? http://www.zhihu.com/question/21486706
2、為什么有這么多python? http://www.oschina.net/translate/why-are-there-so-many-pythons
3、Java是編譯型語言還是解釋性語言? http://www.zhihu.com/question/19608553
4、CSDN討論,Python是強類型語言 http://bbs.csdn.net/topics/310260114
