沒了IDE,你的Java專案還能Run起來嗎~
阿新 • • 發佈:2020-06-10
![](https://upload-images.jianshu.io/upload_images/10998555-214679387e36a110.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
計算機只能識別機器碼0101...程式語言->能執行的機器碼 需要經過 `預處理->編譯->彙編->連結->機器碼`過程。一個語言處理系統的示意圖如下:
![](https://upload-images.jianshu.io/upload_images/10998555-e37dc257f5edca47.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
**編譯器** 是將**源語言程式一次性翻譯**成一個等價的,用目標語言編寫的程式。還存在另一種常見的語言處理器,**直譯器**:它是**逐個語句的執行源語言程式**。由一個編譯器產生的目標語言程式通常比一個直譯器快,但直譯器的錯誤診斷效果通常更好。
Java語言處理器結合了編譯和解釋的過程。一個`.Java`源程式首先被編譯為`.class`位元組碼檔案,被載入到虛擬機器中,然後由虛擬機器將位元組碼翻譯成機器碼。
![](https://upload-images.jianshu.io/upload_images/10998555-5f1b34104fedb27e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
**虛擬機器的好處**在於:一旦一個程式被轉換成 Java 位元組碼,那麼它便可以在不同平臺上的虛擬機器實現裡執行。實現**一次編寫,到處執行**。另外一個好處是它帶來了一個**託管環境**。這個託管環境能夠代替我們處理一些程式碼中冗長而且容易出錯的部分,如自動記憶體管理與垃圾回收。
在Hotspot中,**虛擬機器翻譯位元組碼**有兩種方式:
**1.解釋執行**
即逐條將位元組碼翻譯成機器碼並執行。
**2.即時編譯**
即將一個方法中包含的所有位元組碼編譯成機器碼後再執行。
前者的優勢在於無需等待編譯,而後者的優勢在於實際執行速度更快。HotSpot 預設採用混合模式,綜合瞭解釋執行和即時編譯兩者的優點。它會先解釋執行位元組碼,而後將其中反覆執行的熱點程式碼,以方法為單位進行即時編譯。
即時編譯建立在程式符合二八定律的假設上,也就是百分之二十的程式碼佔據了百分之八十的計算資源。
好了,裝X結束。
![](https://upload-images.jianshu.io/upload_images/10998555-7c0f8141b1e5a2fa.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
阿姨知道的編譯知識全在上面了。。(っ╥╯﹏╰╥c)
如題,下面我們來看一下讓Java專案執行起來我們能做什麼。
我們能做的很簡單,當然不是寫虛擬機器。我們只需要:
1.執行`command javac`,將.Java檔案變為.class檔案。
2.執行`command java`,讓.class檔案執行起來。
也就是 執行command :)
### Java程式的執行方式
Java程式可以通過java命令**執行.class檔案**或**執行可執行Jar檔案**。
我們先看第一種方式:從Hello World開始。
##### 執行.class檔案
`Step1:編寫Java檔案`
![](https://upload-images.jianshu.io/upload_images/10998555-45ff15257e056d59.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
`Step2:執行 command javac`
將.Java檔案變為.class檔案
![](https://upload-images.jianshu.io/upload_images/10998555-8137b11d45867f01.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
> 小貼士:class檔案的全路徑名是包名目錄+ 類檔名。
`Step3:執行 command java`
執行.class檔案
![](https://upload-images.jianshu.io/upload_images/10998555-fadeed307bad9a12.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
神奇,我們沒有用IDE讓Java程式執行起來了 :)
小夥伴先別噴老阿姨,哪特麼有這麼簡單的Java專案啊。。我們工作中用的明明都是Jar檔案啊...
Jar檔案咋執行啊!!
![](https://upload-images.jianshu.io/upload_images/10998555-02fca7cd5e9577ab.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
##### 執行可執行Jar檔案
**Jar檔案**是基於ZIP檔案格式的一種檔案格式,它將大量的Java類檔案、相關的元資料和資源(文字、圖片等)檔案聚合到一個Jar檔案中,此外還包含一個可選的**META-INF資料夾**。這個資料夾下的檔案或資料夾主要用來打包和擴充套件配置資訊,包括安全,版本,擴充套件程式和服務等。如**MANIFEST.MF**檔案定義了擴充套件和打包的相關資料資訊。
一個Jar檔案通常在專案中用作第三方類庫使用,也是專案構建的一部分。
**生成一個Jar檔案大致分為兩步:**
1.將原始檔編譯為.class檔案
2.通過 `command jar`命令將.class檔案,資原始檔等等打成一個檔案格式的Jar檔案。
我們以一個SbDemo專案為例來看Jar檔案的打包和執行。專案目錄結構如下:
![](https://upload-images.jianshu.io/upload_images/10998555-32177a1b766862b2.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
Test2.java中呼叫了Test1.java的方法,
![](https://upload-images.jianshu.io/upload_images/10998555-9bf59e063a36cf95.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
我們需要先**將Test1.java編譯並打成一個Test1.jar檔案,然後通過Test1.jar將Test2.java編譯並打成一個可執行的Test2.jar檔案**。
可執行和不可執行的Jar檔案 區別在於是否在Jar檔案中指定了main方法的入口,我們後面再看。
`Step1:Test1.java的編譯`
![](https://upload-images.jianshu.io/upload_images/10998555-8712779e4e540afc.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
`Step2:將編譯後的classes/com/Test1.class檔案打成一個Test1.jar包`
Java中和jar包相關的命令是`jar命令`,生成一個jar包我們需要定義**資訊檔案(manifest-file)**,它可以定義所生成jar包的classpath類搜尋路徑,jar包的入口類等等。可以理解為**與Jar包相關的元資料配置資訊**。
`Step2.1 書寫資訊檔案`
這裡我們使用**resources/manifest-test1.text檔案**作為資訊檔案
![](https://upload-images.jianshu.io/upload_images/10998555-0de1f975f4d47827.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
是的,Test1.java太簡單了,就是打成一個可被他人引用的jar包,資訊檔案不重要。
`Step2.2 執行打包命令`
![](https://upload-images.jianshu.io/upload_images/10998555-b743af5b43ccf432.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
`Step3. 編譯Test2.java檔案`
因為Test2.java中引用了com.Test1類,所以我們需要在編譯時指定Classpath路徑。
**Classpath**:顧名思義,是指待編譯類依賴的類所在路徑位置。我們可以通過 javac 的 -cp 引數指定。
關於編譯時classpath的值優先順序如下:
* 如果沒有傳入classpath引數,將使用環境變數CLASSPATH的值。(小夥伴不知道環境變數咋檢視和設定?去看阿姨的上一篇文章:)
* 如果沒有發現環境變數CLASSPATH,將使用 執行命令的當前資料夾(.)。
* 如果javac命令列 通過`-classpath or -cp`引數指定了類路徑值,則優先順序最高。
這裡我們使用-cp指定Test1.jar所在位置
![](https://upload-images.jianshu.io/upload_images/10998555-51e1c529f765d3af.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
可以看到classes目錄下已經生成了com2/Test2.class檔案了。
`Step4. 將編譯後的Test2.class和它依賴的Test1.jar一起打成一個可執行的Jar包`
`Step4.1 書寫資訊檔案`
這時候我們使用資訊檔案resources/manifest-test2.text檔案指定這些資訊
![](https://upload-images.jianshu.io/upload_images/10998555-361e0147f03a18af.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
`Step4.2 執行Jar包生成命令`
![](https://upload-images.jianshu.io/upload_images/10998555-68d62a2a8c7ff2bc.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
可以看到在lib目錄下生成了Test2.jar
`Step5.執行我們的可執行Jar`
![](https://upload-images.jianshu.io/upload_images/10998555-0d114df4dca7f75d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
大功告成了,我們的SbDemo專案Run起來了...
當然實際專案不可能人肉編譯,打包。我們需要通過Maven/Gradle等構建工具,幫助我們管理程式碼之間的Jar包依賴,構建,部署...我們可能大多時候通過點一下IDE就託管了Maven的構建部署命令。
拿Maven舉例子,Maven首先定義了一套專案結構,我們按照它的結構書寫程式碼,引入各個模組所需要的Jar包依賴。然後Maven可以通過自己的生命週期管理專案的清理,構建,打包,部署階段。每個階段有對應的Maven外掛執行相應的目標。IDE又整合了Maven,使我們通過點吧點吧按鈕就完成了專案的執行。
但是當一個專案並沒有按照規範的構建工具結構搭建,或者專案沒有成功執行報錯時,瞭解Java實際的編譯執行過程會對理解、解決這類問題有所幫助。
好啦,限於篇幅,阿姨先不講這些年Maven躺過的坑了,有想看的嗎?`關注,在看,轉發`三連回應下 >-<
![](https://upload-images.jianshu.io/upload_images/10998555-1d83439345ac6b6d.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
---
參考資料:
[1].《編譯原理》序 (゚´ω`゚)゚
[2].[https://time.geekbang.org/column/article/11289](https://time.geekbang.org/column/article/11289)