一、系列文章導言
《不該被忽視的CoreJava細節》系列文章將會持續更新。我希望自己通過這一系列文章的寫作,能與讀者一起進步,逐步完善對Java體系結構的了解。
二、本期關注點
幾乎翻看每一本與Java相關的入門書籍,教你跟着敲的第一個程序都會像下面這段代碼一樣。
1 public class HelloWorld { 2 public static void main(String[] args) { 3 System.out.println("Hello World"); 4 } 5 }
這個程序貌似很簡單
,很多人都不以為意。但是,請大家捫心自問一下,你真的有花時間研究過嗎或者說真的理解了嗎?
不過,請你大可放心,本文不是教你編譯器編譯這段程序機制,也不是教你字節碼反編譯后如何解讀,更不會教你JVM如何處理這段程序以及類的裝載機制。
本期只是想讓大家從最基礎的角度讀懂
public static void main(String[] args) { }
這段代碼,僅此而已。
三、main訪問限定詞趣聞
根據Java語言規范,main
方法必須聲明為public
。注意,Java語言是Sun公司對於Java語言的官方文檔,在Java語言方面具有最高的權威性。
但是,Java虛擬機規范並沒有要求main
方法一定是public
。
兩者的迥異導致,有些Java解釋器能夠執行非public
修飾main
方法所在的程序。
不過,這個bug最終在JDK1.4中得到了修復,其后的所有main
方法必須使用public
作為訪問修飾符。
下面我嘗試使用JDK1.7編寫此方法,將main
方法的public
修改成private
。
上圖說明,至少在JDK1.7版本,這個bug已經被修復,無法使用非public
修飾詞修飾main
方法。而且,我可以明確地告訴大家,1.7的虛擬機規范仍然沒有指明必須這樣做。
四、static
修飾詞、void
返回值對main
意味什么
先拿void
開刀。如果在學過Java之前學過C語言,那么你一定會想起每次寫main方法的時候都會寫上這么一段話。
1 int main() { 2 ... 3 return 0; 4 }
表示程序會給操作系統返回一個狀態碼0,操作系統收到狀態碼0表示程序正常退出。
但是,我們的Java語言並沒有返回值。也就是說,理論上Java程序並不會主動返回退出代碼。
那么到底Java程序有沒有返回狀態代碼呢?事實上,如果Java程序正常退出,則會返回默認的退出碼0。
1 public class HelloWorld { 2 public static void main(String[] args) { 3 System.out.println("Hello World"); 4 } 5 } // 程序正常退出,退出碼如下圖。
通過操作系統的errorlevel變量我們可以得到程序所返回的退出碼。關於Java退出碼需要再了解四點:
1)Java程序的退出碼並不會返回給JVM,這也就是方法用void
的原因所在。
2)Java程序的退出碼返回給JVM所在的操作系統,正常退出默認返回給操作系統狀態碼值為0。
3)退出碼其實是約定俗成的。一般約定[0,99]內整數代表正常退出,[100-199]代表警告退出,大於等於200代表異常退出,但是不同的操作系統卻不同。在Java中,默認的正常退出碼為0,異常退出或警告碼為1。
1 public class HelloWorld { 2 public static void main(String[] args) throws ClassNotFoundException{ 3 System.out.println("Hello World"); 4 Class.forName(""); 5 } 6 } // 不聲明異常,聲明異常,退出碼均為 1
4)可以自己設置退出狀態碼。Java調用java.lang.System.exit(int status)
方法結束程序執行,並向操作系統返回status
值。
1 public class HelloWorld { 2 public static void main(String[] args) { 3 System.out.println("Hello World"); 4 System.exit(1000); 5 } 6 } // 向JVM所在操作系統返回自定義狀態碼
順帶提一句,如果在eclipse做實驗並不會有狀態碼,或者說在eclipse跑程序,在命令行中狀態碼始終未0。其中差別始末,暫時未考慮清楚。
五、參數列表--變態考題
如果要出考題,前面的內容或許都用不到,基本沒有什么可以出的題目。但是一提到main
方法的參數列表,就有意思了。下面我就幾個問題解答一下諸位可能存在的疑問。
1)public static void main(String[] s) {}
是否正確?
咦,貌似和原來的有點區別,好像原來的形參是args
。不明真相的群眾可能一口咬定這句話是錯誤的,不巧的是,這句話確是沒有任何無法問題的。
1 public class HelloWorld { 2 public static void main(String[] s) { 3 System.out.println("Hello World"); 4 } 5 }
這段代碼能夠正確地輸出結果。其中的原因在於:原來的args
僅僅是參數,編譯器是不會檢查形參的變量名是否與args
一致的,只要有一個合法的名字,編譯器就認為沒有問題,運行也不會出錯。更進一步說,這個參數就是在控制台輸入參數后用於獲得參數用的,無非就是數組名字改了而已。
如果編譯程序的時候加上參數,比如javac HelloWorld.java 1 2 3
1 public class HelloWorld { 2 public static void main(String[] s) { 3 System.out.println("Hello World"); 4 System.out.println(s[0]); // 輸出結果為1 5 } 6 }
2)public static void main(String args)
是否正確?
這句代碼編譯器不能通過編譯,缺少程序入口。
3)public static void main(Char[] args)
是否正確?
這句代碼編譯器不能通過編譯,缺少程序入口。
上面三種類型說明,必須存在《code>public static void main(String[] 任意合法變量標識符) {}方法。這也是Java官方文檔所規定的,必須遵守。
六、奇怪,怎么有2個main
方法?
為什么就不可能存在多個main
方法?請看下面的例子。
1 public class HelloWorld { 2 public static void main(String[] args) { 3 main("Hello World"); 4 } 5 6 public static void main(String arg) { 7 System.out.println("Hello World"); 8 } 9 }
本例是通過函數的重載實現的。其實很容易想到,在同一個類中有多個相同方法名存在很可能是使用了Java重載機制(編譯器只檢查方法名和參數列表,如果存在不同,則認為是兩個不同的方法,編譯器認為語法成立)。
如果,一不小心寫成下面這種樣子,則會出現StackOverflowError
異常,本質上是虛擬機方法棧幀存不下無窮遞歸過程的數據量。
1 public class HelloWorld { 2 public static void main(String[] args) { 3 main("Hello World"); 4 } 5 // 無遞歸出口,導致無窮遞歸 6 public static void main(String arg) { 7 main("Hello World"); 8 } 9 }
七、結束語
讀完文本,是否覺得自己原來自己還有很多漏洞沒有彌補。別慌,我想說的是:Java只是一種工具,一種為我們服務的編程工具。人與工具之間的關系不應該主次顛倒,應該盡量讓工具為我們寫出高質量應用而服務,而不是在這已經發展了20多年的工具面前如同乞討者一般盲目仰慕前輩做出的巨大貢獻。
其實Java也就那樣,掌握語言的核心知識,學點大牛寫的API,那么隨着時間的積累,代碼量的增多,可能大家也可以作為第三方提供高質量的類庫。雞湯到此結束!