1. 程式人生 > >「MoreThanJava」機器指令到彙編再到高階程式語言

「MoreThanJava」機器指令到彙編再到高階程式語言

![](https://imgkr.cn-bj.ufileos.com/78659a7b-d278-4f03-89e8-ba435511df94.png) - **「MoreThanJava」** 宣揚的是 **「學習,不止 CODE」**,本系列 Java 基礎教程是自己在結合各方面的知識之後,對 Java 基礎的一個總回顧,旨在 **「幫助新朋友快速高質量的學習」**。 - 當然 **不論新老朋友** 我相信您都可以 **從中獲益**。如果覺得 **「不錯」** 的朋友,歡迎 **「關注 + 留言 + 分享」**,文末有完整的獲取連結,您的支援是我前進的最大的動力! # Part 1. 機器指令 ![](https://imgkr.cn-bj.ufileos.com/a0fed95e-1b0c-4f80-8706-c4876b9c8c84.png) [上一次](https://www.wmyskxz.com/2020/06/02/morethanjava-yi-wen-liao-jie-er-jin-zhi-he-cpu-gong-zuo-yuan-li/) 我們已經瞭解了 **二進位制和 CPU 的基本原理**,知道了程式執行時,CPU 每秒數以億次、十億次、百億次地震盪著時鐘,同步執行著微小的 **「電子操作」**,例如:從記憶體讀取一個位元組的資料到 CPU 又或者判斷位元組中的某一位是 `0` 還是 `1`。 **CPU** 本身有一組 **規定好的** 可以執行的 **「基本動作」**(被稱為 **機器指令**): 1. 讀取指令;2. 執行指令;3. 寫暫存器; **這幾乎就是 CPU 工作的全部了。** 這些動作雖然每次只能執行一次,但是每秒可以執行數十億次,這個數量級的「小操作」累加成為一個大的「有用的操作」。 處理器所做的一切都是基於這些微小的操作!幸運的是,我們已經不再需要了解這些操作的詳細資訊就可以編寫和使用各類程式。諸如 **Java** 這一類的 **「高階語言」** 的 **目的** 就是 **將這些微小的電子操作組織成由人類可讀的「程式語言」表示的大型有用單元**。 ## 機器指令演示 一條 **機器指令** 一般由記憶體中的幾個位元組組成,它們告訴 CPU 應該執行一個什麼樣的 **「機器操作」**(是取資料還是寫暫存器等..)。處理器依次檢視 CPU 中的機器指令,並執行每一條。記憶體中的一組機器指令被稱為 **「機器語言程式」**,或稱為 **「可執行程式」**。 下面我們來使用機器語言來演示一個控制燈泡亮度的機器語言程式。 ### 先和硬體做好規定 假設燈泡由記憶體中的某一個程式控制,該程式能夠完全開啟和關閉燈泡,可以使燈泡變亮或變暗,機器指令一個位元組長度,並且與機器操作對應如下: 機器指令 | 機器操作 :-- | :-- 00000000 | 停止程式 00000001 | 完全開啟燈泡 00000010 | 完全關閉燈泡 00000100 | 燈泡暗淡 10% 00001000 | 將燈泡照亮 10% 00010000 | 如果燈泡完全點亮,則跳過下一條說明 00100000 | 如果燈泡完全熄滅,請跳過下一條說明 01000000 | 轉到程式的開始(地址 0) ### Demo 程式 && 演示 根據上方作出的規定,我們寫下如下的程式:(為了方便理解,我把對應的機器操作也寫在了後面,實際的程式只包含機器指令) 地址 | 機器指令 | 機器操作 :-- | :-- | :-- 0 | 00000001 | 完全開啟燈泡 1 | 00000010 | 完全關閉燈泡 2 | 00000001 | 完全開啟燈泡 3 | 00000100 | 燈泡暗淡10% 4 | 00000100 | 燈泡暗淡10% 5 | 00000000 | 停止程式 所以這樣的一段程式執行效果就如下圖: ![](https://imgkr.cn-bj.ufileos.com/48e82cdb-42e2-40b5-bff7-3732c9ffba42.gif) 您可以嘗試自己利用 `01000000`(跳轉到程式開始) 來改寫程式來達到讓「燈逐漸變亮又逐漸變暗」的目的。 ### 小結 上面演示的程式 **核心思想** 是: - 機器語言程式是記憶體中一系列機器指令的集合; - 機器指令由一個或多個位元組組成(在此示例中,僅一個位元組); - 處理器一次執行一條機器指令的程式; - 所有的小機器操作加起來都是有用的; 在實際的 CPU 中,擁有更多的機器指令,而且更詳細,並且不同的 CPU,指令集是不同的。典型的 CPU 擁有一千或更多的機器指令。 # Part 2. 組合語言 ![](https://imgkr.cn-bj.ufileos.com/d50d8b9a-e175-4e63-86a6-906b5ba6d208.png) - 圖片來源:http://www.ruanyifeng.com/blog/2018/01/assembly-language-primer.html ## 機器語言太 "反人類” 我們已經可以開始寫一些程式使用了,但是使用 **機器語言編寫程式碼會十分辛苦**,比如: ```text 00000001 00000010 00000001 00000100 00000100 00000000 ``` 即使你剛看過你也會對這一段就在 *上方的例項程式碼* 沒有什麼感知,這是因為機器語言是設計給機器的,人類記憶和使用起來就會顯得十分麻煩。 如此你就會感知到 **上個世紀** 的程式設計師使用 **打孔卡片**: ![](https://imgkr.cn-bj.ufileos.com/b9cb03fb-e41a-4b4b-b864-e09fccf073ff.png) 使用 **紙帶**: ![](https://imgkr.cn-bj.ufileos.com/62dd4e15-b604-478d-86fe-c7738e7160ac.png) 甚至是 **直接插拔線路 or 按下開關**: ![](https://imgkr.cn-bj.ufileos.com/182823be-2916-4c1d-bf2b-280d30882fdb.png) 是一件多麼硬核的事情... ![](https://imgkr.cn-bj.ufileos.com/5b98d856-bc1f-4d8b-9040-f25502881fb3.jpg) > 如果你對它們如何工作以及多麼硬核感興趣,可以參考一下下方的連結: > - 開發語言小傳之一:最早的程式語言——機器語言 - https://blog.csdn.net/killer080414/article/details/42219091 > - 50年前的登月程式和程式設計師有多硬核 - https://coolshell.cn/articles/19612.html、 > > 再附帶一個寶藏網站(哥倫比亞大學出版的計算機歷史,非常詳細),有條件的同學 **非常推薦** 進去瀏覽一下: > - http://www.columbia.edu/cu/computinghistory/index.html ## 組合語言誕生 CPU 的指令都是 **二進位制** 的,這顯然對於人類來說是 **不可讀** 的。為了解決二進位制指令的可讀性問題,工程師將那些指令寫成了 **八進位制**。二進位制轉八進位制是輕而易舉的,但是八進位制的可讀性也不行。 很自然地,最後還是用文字表達,加法指令寫成 `ADD`。記憶體地址也不再直接引用,而是 **用標籤** 表示。 這樣的話,就多出一個步驟,**要把這些文字指令翻譯成二進位制**,這個步驟就稱為 `assembling`,完成這個步驟的程式就叫做 `assembler`。它處理的文字,自然就叫做 `aseembly code`。標準化以後,稱為 `assembly language`,縮寫為 `asm`,中文譯為 **組合語言**。 ![](https://imgkr.cn-bj.ufileos.com/391ab75e-84a6-4428-979e-91a3e7b2aee8.png) - 圖片來源:http://www.ruanyifeng.com/blog/2018/01/assembly-language-primer.html ## 理解組合語言 每一種 CPU 的機器指令都是不一樣的,因此對應的組合語言也不一樣。本文介紹的是目前最常見的 `x86` 組合語言,即 `Intel` 公司的 CPU 使用的那一種。 ### 暫存器 要學習組合語言,首先必須瞭解兩個知識點:**暫存器** 和 **記憶體模型**。 先來看暫存器。**CPU 本身只負責運算,不負責儲存資料**。資料一般都儲存在記憶體之中,CPU 要用的時候就去記憶體讀寫資料。但是,CPU 的運算速度遠高於記憶體的讀寫速度,**為了避免被拖慢**,CPU 都自帶一級快取和二級快取。基本上,CPU 快取可以看作是讀寫速度較快的記憶體。 但是,CPU 快取還是不夠快,另外資料在快取裡面的地址是不固定的,CPU 每次讀寫都要定址也會拖慢速度。因此,除了快取之外,CPU 還自帶了暫存器(register),用來儲存最常用的資料。也就是說,那些最頻繁讀寫的資料(比如迴圈變數),都會放在暫存器裡面,CPU 優先讀寫暫存器,再由暫存器跟記憶體交換資料。 ![](https://imgkr.cn-bj.ufileos.com/39bf5a1d-c600-4f68-902e-ea0b24c306a7.png) 暫存器不依靠地址區分資料,而依靠名稱。每一個暫存器都有自己的名稱,我們告訴 CPU 去具體的哪一個暫存器拿資料,這樣的速度是最快的。有人比喻暫存器是 CPU 的零級快取。 #### 暫存器的種類 早期的 `x86` CPU 只有 `8` 個暫存器,而且每個都有不同的用途。現在的暫存器已經有 `100` 多個了,都變成通用暫存器,不特別指定用途了,但是早期暫存器的名字都被儲存了下來。 >- EAX >- EBX >- ECX >- EDX >- EDI >- ESI >- EBP >- ESP 上面這 `8` 個暫存器之中,前面七個都是通用的。ESP 暫存器有特定用途,儲存當前 Stack 的地址(詳見下一節)。 ![](https://imgkr.cn-bj.ufileos.com/5c360f32-b78c-461f-a099-f1dbbffb120e.png) 我們常常看到 `32` 位 CPU、`64` 位 CPU 這樣的名稱,其實指的就是暫存器的大小。`32` 位 CPU 的暫存器大小就是 `4` 個位元組。 ### 記憶體模型:Heap(堆) 暫存器只能存放很少量的資料,大多數時候,CPU 要指揮暫存器,直接跟記憶體交換資料。所以,除了暫存器,還必須瞭解記憶體怎麼儲存資料。 程式執行的時候,作業系統會給它分配一段記憶體,用來儲存程式和執行產生的資料。這段記憶體有起始地址和結束地址,比如從 `0x1000` 到 `0x8000`,起始地址是較小的那個地址,結束地址是較大的那個地址。 ![](https://imgkr.cn-bj.ufileos.com/42782853-29a0-4532-883c-438504800706.png) 程式執行過程中,對於動態的記憶體佔用請求(比如新建物件,或者使用 `malloc` 命令),系統就會從預先分配好的那段記憶體之中,劃出一部分給使用者,具體規則是從起始地址開始劃分(實際上,起始地址會有一段靜態資料,這裡忽略)。舉例來說,使用者要求得到 `10` 個位元組記憶體,那麼從起始地址 `0x1000` 開始給他分配,一直分配到地址 `0x100A`,如果再要求得到 `22` 個位元組,那麼就分配到 `0x1020`。 ![](https://imgkr.cn-bj.ufileos.com/2943274f-12e0-40b2-90cf-4efe990a0f29.png) 這種因為使用者主動請求而劃分出來的記憶體區域,叫做 **Heap(堆)**。它由起始地址開始,從低位(地址)向高位(地址)增長。Heap 的一個重要特點就是不會自動消失,必須手動釋放,或者由垃圾回收機制來回收。 ### 記憶體模型:Stack(棧) 除了 Heap 以外,其他的記憶體佔用叫做 **Stack(棧)**。簡單說,Stack 是由於 **函式執行** 而 **臨時佔用** 的記憶體區域。 ![](https://imgkr.cn-bj.ufileos.com/2087853e-fc81-44f5-95d1-39339cbc98ea.png) 例如我們在執行一個叫 `main` 的函式時,會為它在記憶體裡面建立一個 **幀**,用來儲存所有 `main` 中使用的內部變數。`main` 函式執行結束後,該幀就會被回收,釋放所有的內部變數,不再佔用空間。 ![](https://imgkr.cn-bj.ufileos.com/dc27d86d-f77b-4d9c-814a-4cdf6d88ec2a.png) 如果在 `main` 函式 **內部呼叫了其他函式**,例如 `add_a_and_b` 函式,那麼執行到這一行的時候,系統也會為 `add_a_and_b` 新建一個幀,用來儲存它的內部變數。也就是說,此時同時存在兩個幀:`main` 和 `add_a_and_b`。一般來說,呼叫棧有多少層,就有多少幀。 ![](https://imgkr.cn-bj.ufileos.com/6cc0655c-0169-4801-aa81-42a02b99cbb9.png) 等到 `add_a_and_b` 執行結束,它的幀就會被回收,系統會回到函式 `main` 剛才中斷執行的地方,繼續往下執行。通過這種機制,就實現了函式的 **層層呼叫**,並且 **每一層都能使用自己的本地變數**。 我們可以把棧理解為一個下方密封,而上方開啟的「桶」。 ![](https://imgkr.cn-bj.ufileos.com/6b064f00-a750-464e-a66c-4611be7ebc25.png) 生成的新幀放入我們稱之為 **「入棧」**,而釋放幀我們稱之為 **「出棧」**。**棧的特點** 就是,**最晚入棧的幀最早出棧**(因為最內層的函式呼叫,最先結束執行),這就叫做 **"後進先出"** 的資料結構。每一次函式執行結束,就自動釋放一個幀,所有函式執行結束,整個棧就都釋放了。 ## 組合語言演示 舉個簡單的例子,我們需要計算: ```text (1 + 4) * 2 + 3 ``` 我們按照 **「字尾表示法」** 進行一下轉換: ```text 1,4,+,2,*,3,+ ``` 我們平常使用的方法是 **「中綴表示法」**,也就是把計算符號放中間,例如 `1 + 3`,字尾則是把符號放最後,例如 `1, 3, +`。 這樣做的好處是沒有先乘除後加減的影響,也沒有括號,直接運算就行了。*(例如 `1, 3, +`,先把 `1` 和 `3` 儲存起來碰到 `+` 知道是加法則直接相加)* OK,我們從頭開始使用匯編語言來編寫一下程式,首先第一步:把 `1` 儲存起來(放入暫存器): ```arm MOV 1 ``` 之後是 `4, +`,那就直接加一下: ```arm ADD 4 ``` 然後是 `2, *`,那就直接乘一下(`SHL` 是向左移動一位的意思,二進位制中左移一個單位就相當於乘以 `2`,例如 `01` 表示 `1`,而 `10` 則表示 `2`): ```arm SHL 0 ``` 最後是 `3, +`,再加一下: ```arm ADD 3 ``` 完整程式如下: ```arm MOV 1 ADD 4 SHL 0 ADD 3 ``` 這似乎看起來比 `00001111` 這樣的二進位制要好上太多了!程式設計師們感動到落淚: ![](https://imgkr.cn-bj.ufileos.com/8598396f-d110-4e3b-88f3-e6dafd6df4aa.png) # Part 3. 高階程式語言 ![](https://imgkr.cn-bj.ufileos.com/464d9574-7184-495b-bee3-3e1010c0baa9.png) 擺脫了 **二進位制**,我們有了更可讀的 **組合語言**,但仍然十分繁瑣和複雜,每一條彙編指令代表一個基本操作,例如:「從記憶體 x 位置獲取一個數字並放入暫存器 A」、「將暫存器 A 中的數字新增到暫存器 B 的數字上」。**這樣的程式設計風格既費時又容易出錯,並且一旦出錯還很難發現。** 例如,我們來看一看 **「1969 年阿波羅 11號登月計劃」** 用來 **防止登月艙計算機耗盡自身資源** 的 BAILOUT 程式碼: ```arm POODOO INHINT CA Q TS ALMCADR TC BANKCALL CADR VAC5STOR # STORE ERASABLES FOR DEBUGGING PURPOSES. INDEX ALMCADR CAF 0 ABORT2 TC BORTENT OCT77770 OCT 77770 # DONT MOVE CA V37FLBIT # IS AVERAGE G ON MASK FLAGWRD7 CCS A TC WHIMPER -1 # YES. DONT DO POODOO. DO BAILOUT. TC DOWNFLAG ADRES STATEFLG TC DOWNFLAG ADRES REINTFLG TC DOWNFLAG ADRES NODOFLAG TC BANKCALL CADR MR.KLEAN TC WHIMPER ``` - 出處:改變世界的程式碼行 - https://www.infoq.cn/article/5CaYH8NbS6BmptWKRgkX 似乎不太容易讀的樣子... > 阿波羅登月計劃的原始碼在 Github 上已經公開,有興趣的可以去下方連結膜拜一下(可以去感受一下當時程式設計師的工程能力): > - https://github.com/chrislgarry/Apollo-11 > > 另外附一下當時程式碼的設計負責人 Margaret Heafield Hamilton(女程式設計師)和完成的堆起來跟人一樣高的程式碼量: > ![](https://imgkr.cn-bj.ufileos.com/4cfa57e6-72bc-4979-b956-225679bdd11a.png) ## 第一個高階語言:FORTRAN 當 **John Backus** 在 `1950` 年以一名科學程式設計師的身份加入 IBM 時,已經可以使用諸如 `ADD` 之類的助記詞代替數字程式碼來編寫程式,也就是我們的組合語言。這使程式設計變得容易一些,但是即使是一個簡單的程式也需要數十次操作,並且仍然很難找到錯誤。 巴克斯認為,應該有可能建立一種程式語言,使一系列計算可以用類似於數學符號的形式來表達。然後,使用特定的翻譯程式(以今天的術語來說是編譯器)可以將其轉換為計算機可以理解的數字程式碼。 Backus 在 `1953` 年向他的經理提出了這個想法。他得到了預算,並被鼓勵僱用一個小團隊來測試該想法的可行性。三年後,該團隊釋出了一本手冊,其中描述了 IBM Mathematical Formula Translating System(簡稱 FORTRAN)。不久之後, IBM 向 IBM 704 的使用者提供了第一個 FORTRAN 編譯器。 ![FORTRAN 之父](https://imgkr.cn-bj.ufileos.com/d5a45166-a05d-48ae-a8c8-4e47277ae5c0.png) Backus 和他的團隊創造了世界上第一種高階程式語言。**科學家和工程師將不再需要將其程式編寫為數字程式碼或冗長的助記符**。 ### FORTRAN 程式碼演示 下面演示計算並輸出 `8 * 6` 的程式碼例項: ```fortran program VF0944 implicit none integer a, b, c a= 8 b= 6 c= a*b print *, 'Hello World, a, b, c= ', a, b, c end program VF0944 ``` *對比彙編程式碼,是不是看上去要清晰(人類可讀)多了呢?* ### FORTRAN 的意義 FORTRAN 的問世在計算機史上具有劃時代的意義,它使計算機語言從原始的低階組合語言走出來,進入了更高的境界,使得 **計算機語言不再是計算機專家的專利**,使廣大的工程技術人員有了進行計算機程式設計的手段。 由此計算機更快地深入到了社會之中,它在工業部門中初露頭角,更是在火箭、導彈、人造地球衛星的設計中大顯身手,因此有人稱 FORTRAN 語言使計算機的工業應用成了可能,是推動第二次世界大戰以後西方工業經濟復甦和進入第二次工業革命的無形力量,是 "看不見的蒸汽機"。 ## FORTRAN 後時代 FORTRAN 高階程式設計語言的出現孕育了計算機軟體業,繼其之後,計算機高階程式語言的開發進入到了一個蓬勃發展的時代。 ### **1959** Grace Hopper 發明了第一個面向企業業務的程式語言,又稱 “面向商業的通用語言”,也常常簡稱 COBOL。 ### **1964** 美國達特茅斯學院約翰·凱梅尼和托馬斯·卡茨認為,像 FORTRAN 那樣的語言太過專業,程式設計非常困難。於是他們簡化了 FORTRAN,並設計出了更適合初學者的 BASIC 語言。 ### **1970** 尼古拉斯·沃斯非常痴迷於程式語言,他率先提出了結構化程式設計思想併發明瞭 Pascal 語言。 此外他還提出了 Wirth 定律,意為 “軟體變慢的速度比硬體變快的速度更快”,這讓摩爾定律變得充滿諷刺。之後的 Electron.js 也確實證明了這一點。 ### **1972** 丹尼斯·裡奇在貝爾實驗室工作期間發明了 C 語言,開啟了現代程式語言的革命。之後,他又添加了段錯誤和其他一些幫助開發人員的實用功能,大大提升了程式設計效率。 除了 C 語言之外, 他和貝爾實驗室的同事還創造了偉大的 Unix 作業系統。 ### **1980** Alan Kay 發明了面向物件的程式語言 Smalltalk,在 Smalltalk 中,一切皆物件。 ### **1987** 拉里·沃爾發明了 Perl 語言。 ### **1983** Jean Ichbiah 發現 Ada Lovelace 的程式從未執行成功過,因此決定用她的名字建立一種語言,於是 Ada 語言誕生了。 ### **1986** Brac Box 和 Tol Move 通過融合 C 語言和 Smalltalk 的特徵,發明了 Objective-C。但由於其語法晦澀,不太容易理解。 ### **1983** Bjarne Stroustrup 在 C 語言的基礎上引入並擴充了面向物件的概念,發明了—種新的程式語言並將其命名為 C++。 C++ 大大提升了應用程式的程式設計效率。 ### **1991** Guido van Rossum 討厭帶有大括號的程式語言,於是他參考 Monty Python 和 Flying Circus 語法,併發明瞭 Python。 ### **1993** Roberto Ierusalimschy 和其朋友創造了一門巴西本地的指令碼語言。在本地化過程中,由於一個小的錯誤使得索引從1開始,而不是0。這門語言就是 Lua。 ### **1994** Rasmus Lerdorf 為他個人主頁的 CGI 指令碼製作了一個模板引擎,用來統計他自己網站的訪問量。 這個檔案被上傳到網上之後用它的人越來越多。後來又用 C 語言重新編寫,還添加了資料庫訪問功能。這門語言就是 PHP。 ### **1995** 松本行弘發明了 Ruby 語言。 ### **1995** Brendan Eich 利用週末時間設計了一種語言,用於為世界各地的網頁瀏覽器提供支援,並最終推出了 Skynet。他最初去了 Netscape,並將這門語言命名為 LiveScript,後來在程式碼審查期間 Java 逐漸開始風靡,因此他們決定將其改名為 JavaScript。 後來 Java 使其陷入了商標麻煩,於是 JavaScript 被更名為 ECMAScript。但是人們還是習慣稱之為 JavaScript。 ### **1996** James Gosling 發明了 **Java**,這是 **第一個真正意義上面向物件得程式語言**,其中設計模式在實用主義中佔統治地位。 ### **More...** > 對於這一段計算機歷史感興趣的同學可以拜讀一下「IT 通史 12.2 節 - 高階計算機程式設計語言」的內容,線上預覽連結如下: > - https://books.google.com.hk/books?id=ZrAol3RzcNkC&printsec=frontcover&hl=zh-CN#v=onepage&q&f=false ## 高階語言分類 CPU 終究只認識二進位制指令,在我們發明高階語言之後,仍然無可避免的需要進行 **「翻譯」** 工作。按照翻譯方式的不同,我們又把高階語言分為了 **「編譯型」** 和 **「解釋型」**。 ### 編譯型 ![](https://imgkr.cn-bj.ufileos.com/a6cc59bb-d39a-4997-84ba-2dca04b29121.png) **編譯型專業解釋為:** 使用 **專門的編譯器**,針對 **特定的平臺**,將高階語言原始碼 **一次性** 的編譯成可被該平臺硬體執行的機器碼,幷包裝成該平臺所能識別的可執行性程式的格式,並且只需要編譯一次,以後再也不用編譯。其實可以簡單理解成谷歌/ 百度翻譯,我們把要翻譯的文字全部放進去,一次翻譯,下次使用直接使用上一次翻譯好的結果。 ![](https://imgkr.cn-bj.ufileos.com/ffca82c8-6fd6-406c-9570-c0af169b31f7.png) - **優點(較解釋型)**:執行效率高(有直譯器省去很多翻譯的過程) - **缺點(較解釋型)**:開發效率低(寫完所有的程式碼才能檢查 `bug`,得多恐怖呀???) ### 解釋型 ![](https://imgkr.cn-bj.ufileos.com/232c4f68-d364-4c1b-af2c-02ce74abc2dc.png) **解釋型專業解釋為:** 使用 **專門的直譯器** 對源程式逐行解釋成 **特定平臺** 的機器碼並 **立即執行**,它不需要事先編譯,直接將程式碼解釋稱機器碼直接執行,也就是說只要某一平臺提供了相應的直譯器即可執行程式碼。其實可以理解成同聲傳譯,我們需要翻譯的時候,找一個翻譯員,對方說一句翻譯員翻譯一句,下次翻譯還是需要一個翻譯員一句一句的翻譯。 ![](https://imgkr.cn-bj.ufileos.com/aca34a36-2390-49b9-88a7-676ff3ff4ba7.png) - **缺點(較編譯型)**:執行效率低(寫一次翻譯一次) - **優點(較編譯型)**:開發效率高(寫一行翻譯一行,錯了馬上就知道,媽媽再也不用擔心我找不到 bug 了) ### 半解釋半編譯的 Java 不同廠商、不同時間開發的 CPU 的指令集是不一樣的,這就是上方為什麼提到要使用 **專門的直譯器**,要用於 **特定的平臺** 的原因。 所以 Java 為了實現 **「一次編譯,到處執行」** 的目的,採用了一種特別的方案:先 **編譯** 為 **與任何具體及其環境及作業系統環境無關的中間程式碼**(也就是 `.class` 位元組碼檔案),然後交由各個平臺特定的 Java 直譯器(也就是 JVM)來負責 **解釋** 執行。 程式設計人員和計算機都無法直接讀懂位元組碼檔案,它必須由專用的 Java 直譯器來解釋執行,因此 Java 是一種在 **編譯基礎上進行解釋執行** 的語言。(Java 程式執行流程如下) ![](https://imgkr.cn-bj.ufileos.com/d5b30e41-3c06-412b-b04d-c9cc54e4885a.png) **Java 直譯器** 負責將位元組碼檔案翻譯成具體硬體環境和作業系統平臺下的機器程式碼,以便執行。因此 Java 程式不能直接執行在現有的作業系統平臺上,它必須執行在被稱為 Java 虛擬機器的軟體平臺之上。 **Java 虛擬機器(JVM)** 是執行 Java 程式的軟體環境(我們後面會詳細說到,這是學習 Java 繞不過的題),Java 直譯器是 Java 虛擬機器的一部分。在執行 Java 程式時,首先會啟動 JVM,然後由它來負責解釋執行 Java 的位元組碼程式,並且 Java 位元組碼程式只能運行於 JVM 之上。這樣利用 JVM 就可以把 Java 位元組碼程式和具體的硬體平臺以及作業系統環境分隔開來,只要在不同的計算機上安裝了針對特定平臺的 JVM,Java 程式就可以執行,而不用考慮當前具體的硬體平臺及作業系統環境,也不用考慮位元組碼檔案是在何種平臺上生成的。 JVM 把這種不同軟、硬體平臺的具體差別隱藏起來,從而 **實現了真正的二進位制程式碼級的跨平臺移植**。JVM 是 Java 平臺架構的基礎,Java 的跨平臺特性正是通過在 JVM 中執行 Java 程式實現的。Java 的這種執行機制可以通過下圖來說明: ![](https://imgkr.cn-bj.ufileos.com/f0b29fd0-5ffe-41fa-8a5d-27046b9c7689.png) Java 語言這種「一次編寫,到處執行」的方式,有效地解決了目前大多數高階程式設計語言需要針對不同系統來編譯產生不同機器程式碼的問題,即硬體環境和操作平臺的異構問題,大大降低了程式開發、維護和管理的開銷。 - **提示:** Java 程式通過 JVM 可以實現跨平臺特性,但 JVM 是不跨平臺的。也就是說,**不同作業系統之上的 JVM 是不同的**,Windows 平臺之上的 JVM 不能用在 Linux 平臺,反之亦然。 # 參考資料 1. Introduction to Computer Science using Java | CHAPTER 4 - http://programmedlessons.org/Java9/chap04/ch04_01.html 1. 組合語言入門教程 - http://www.ruanyifeng.com/blog/2018/01/assembly-language-primer.html 1. CPU 是怎麼認識程式碼的? | 知乎@Zign - https://www.zhihu.com/question/348237008/answer/843382847 1. 改變世界的程式碼行 - https://www.infoq.cn/article/5CaYH8NbS6BmptWKRgkX 1. The History of FORTRAN - https://www.obliquity.com/computer/fortran/history.html 1. 《IT 通史》 | @李彥 1. A Brief Totally Accurate History Of Programming Languages - https://medium.com/commitlog/a-brief-totally-accurate-history-of-programming-languages-cd93ec806124 1. 程式語言分類 - https://www.cnblogs.com/nickchen121/p/10722720.html # 往期精彩 1. [「MoreThanJava」當大學選擇了計算機之後應該知道的](https://www.wmyskxz.com/2020/04/02/morethanjava-dang-da-xue-xuan-ze-liao-ji-suan-ji-zhi-hou-ying-gai-zhi-dao-de/) 1. [「MoreThanJava」計算機發展史—從織布機到IBM](https://www.wmyskxz.com/2020/04/07/morethanjava-ji-suan-ji-fa-zhan-shi-cong-zhi-bu-ji-dao-ibm/) 1. [「MoreThanJava」計算機系統概述](https://www.wmyskxz.com/2020/06/01/morethanjava-ji-suan-ji-xi-tong-gai-shu/) 1. [「MoreThanJava」一文了解二進位制和CPU工作原理](https://www.wmyskxz.com/2020/06/02/morethanjava-yi-wen-liao-jie-er-jin-zhi-he-cpu-gong-zuo-yuan-li/) > - 本文已收錄至我的 Github 程式設計師成長系列 **【More Than Java】,學習,不止 Code,歡迎 star:[https://github.com/wmyskxz/MoreThanJava](https://github.com/wmyskxz/MoreThanJava)** > - **個人公眾號** :wmyskxz,**個人獨立域名部落格**:wmyskxz.com,堅持原創輸出,下方掃碼關注,2020,與您共同成長! ![](https://imgkr.cn-bj.ufileos.com/ace97ed9-3cfd-425f-85e5-c1a1e5ca7d3f.png) 非常感謝各位人才能 **看到這裡**,如果覺得本篇文章寫得不錯,覺得 **「我沒有三顆心臟」有點東西** 的話,**求點贊,求關注,求分享,求留言!** 創作不易,各位的支援和認可,就是我創作的最大動力,我們下篇文