不該被忽視的CoreJava細節(一)


一、系列文章導言

《不該被忽視的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,那么隨着時間的積累,代碼量的增多,可能大家也可以作為第三方提供高質量的類庫。雞湯到此結束!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM