javac命令的使用和運(yùn)作原理

王二北原創(chuàng)次哈,轉(zhuǎn)載請標(biāo)明出處:來自王二北

一胎署、簡單嘮嘮JAVAC

javac這個(gè)命令,搞java的都不陌生窑滞,很多人在第一次用java輸出hello world時(shí)琼牧,都接觸過這個(gè)命令,它的作用簡單來說哀卫,就是將指定的java源代碼編譯成class文件巨坊,后來我們有了Eclipse等IDE工具,就很少關(guān)心這個(gè)命令了此改,偶爾需要的時(shí)候趾撵,才想起來javac xx.java一下,其實(shí)我也一樣共啃。我也認(rèn)為javac命令很簡單占调,不就是編譯個(gè)class文件嗎暂题。但是隨著對jvm的了解,我才覺得妈候,jdk中提供的很多命令都是很有意思的敢靡。javac這個(gè)命令也遠(yuǎn)沒有我想想的那么簡單。

比如苦银,我在介紹javap命令時(shí)啸胧,如果對直接使用javac編譯的class文件,進(jìn)行javap xxx查看其反匯編信息時(shí)幔虏,看不到方法的局部變量表纺念、代碼行與指令行的偏移映射表等信息。再比如想括,jdk8之前陷谱,并沒有提供通過反射獲得方法中入?yún)⒌膮?shù)名的api(如何方法入?yún)?shù)名,可以參考我的文章)等等瑟蜈。

接下來烟逊,我就根據(jù)我查到的資料和感悟來簡單的說說javac命令。

二铺根、JAVAC命令的工作過程

我們都知道javac命令的作用是將java源碼編譯成二進(jìn)制字節(jié)碼class文件宪躯,那么從java源文件編譯成class文件這個(gè)過程中JAVAC命令都進(jìn)行了什么操作呢,或者說JAVAC命令的工作過程是什么樣的呢位迂?
首先访雪,來看看編譯原理(上學(xué)時(shí)學(xué)過,現(xiàn)在基本都還給老師了掂林,網(wǎng)上查了查資料)中編譯過程主要經(jīng)歷以下階段:


編譯過程臣缀,圖片來自網(wǎng)絡(luò)

javac命令在進(jìn)行編譯操作時(shí),也會按照類似的過程進(jìn)行:

(1)詞法分析階段

詞法分析泻帮,就是將獲得的java源代碼信息轉(zhuǎn)化為標(biāo)記(Token)集合精置,比如關(guān)鍵字、變量刑顺、運(yùn)算符等等氯窍,都是一個(gè)個(gè)的標(biāo)記。詞法分析的過程就是將這些標(biāo)記解析出來蹲堂。

(2)語法分析階段

語法分析是在詞法分析得到的標(biāo)記集合的基礎(chǔ)上狼讨,抽象出對應(yīng)的語法樹。什么是語法樹呢柒竞?簡單的來說政供,一個(gè)java源文件中包信息,import信息、類定義信息布隔、方法信息离陶、字段信息等待作為一個(gè)個(gè)的項(xiàng),這些項(xiàng)集合在一起就抽象為一棵語法樹衅檀。
為了更直觀的解釋什么是語法樹招刨,這里做個(gè)測試,首先在eclipse中創(chuàng)建一個(gè)Test1.java文件哀军,在這個(gè)java文件中添加兩個(gè)類Test1和Test2沉眶,內(nèi)容如下:

package com.test.map;
import java.util.Date;

public class Test1 {
   private String name;
   public static final int age = 20;
    
   public void test(String username){
       this.name = username;
       System.out.println(new Date());
   }
    
   public void test2(int a){
       try {
           System.out.println(a/0);
       } catch (Exception e) {
           throw new RuntimeException(e);
       }
   }
}

class Test2{
   public void tst(){
        
   }
}

然后使用EClipse的AST插件(AST插件安裝)分析當(dāng)前java源碼的語法樹,內(nèi)容如下杉适,一目了然谎倔,很清晰,包含了這個(gè)java文件package信息猿推、import引入的依賴信息片习,定義的類,類的字段和方法等等信息:


(3)符號表填充階段

符號表(SymbolTable)是由一組符號地址和符號信息構(gòu)成的表格蹬叭,可以把它想象成哈希表中K-V值對的形式(實(shí)際上符號表不一定是哈希表實(shí)現(xiàn)藕咏,可以是有序符號表、樹狀符號表秽五、棧結(jié)構(gòu)符號表等)侈离。符號表中所登記的信息在編譯的不同階段都要用到。在語義分析中筝蚕,符號表所登記的內(nèi)容將用于語義檢查(如檢類型是否匹配等)和產(chǎn)生中間代碼。在目標(biāo)代碼生成階段铺坞,當(dāng)對符號名進(jìn)行地址分配時(shí)起宽,符號表是地址分配的依據(jù)。

(4)注解處理階段

在jdk1.5之后济榨,java提供了對注解的支持坯沪,注解可以在編譯、類加載擒滑、運(yùn)行時(shí)被讀取腐晾,并執(zhí)行相應(yīng)的處理。通過使用注解丐一,開發(fā)人員可以在不改變原有邏輯的情況下藻糖,在源文件中嵌入一些補(bǔ)充信息。如果有需要在編譯期間被處理的注解库车,則這些注解將會在當(dāng)前階段進(jìn)行讀取和處理巨柒。

(5)語義分析

在語法分析之后,編譯器獲得了程序源碼的抽象語法樹,語法樹能表示一個(gè)結(jié)構(gòu)正確的源程序的抽象洋满,但無法保證源程序是符合邏輯的晶乔。而語義分析的主要任務(wù)是對結(jié)構(gòu)上正確的源程序進(jìn)行上下文有關(guān)性質(zhì)的審查,如進(jìn)行類型審查牺勾。舉個(gè)例子正罢,假設(shè)有如下的3個(gè)變量定義語句:

int a = 1;
boolean b = false;
char c = 3;

后續(xù)的代碼中可能出現(xiàn)操作賦值運(yùn)行:

int d = a+c;
int e = b+c;
char f = a+b;

上面的代碼中,它們都能構(gòu)成結(jié)構(gòu)正確的語法樹驻民,但是只有第1種的寫法在語義上是沒有問題的翻具,能夠通過編譯,其余兩種在Java語言中是不合邏輯的川无,無法編譯(在java中int類型可以和char呛占、short、byte類型進(jìn)行加減等操作懦趋,但不能和boolean進(jìn)行相關(guān)操作)晾虑。
javac的編譯過程中,語義分析過程分為標(biāo)注檢查、數(shù)據(jù)及控制流分析、解除語法糖3個(gè)步驟醉蚁。

1件已、標(biāo)注檢查

標(biāo)注檢查,主要包括諸如變量使用前是否已被聲明石景、變量與賦值之間的數(shù)據(jù)類型是否能夠匹配等。在標(biāo)注檢查步驟中,還有一個(gè)重要的動作稱為常量折疊竟痰,如果我們在代碼中寫了如下定義:

int a = 1+2;

那么在語法樹上仍然能看到字面量“1”、“2”以及操作符“+”掏呼,但是在經(jīng)過語義分析階段的常量折疊之后坏快,它們將會被折疊為字面常量“3”。

2憎夷、數(shù)據(jù)及控制流分析

數(shù)據(jù)及控制流分析是對程序上下文邏輯更進(jìn)一步的驗(yàn)證莽鸿,它可以檢查出諸如程序局部變量在使用前是否有賦值、方法的每條路徑是否都有返回值拾给、是否所有的受查異常都被正確處理了等問題祥得。編譯時(shí)期的數(shù)據(jù)及控制流分析與類加載時(shí)的數(shù)據(jù)及控制流分析的目的基本上是一致的,但校驗(yàn)范圍有所區(qū)別蒋得,有一些校驗(yàn)項(xiàng)只有在編譯期或運(yùn)行期才能進(jìn)行级及。

3、解除語法糖

語法糖窄锅,簡單的來說创千,就是在開發(fā)語言中添加某些語法缰雇,這些語法對開發(fā)人員是非常友好和有用的,主要用來使用的開發(fā)語義更易用追驴、開發(fā)人員開發(fā)出的代碼有更好的可讀性械哟、減少程序代碼出錯率等。但是這些語法對開發(fā)語言的功能和性能并沒有太大的影響殿雪。
舉個(gè)例子暇咆,java中基本類型和其對應(yīng)引用類型直接的拆箱裝箱操作,在我們的代碼中我們可以這樣寫:

int a = 1;
Integer b = a+2;

實(shí)際上丙曙,在編譯之后,通過gui等class反編譯工具爸业,可以看到:

int a = 1;
Integer b = Integer.valueOf(a+2);

在java中,還提供了for(i:xx)循環(huán)遍歷亏镰、支持string的switch case扯旷、泛型、變長參數(shù)等等語法糖索抓。關(guān)于java中的語法糖钧忽,后續(xù)會出一篇博客,專門講述逼肯。
解除語法糖階段耸黑,就是將我們代碼中java提供的語法糖解析還原為java原本的基礎(chǔ)語法結(jié)構(gòu)。因?yàn)樵谶\(yùn)行期間篮幢,jvm是不支持這些語法糖對應(yīng)的語法的大刊。

(6)字節(jié)碼生成階段

字節(jié)碼生成階段,會將前面生成的語法樹三椿、符號表等信息轉(zhuǎn)化為字節(jié)碼輸出到磁盤中缺菌,并且會進(jìn)行相關(guān)的代碼添加和轉(zhuǎn)換工作。
比如搜锰,當(dāng)我們使用javap查看反匯編代碼時(shí)男翰,會看到通過new創(chuàng)建對象時(shí),實(shí)際上是調(diào)用了這個(gè)對象的<init>方法纽乱,完成對象的初始化,這個(gè)<init>方法就是在字節(jié)碼生成階段添加的昆箕,它會將我們在代碼中寫的普通語句塊鸦列、成員變量初始化、調(diào)用父類構(gòu)造器等等操作都放入到<init>方法中鹏倘,完成對象的初始化操作薯嗤。
再比如,多個(gè)字符串變量相加a+b+c纤泵,實(shí)際上是創(chuàng)建了一個(gè)StringBuilder對象骆姐,對這些字符串變量進(jìn)行append()操作镜粤,這些通過javap都能看到。
關(guān)于javap的使用玻褪,可以參考我的博客《通過javap命令分析java匯編指令》肉渴。

以上,就是javac命令工作的過程带射,下面講述一下javac命令的使用同规。

三、JAVAC命令的使用

一般安裝好jdk后窟社,我們會在控制臺執(zhí)行javac命令券勺,以驗(yàn)證jdk是否安裝成功,比如灿里,我在我本地執(zhí)行javac关炼,會輸出一下內(nèi)容:

用法: javac <options> <source files>
其中, 可能的選項(xiàng)包括:
  -g                         生成所有調(diào)試信息
  -g:none                    不生成任何調(diào)試信息
  -g:{lines,vars,source}     只生成某些調(diào)試信息
  -nowarn                    不生成任何警告
  -verbose                   輸出有關(guān)編譯器正在執(zhí)行的操作的消息
  -deprecation               輸出使用已過時(shí)的 API 的源位置
  -classpath <路徑>            指定查找用戶類文件和注釋處理程序的位置
  -cp <路徑>                   指定查找用戶類文件和注釋處理程序的位置
  -sourcepath <路徑>           指定查找輸入源文件的位置
  -bootclasspath <路徑>        覆蓋引導(dǎo)類文件的位置
  -extdirs <目錄>              覆蓋所安裝擴(kuò)展的位置
  -endorseddirs <目錄>         覆蓋簽名的標(biāo)準(zhǔn)路徑的位置
  -proc:{none,only}          控制是否執(zhí)行注釋處理和/或編譯。
  -processor <class1>[,<class2>,<class3>...] 要運(yùn)行的注釋處理程序的名稱; 繞過默認(rèn)的搜索進(jìn)程
  -processorpath <路徑>        指定查找注釋處理程序的位置
  -d <目錄>                    指定放置生成的類文件的位置
  -s <目錄>                    指定放置生成的源文件的位置
  -implicit:{none,class}     指定是否為隱式引用文件生成類文件
  -encoding <編碼>             指定源文件使用的字符編碼
  -source <發(fā)行版>              提供與指定發(fā)行版的源兼容性
  -target <發(fā)行版>              生成特定 VM 版本的類文件
  -version                   版本信息
  -help                      輸出標(biāo)準(zhǔn)選項(xiàng)的提要
  -A關(guān)鍵字[=值]                  傳遞給注釋處理程序的選項(xiàng)
  -X                         輸出非標(biāo)準(zhǔn)選項(xiàng)的提要
  -J<標(biāo)記>                     直接將 <標(biāo)記> 傳遞給運(yùn)行時(shí)系統(tǒng)
  -Werror                    出現(xiàn)警告時(shí)終止編譯
  @<文件名>                     從文件讀取選項(xiàng)和文件名

直接執(zhí)行javac命令匣吊,就等同執(zhí)行javac -help儒拂,它會輸出javac這個(gè)命令的使用規(guī)則,以及可以使用的相關(guān)參數(shù)及參數(shù)的簡介缀去。

3.1侣灶、JAVAC命令的使用格式

在前面執(zhí)行javac輸出的信息中,我們看到其輸出的用法為:

javac <options> <source files>

其中:

  • options表示我們在使用javac命令時(shí)需要指定的參數(shù)選項(xiàng)缕碎,可以同時(shí)指定多個(gè)參數(shù)褥影,每個(gè)參數(shù)之間使用空格隔開。參數(shù)選項(xiàng)在javac的使用說明中都列出來了咏雌,下一小節(jié)凡怎,會著重講解其中的幾個(gè)參數(shù)。

  • source files表示我們要編譯的java源碼文件赊抖⊥车梗可以是多個(gè)文件,使用空格隔開氛雪。并且房匆,這些文件文件名都必須以.java結(jié)尾。

比如:

javac -nowarn,-verbose Test.java Test2.java

另外报亩,javac后面的參數(shù)浴鸿、文件等信息并沒有固定的順序,你可以按照自己的意愿隨便指定各個(gè)參數(shù)和文件信息的位置順序弦追,比如:

javac -nowarn Test1.java -verbose

還有一點(diǎn)需要注意:

在前面javac輸出的信息中岳链,最后一項(xiàng):

@<文件名> 從文件讀取選項(xiàng)和文件名

意思是,你可以將參數(shù)選項(xiàng)劲件,文件信息單獨(dú)放到一個(gè)或多個(gè)文件中掸哑,然后執(zhí)行:
javac @xxx文件就可以在執(zhí)行javac命令時(shí)约急,將xxx文件中的內(nèi)容傳遞給javac命令。

例如苗分,有兩個(gè)java文件Test1.java厌蔽,Test2.java。然后新建一個(gè)classmsgs.txt文件俭嘁,文件內(nèi)容為:

Test1.java Test2.java -verbose

然后執(zhí)行下面的命令:

javac -nowarn @classmsg.txt

如果有多個(gè)文件躺枕,可以使用空格隔開,如:

javac -nowarn @classmsgs.txt @classmsgs2.txt

就可以看到供填,Test1和Test2被編譯成class文件拐云,并且在編譯時(shí)會輸出編譯器正在執(zhí)行的操作日志(因?yàn)槭褂昧?verbose參數(shù))。

一般情況下近她,當(dāng)你需要編譯的java文件比較多叉瘩,或者需要設(shè)置的參數(shù)比較多時(shí),亦或者要復(fù)用一些參數(shù)信息時(shí)粘捎,可以將這些java文件名或者參數(shù)項(xiàng)抽取出來薇缅,放到一個(gè)或多個(gè)文件中。這一點(diǎn)很像spring中xml的import攒磨,css中@等在一個(gè)文件中引入其他文件的方式泳桦。看來軟件開發(fā)中娩缰,很多地方都是想通的灸撰。

綜上,javac的實(shí)際使用格式可以歸納為:

javac <options> <source files> @files

3.2拼坎、JAVAC命令中的參數(shù)項(xiàng)

在前面直接指向javac命令輸出的信息中浮毯,我們看到j(luò)avac用到的參數(shù)項(xiàng)有很多,那么這些參數(shù)項(xiàng)都是用來干什么的得呢?這里我按照自己查閱的資料以及自己的實(shí)驗(yàn)和理解泰鸡,做一下講解债蓝。這些參數(shù)項(xiàng)很多,我將它們分為一下幾類:
先寫到這里盛龄,先挖個(gè)坑饰迹,后續(xù)再填。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末余舶,一起剝皮案震驚了整個(gè)濱河市蹦锋,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌欧芽,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件葛圃,死亡現(xiàn)場離奇詭異千扔,居然都是意外死亡憎妙,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進(jìn)店門曲楚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來厘唾,“玉大人,你說我怎么就攤上這事龙誊「Ю” “怎么了?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵趟大,是天一觀的道長鹤树。 經(jīng)常有香客問我,道長逊朽,這世上最難降的妖魔是什么罕伯? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮叽讳,結(jié)果婚禮上追他,老公的妹妹穿的比我還像新娘。我一直安慰自己岛蚤,他們只是感情好邑狸,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著涤妒,像睡著了一般单雾。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上届腐,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天铁坎,我揣著相機(jī)與錄音,去河邊找鬼犁苏。 笑死硬萍,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的围详。 我是一名探鬼主播朴乖,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼助赞!你這毒婦竟也來了买羞?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤雹食,失蹤者是張志新(化名)和其女友劉穎畜普,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體群叶,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡吃挑,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年钝荡,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片舶衬。...
    茶點(diǎn)故事閱讀 39,779評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡埠通,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出逛犹,到底是詐尸還是另有隱情端辱,我是刑警寧澤,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布虽画,位于F島的核電站舞蔽,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏狸捕。R本人自食惡果不足惜喷鸽,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望灸拍。 院中可真熱鬧做祝,春花似錦、人聲如沸鸡岗。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽轩性。三九已至声登,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間揣苏,已是汗流浹背悯嗓。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留卸察,地道東北人脯厨。 一個(gè)月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像坑质,于是被迫代替她去往敵國和親合武。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法涡扼,類相關(guān)的語法稼跳,內(nèi)部類的語法,繼承相關(guān)的語法吃沪,異常的語法汤善,線程的語...
    子非魚_t_閱讀 31,625評論 18 399
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,808評論 6 342
  • 流程圖是以特定的圖形符號加上說明,表示算法的圖,所謂千言萬語不如一張圖红淡。對于電商甚至是企業(yè)的運(yùn)營管理來說卸伞,任何環(huán)節(jié)...
    一只流浪喵閱讀 44,263評論 0 4
  • 在這個(gè)浮躁的社會里披荊斬棘一路向前時(shí) 夜深人靜下班回家看見路燈一盞一盞孤獨(dú)的光芒 有那么一刻總是對自己充滿疑問 我...
    憶傾姐閱讀 200評論 0 0
  • 文 | 子在川上(王紫川) 時(shí)間輪回、卻不回頭锉屈!多情,終究輪回成無情垮耳。 花開時(shí)節(jié)本應(yīng)逢君颈渊,卻不幸恰逢...
    子在川上cherry閱讀 1,203評論 3 1