深入理解 Java HelloWorld

HelloWorld是每個(gè)Java程序員都知道的程序实愚。它很簡(jiǎn)單,但是簡(jiǎn)單的開始可以引導(dǎo)你去深入了解更復(fù)雜的東西。這篇文章將探究從這個(gè)HelloWorld這個(gè)簡(jiǎn)單程序中可以學(xué)到的東西。如果你對(duì)HelloWorld有獨(dú)到的理解逊躁,歡迎留下你的評(píng)論。

**HelloWorld.java
**

public class HelloWorld {

/**
 * 
 * @param args
 */

public static void main(String[] args) {
    // TODO Auto-generated method stub
    System.out.println("Hello World");
}

}
**
1隅熙、為什么一切都是從類開始稽煤?
**
Java程序是從類開始構(gòu)建的, 每個(gè)方法和字段都必須在類里面核芽。這是由于Java面向?qū)ο蟮奶匦? 一切都是對(duì)象,它是類的一個(gè)實(shí)例酵熙。面向?qū)ο缶幊陶Z(yǔ)言相比函數(shù)式編程語(yǔ)言有許多的優(yōu)勢(shì)轧简,比如更好的模塊化、可擴(kuò)展性等等绿店。

2****吉懊、為什么總有一個(gè)“main方法”?

main方法是程序的入口假勿,并且是靜態(tài)方法。static關(guān)鍵字意味著這個(gè)方法是類的一部分态鳖,而不是實(shí)例對(duì)象的一部分转培。為什么會(huì)這樣呢? 為什么我們不用一個(gè)非靜態(tài)的方法作為程序的入口呢?

如果一個(gè)方法不是靜態(tài)的浆竭,那么對(duì)象需要先被創(chuàng)建好以后才能使用這個(gè)方法浸须。因?yàn)檫@個(gè)方法必須要在一個(gè)對(duì)象上調(diào)用。對(duì)于一個(gè)入口來說邦泄,這是不現(xiàn)實(shí)的删窒。因此,程序的入口方法是靜態(tài)的顺囊。

參數(shù) “String[] args”表明可以將一個(gè)字符串?dāng)?shù)組傳遞給程序來幫助程序初始化肌索。

3、HelloWorld程序的字節(jié)碼

為了執(zhí)行這個(gè)程序特碳,Java文件首先被編譯成Java字節(jié)碼存儲(chǔ)到.class文件中诚亚。那么字節(jié)碼看起來是什么樣的呢?字節(jié)碼本身是不可讀的午乓,如果我們使用一個(gè)二進(jìn)制編輯器打開站宗,它看起來就像下面那樣:

file

在上面的字節(jié)碼中,我們可以看到很多的操作碼(比如CA益愈、4C等等)梢灭,它們中的每一個(gè)都有一個(gè)對(duì)應(yīng)的助記碼(比如下面例子中的aload_0)。操作碼是不可讀的蒸其,但是可以使用javap來查看.class文件的助記形式敏释。

對(duì)于類中的每個(gè)方法執(zhí)行“javap -c”可以輸出反匯編代碼。反匯編代碼即組成Java字節(jié)碼的指令枣接。

javap -classpath . -c HelloWorld

Compiled from "HelloWorld.java"
public class HelloWorld extends java.lang.Object{
public HelloWorld();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return

public static void main(java.lang.String[]);
Code:
0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3; //String Hello World
5: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
}

上面的代碼包含兩個(gè)方法: 一個(gè)是編譯器推斷出來的默認(rèn)的構(gòu)造器颂暇;另外一個(gè)是main方法。

接下來但惶,每個(gè)方法都有一系列的指令耳鸯。比如aload_0湿蛔、invokespecial #1等等∠嘏溃可以在Java字節(jié)碼指令集中查到每個(gè)指令的功能阳啥,例如aload_0用來從局部變量0中加載一個(gè)引用到堆棧,getstatic用來獲取類的一個(gè)靜態(tài)字段值财喳〔斐伲可以注意到,getstatic指令之后的“#2″指向的是運(yùn)行期常量池耳高。常量池是JVM運(yùn)行時(shí)數(shù)據(jù)區(qū)之一扎瓶。我們可以通過“javap -verbose”命令來查看常量池。

另外, 每個(gè)指令從一個(gè)數(shù)字開始,比如0、1踏堡、4等等。在.class文件中误证,每個(gè)方法都有一個(gè)對(duì)應(yīng)的字節(jié)碼數(shù)組。這些數(shù)字對(duì)應(yīng)于存儲(chǔ)每個(gè)操作碼及其參數(shù)的數(shù)組的下標(biāo)修壕。每個(gè)操作碼都是1個(gè)字節(jié)長(zhǎng)度愈捅,并且指令可以有0個(gè)或多個(gè)參數(shù)。這就是為什么這些數(shù)字不是連續(xù)的原因慈鸠。

現(xiàn)在蓝谨,我們使用“javap -verbose”這個(gè)命令來進(jìn)一步觀察這個(gè)類。

javap -classpath . -verbose HelloWorld

Compiled from "HelloWorld.java"
public class HelloWorld extends java.lang.Object
SourceFile: "HelloWorld.java"
minor version: 0
major version: 50
Constant pool:
const #1 = Method #6.#15; // java/lang/Object."<init>":()V
const #2 = Field #16.#17; // java/lang/System.out:Ljava/io/PrintStream;
const #3 = String #18; // Hello World
const #4 = Method #19.#20; // java/io/PrintStream.println:(Ljava/lang/String;)V
const #5 = class #21; // HelloWorld
const #6 = class #22; // java/lang/Object
const #7 = Asciz <init>;
const #8 = Asciz ()V;
const #9 = Asciz Code;
const #10 = Asciz LineNumberTable;
const #11 = Asciz main;
const #12 = Asciz ([Ljava/lang/String;)V;
const #13 = Asciz SourceFile;
const #14 = Asciz HelloWorld.java;
const #15 = NameAndType #7:#8;// "<init>":()V
const #16 = class #23; // java/lang/System
const #17 = NameAndType #24:#25;// out:Ljava/io/PrintStream;
const #18 = Asciz Hello World;
const #19 = class #26; // java/io/PrintStream
const #20 = NameAndType #27:#28;// println:(Ljava/lang/String;)V
const #21 = Asciz HelloWorld;
const #22 = Asciz java/lang/Object;
const #23 = Asciz java/lang/System;
const #24 = Asciz out;
const #25 = Asciz Ljava/io/PrintStream;;
const #26 = Asciz java/io/PrintStream;
const #27 = Asciz println;
const #28 = Asciz (Ljava/lang/String;)V;

{
public HelloWorld();
Code:
Stack=1, Locals=1, Args_size=1
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 2: 0

public static void main(java.lang.String[]);
Code:
Stack=2, Locals=1, Args_size=1
0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3; //String Hello World
5: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 9: 0
line 10: 8
}

引用JVM規(guī)范中的描述:運(yùn)行期常量池提供的功能類似傳統(tǒng)編程語(yǔ)言的符號(hào)表所起作用, 盡管它比傳統(tǒng)的符號(hào)表包含的內(nèi)容更廣范林束。

“invokespecial #1″指令中的“#1″指向常量池中的#1常量像棘。這個(gè)常量是 “Method #6.#15;”。通過這個(gè)數(shù)字壶冒,我們可以遞歸地得到最終的常量缕题。

LineNumberTable為調(diào)試器提供了用來指示Java源代碼與字節(jié)碼指令之間的對(duì)應(yīng)信息。例如胖腾,Java源代碼中的第9行對(duì)應(yīng)于main方法中的字節(jié)碼0烟零,并且第10行對(duì)應(yīng)于字節(jié)碼8。

如果想要了解更多關(guān)于字節(jié)碼的內(nèi)容咸作,可以創(chuàng)建一個(gè)更加復(fù)雜的類進(jìn)行編譯和查看锨阿,HelloWorld真的只是一個(gè)開始。

4记罚、HelloWorld在JVM中是如何執(zhí)行的墅诡?

現(xiàn)在的問題是JVM是怎樣加載這個(gè)類并調(diào)用main方法?

在main方法執(zhí)行之前, JVM需要加載桐智、鏈接以及初始化這個(gè)類末早。

1.加載將類/接口的二進(jìn)制形式裝入JVM中烟馅。

2.鏈接將二進(jìn)制類型的數(shù)據(jù)融入到JVM的運(yùn)行時(shí)。鏈接由3個(gè)步驟組成:驗(yàn)證然磷、準(zhǔn)備郑趁、以及解析(可選)。驗(yàn)證確保類姿搜、接口在結(jié)構(gòu)上是正確的寡润;準(zhǔn)備涉及到為類、接口分配所需要的內(nèi)存舅柜;解析是解析符號(hào)引用梭纹。

3.最后,初始化為類變量分配正確的初始值致份。

file

加載工作是由Java類加載器來完成的栗柒。當(dāng)JVM啟動(dòng)時(shí),會(huì)使用下面三個(gè)類加載器:

1.Bootstrap類加載器:加載位于/jre/lib目錄下的核心Java類庫(kù)知举。它是JVM核心的一部分,并且使用本地代碼編寫太伊。

2.擴(kuò)展類加載器:加載擴(kuò)展目錄中的代碼(比如/jar/lib/ext)雇锡。

3.系統(tǒng)類加載器:加載在CLASSPATH上的代碼。

所以僚焦,HelloWorld類是由系統(tǒng)加載器加載的锰提。當(dāng)main方法執(zhí)行時(shí),它會(huì)觸發(fā)加載其它依賴的類芳悲,進(jìn)行鏈接和初始化立肘。前提是它們已經(jīng)存在。

最后名扛,main()幀被壓入JVM堆棧谅年,并且程序計(jì)數(shù)器(PC)也進(jìn)行了相應(yīng)的設(shè)置。然后肮韧,PC指示將println()幀壓入JVM堆棧棧頂融蹂。當(dāng)main()方法執(zhí)行完畢會(huì)被彈出堆棧,至此執(zhí)行過程就結(jié)束了弄企。

今天就分享這么多超燃,歡迎各位朋友在留言區(qū)評(píng)論,對(duì)于有價(jià)值的留言拘领,我都會(huì)一一回復(fù)的意乓。如果覺得文章對(duì)你有一丟丟幫助,請(qǐng)給我點(diǎn)個(gè)贊吧约素,讓更多人看到該文章届良。
另外笆凌,小編最近將收集的Java程序員進(jìn)階架構(gòu)師和面試的資料做了一些整理,免費(fèi)分享給每一位學(xué)習(xí)Java的朋友伙窃,需要的可以進(jìn)群:751827870菩颖,歡迎大家進(jìn)群和我一起交流。

本文由博客一文多發(fā)平臺(tái) OpenWrite 發(fā)布为障!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末晦闰,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子鳍怨,更是在濱河造成了極大的恐慌呻右,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,383評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件鞋喇,死亡現(xiàn)場(chǎng)離奇詭異声滥,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)侦香,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門落塑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人罐韩,你說我怎么就攤上這事憾赁。” “怎么了散吵?”我有些...
    開封第一講書人閱讀 157,852評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵龙考,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我矾睦,道長(zhǎng)晦款,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,621評(píng)論 1 284
  • 正文 為了忘掉前任枚冗,我火速辦了婚禮缓溅,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘官紫。我一直安慰自己肛宋,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,741評(píng)論 6 386
  • 文/花漫 我一把揭開白布束世。 她就那樣靜靜地躺著酝陈,像睡著了一般。 火紅的嫁衣襯著肌膚如雪毁涉。 梳的紋絲不亂的頭發(fā)上沉帮,一...
    開封第一講書人閱讀 49,929評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼穆壕。 笑死待牵,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的喇勋。 我是一名探鬼主播缨该,決...
    沈念sama閱讀 39,076評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼川背!你這毒婦竟也來了贰拿?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,803評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤熄云,失蹤者是張志新(化名)和其女友劉穎膨更,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體缴允,經(jīng)...
    沈念sama閱讀 44,265評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡荚守,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,582評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了练般。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片矗漾。...
    茶點(diǎn)故事閱讀 38,716評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖薄料,靈堂內(nèi)的尸體忽然破棺而出缩功,到底是詐尸還是另有隱情,我是刑警寧澤都办,帶...
    沈念sama閱讀 34,395評(píng)論 4 333
  • 正文 年R本政府宣布,位于F島的核電站虑稼,受9級(jí)特大地震影響琳钉,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蛛倦,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,039評(píng)論 3 316
  • 文/蒙蒙 一歌懒、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧溯壶,春花似錦及皂、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至又跛,卻和暖如春碍拆,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評(píng)論 1 266
  • 我被黑心中介騙來泰國(guó)打工感混, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留端幼,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,488評(píng)論 2 361
  • 正文 我出身青樓弧满,卻偏偏與公主長(zhǎng)得像婆跑,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子庭呜,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,612評(píng)論 2 350

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