Java入門記(一):折騰HelloWorld
HelloWorld,學習每門語言的第一步。有人戲稱,這些年的程式設計生涯就是學習各種語言的HelloWorld,不知是自謙還是自嘲。目前所在的公司使用Java作為主要開發語言,我進行語言轉換也大半年了,這HelloWorld便是語言轉換的第一關。好在本科的時候學過那麼一點,而且在此之前進行了較長時間的C/C++開發,其間有不少的相似之處。這裡略去JDK的安裝和環境配置(JDK為1.6.0.45),直接從程式碼入手。
首先看一個最簡單的Java下的HelloWorld:
public class HelloWorld { public static void main(String[]agrs) { System.out.println("HelloWorld!"); } }
一般來說,初學者寫HelloWorld到這裡,編譯完執行一下看到結果就可以結束了。下面對這個小程式進行更多的探索,進一步瞭解和學習Java程式設計中的特性。
1.原始碼檔案的編碼
最初為了簡單起見,我是在Win7中用記事本編寫並儲存程式碼為HelloWorld.java,然後用命令列直接javac編譯。出於在Windows下寫Linux程式的習慣,我在記事本儲存時將程式碼儲存為UTF-8編碼的HelloWorld.java檔案。編譯時提示:
在仔細檢查原始碼確定沒有任何拼寫錯誤後,嘗試將編碼改回Windows預設的ANSI,成功生成了HelloWorld.class並能夠正確執行,看來是編碼不一致惹得禍。接下來,抱著嘗試的心態,使用Unicode和Unicode Big Endian儲存原始碼,發現也會報錯,只是提示不同,編譯器提示有非法字元。這個問題如果在Eclipse中用預設方式儲存檔案,則不會出現。
有趣的是,如果使用Java的I/O方法生成文字檔案,應該如何確定檔案的編碼,也是一個常見的問題。如果僅僅是涉及Windows/Linux兩個平臺之間的編碼差異,而不包括中文編碼,前者使用\r\n,而後者使用\n\r或\n即可。對於漢字編碼,需要在使用到的I/O方法中指定編碼,這裡不再做一步的詳述。
2.為什麼沒有import語句?
還記得經典的K&R中經典的HelloWorld麼?即使極盡精簡,C中仍然避免不了使用#include <stdio.h>來引入標頭檔案,才能使用printf函式。
而Java和C/C++不一樣,這個簡單的HelloWorld不需要類似include的import,也不需要使用名稱空間,看似更簡單了些。實際上,這是因為Java給每個Java檔案都預設匯入了java.lang這個包,從而省去了import java.lang;這個語句罷了。這樣,下面進行螢幕輸出直接使用System.out.println()即可。
java.lang中包括的都是常用的類和方法,具體內容讀者有興趣可以自行查閱。上文提到java.import是“預設匯入”,有沒有什麼辦法禁止其匯入?我搜索了下,目前還沒有查到相關的資料,如果哪位讀者瞭解,希望能告訴我。(這可能涉及到類載入器的問題,暫未進行研究)
如果你執意在這段簡單的程式碼中使用與import對應的package,可以參考本文第六節。
3.檔名為什麼要與類名一致?類名與修飾符問題
在實踐中可以看出,編譯結果是HelloWorld.class,但是執行的命令卻是java HelloWorld。如果這個檔案還有更多的類,可以看到這些類在編譯時都生成了*.class檔案。對於“類名和檔名一致”這個疑問提的並不合理,顯然程式碼編寫時,一個檔案中可以有很多個類。這涉及到了Java的特性(來自《Java程式設計思想(第四版中文版)》):
每個編譯單元(檔案)只能最多有一個public類;如果有,其名稱必須與含有這個編譯單元的檔名相匹配,包括大小寫。
如果不遵守這個要求,寫出類似下面的程式碼
//ERROR IN CODE public class HelloWorld { public static void main(String[]agrs) { System.out.println("HelloWorld!"); } } public class HelloWorld2 { public static void main(String[]agrs) { System.out.println("HelloWorld, me too!"); } }
那麼編譯器會提示這一點
如果把HelloWorld2類的public去掉,將使其變成包訪問許可權,程式可以正常執行,此時只執行HelloWorld.main(),並不會發生衝突。
實際上,如果這個檔案只有一個HelloWorld類,或者有兩個類,只要這個包括main()的類名與檔名一致,類名前不加public也是可以正常執行的,且呼叫的是與檔名一致的類的main()方法。但個人認為這不是良好的程式設計實踐,如下:
class HelloWorld { public static void main(String[]agrs) { System.out.println("HelloWorld!"); } } class HelloWorld2 { public static void main(String[]agrs) { System.out.println("HelloWorld, me too!"); } }
編譯時將生成HelloWorld.class和HelloWorld2.class,分別執行時,結果為兩個類各自的main()方法。
4.main()函式的引數表和修飾符
在C中,對於main()的修飾符和引數表有著很多細節要注意(可以參考五花八門的main())。對於Java,這裡對main()的寫法也進行簡單的探究。
先來看引數表String args[]。雖說編譯器要求必須是這種形式,但如果不用標準形式而用其他形式如int x、String s作為引數表,編譯是可以通過的,但是在執行時則會丟擲異常,無論是否提供了引數:
NoSuchMethodError表名,期望的是引數為String args[]的main()方法。雖然提供了同名方法,由於方法的過載機制,並不能代替期望的main(String args[])方法。
接下來看修飾符public。在第3條已經提到了public對於類名的修飾有所說明,而對於main()這個與檔案同名的類的成員方法,為了能被呼叫,只能用public修飾。不使用修飾符(包訪問許可權)、使用private或protected都會提示:
對於修飾符static,表明這個方法是在儲存在靜態儲存區的,不需要例項化物件就可以呼叫。去掉static後,可以編譯通過,執行時提示
為了進一步驗證這一點,可以編寫構造方法來驗證。(構造方法是在類的物件在例項化時會被呼叫的方法)
public class HelloWorld { HelloWrold { System.out.println("Constructor"); } public static void main(String[]agrs) { System.out.println("HelloWorld!"); } }
編譯執行時,可以看到構造方法並沒有執行。
對於修飾符void,也是必須的。改成int等並加上對應的return語句同樣會提示“NoSuchMethodError: main”。在《Java虛擬機器規範(JavaSE7)》(周志明等譯)中介紹到
Java虛擬機器的啟動是通過引導類載入器(Bootstrap Class Loader §5.3.1)建立一個初始類(Initial Class)來完成,這個類是由虛擬機器的具體實現指定。緊接著,Java虛擬機器連結這個初始類,初始化並呼叫它的public void main(String[])方法。之後的整個執行過程都是由對此方法的呼叫開始。
可見,void返回值也是被要求的,其他形式是不允許的。
經過進一步的測試可知,args[0]是第一個引數;而在C中,argv[0]是執行的程式名。
5.既然main()方法是類方法……
既然main()方法是類方法,那麼在例項化這個類的物件時,自然可以再次呼叫這個方法。對HelloWorld原始碼加對應的兩行,如下所示
public class HelloWorld { public static void main(String[] args) { HelloWorld h = new HelloWorld(); System.out.println("HelloWorld!"); h.main(args); } }
執行結果為
HelloWorld!
HelloWorld!
HelloWorld!
... ...
HelloWorld!
Exception in thread "main" java.lang.StackOverflowError
at sun.nio.cs.ext.DoubleByteEncoder.encodeLoop(Unknown Source)
at java.nio.charset.CharsetEncoder.encode(Unknown Source)
at sun.nio.cs.StreamEncoder.implWrite(Unknown Source)
at sun.nio.cs.StreamEncoder.write(Unknown Source)
at java.io.OutputStreamWriter.write(Unknown Source)
at java.io.BufferedWriter.flushBuffer(Unknown Source)
at java.io.PrintStream.write(Unknown Source)
at java.io.PrintStream.print(Unknown Source)
at java.io.PrintStream.println(Unknown Source)
at main.HelloWorld.main(HelloWorld.java:15)
at main.HelloWorld.main(HelloWorld.java:16)
at main.HelloWorld.main(HelloWorld.java:16)
at main.HelloWorld.main(HelloWorld.java:16)
... ...
可見HelloWorld被玩壞了,這個無限遞迴建立物件的過程導致了記憶體溢位。
6.試試package
當然,使用java更多的時候往往要處理多個檔案。為了組織同一名稱空間下的檔案,需要使用包來進行。對應於import,為了指定當前檔案在哪個包,需要加上package語句。隨便加上一個包名,最初的程式碼變成了
package test; public class HelloWorld { public static void main(String[]agrs) { System.out.println("HelloWorld!"); } }
編譯後,卻無法執行,如下圖所示
其實,包名是隱含目錄結構的。為了執行,需要把HelloWorld.class移入這個路徑的test資料夾,按照下面的方式執行才可以:
(2015.10.6更新)如果引用了三方jar包,可以在執行javac和java命令的時候使用-cp指定jar包所在相對路徑,或者直接把jar包放在該class檔案所在目錄或環境變數CLASSPATH指定的目錄下。
小結
可見,對於一個小小的HelloWorld,還是有不少東西可以發掘,只是限於篇幅和本人水平,本文僅僅進行了簡要的介紹。以下是本文提出的可以在後續學習中繼續深入的主題,僅供參考:
1.I/O方法編碼方式的選擇
2.包和程式碼組織
3.Java虛擬機器(JVM)
相關閱讀
相關推薦
Java入門記(一):折騰HelloWorld
HelloWorld,學習每門語言的第一步。有人戲稱,這些年的程式設計生涯就是學習各種語言的HelloWorld,不知是自謙還是自嘲。目前所在的公司使用Java作為主要開發語言,我進行語言轉換也大半年了,這HelloWorld便是語言轉換的第一關。好在本科的時候學過那麼一點,而且在此之前進行了較長時間的C
Java入門記(二):向上轉型與向下轉型
interface CanFight { void fight(); } interface CanSwim { void swim(); } interface CanFly { void fly(); } class ActionCharacter {
Java入門記(四):容器關係的梳理(上)——Collection
目錄 三、Set Java.util中的容器又被稱為Java Collections framework。雖然被稱為框架,但是其主要目的是提供一組介面儘量簡單而且相同、並且儘量高效、以便於開發人員按照場景選用,而不
Java入門記(三):初始化順序
初始化順序的規則 1.在一個類的物件例項化時,成員變數首先初始化,然後才呼叫構造器,無論書寫順序。如果呼叫構造器前,沒有顯式初始化,那麼會賦預設值。 這樣做法的原因可以理解為:構造器執行時可能會用到一些成員變數的初值。 2.static變數早於所有其他的類成員變數初始化,同樣無論書寫順序。但是stati
Java入門記(五):容器關係的梳理(下)——Map
注意:閱讀本文及相關原始碼時,需要資料結構相關知識,包括:雜湊表、連結串列、紅黑樹。 Map是將鍵(key)對映到值(value)的物件。不同的對映不能包含相同的鍵;每個鍵最多隻能對映到一個值。下圖是常見Map的介面和實現。與Collection相比,繼承關係簡單不少。 一、Map介面和Abs
Java入門(一):Hello World !
公司 java入門(一) 擁有 sys 成熟 pos pac runt asc 前言 從今天開始,準備寫Java Web開發的系列文章,畢竟自己主攻的還是Java方向,Python只是業余學習的興趣使然,在第二技能還沒有培養成熟前,做好第一技能的鞏固和提高是很有必要的。從正
SpringBoot入門(一):從HelloWorld開始
從0開始建立springBoot專案,話不多說,跟著我一步一步來就行了。 1.新建專案 1) 建立新專案,選擇project, 點點點就好了 2) Spring Initializr——>選擇SDK(1.8)—&mda
Java NIO筆記(一):NIO介紹
事件 .net 數據報 數據類型 單線程 long 處理 哪些 選擇 Java NIO即Java Non-blocking IO(Java非堵塞I/O),由於是在Jdk1.4之後添加的一套新的操作I/O工具包,所以通常會被叫做Java New IO。NI
java工具包一:日期處理
div 方便 開始 .text simple nor atd blog param 作者:NiceCui 本文謝絕轉載,如需轉載需征得作者本人同意,謝謝。 本文鏈接:http://www.cnblogs.com/NiceCui/p/7846812.html 郵箱:
java入門必學:HTML和CSS
java ;入門 ; html; css Java開發已經悄無聲息的走進我們的生活中,無論是手機軟件、手機Java遊戲還是電腦軟件等,只要你使用到電子產品就會碰到和Java有關的東西,更多的企業正采用Java語言開發網站,也吸引了好多誌同道合的朋友開始加入Java開發的行列。 我們知道在
java 進階一:代理和動態代理
實現 public rgs tostring 開源項目 body llb ack code 靜態代理: 定義頂級接口:Iservice //目標類和代理類都實現該接口 public interface Iservice { public String serv
Java入門第一課:資料型別
Java入門第一課:資料型別 文章目錄 Java入門第一課:資料型別 1. 科普類 2.資料型別 2.1整型 2.2浮點型別 2.3 char型別 2.4布林型別 2.5變數
Java的執行機制——有C++基礎的Java入門(一)
前言:還是那句話,第一、凡是涉及到概念性內容的時候,我都會到官網去確認內容的真實性!第二、我喜歡偏向於原理學習。在 java介紹裡面,我認為知道這是一門完全面向物件的語言就足夠了。我的導師說C++是認為程式設計師是很強大的,開放了所有的功能許可權;Java是認為程式設計師不是那麼全能的
Java基礎 實驗一:簡單資料型別和流程控制
1.實驗目的 掌握識別符號的定義規則、表示式的組成、各種資料型別及其使用方法、各種運算子的使用及其優先順序控制。掌握分支結構,迴圈結構,continue,break,語句標號等內容。 2.實驗內容 (1)輸入一個三角形的3個邊長,檢查是否能構成一個直角三角形。 (2)任意從鍵盤輸入一個0到
Java學習階段一:2.CSS
目錄 瞭解CSS 五大CSS選擇器 CSS匯入方式 瞭解CSS CSS 概述 CSS 指層疊樣式表 (Cascading Style Sheets) 樣式定義如何顯示 HTML 元素 樣式通常儲存在樣式表中
Java學習階段一:1.HTML
目錄 瞭解HTML語言 HTML整體結構 字型標籤 表單元素 瞭解HTML語言 什麼是 HTML? HTML 是用來描述網頁的一種語言。 HTML 指的是超文字標記語言 (Hyper Text Markup Language)
Java學習階段一:3.JavaScript
目錄 瞭解JavaScript JavaScript基本語法 JavaScript內部物件 JavaScript的DOM BOM 自定義函式 全域性函式 事件處理 瞭解JavaScript JavaScript 是指令碼語言 JavaScript 是一
RocketMQ爬坑記(一):安裝使用
1. RocketMQ簡介 RocketMQ作為一款純java、分散式、佇列模型的開源訊息中介軟體(阿里的說法是不遵循任何規範的,所以不能完全用JMS的那一套東西來看它),經歷了淘寶雙十一的洗禮,在功能和效能上據說是遠超ActiveMQ。 RocketMQ簡介,其中
JAVA入門(一)
前言 Java是一門面向物件程式語言,不僅吸收了C++語言的各種優點,還摒棄了C++裡難以理解的多繼承、指標等概念,因此Java語言具有功能強大和簡單易用兩個特徵。Java語言作為靜態面向
JAVA NIO學習一:NIO簡介、NIO&IO的主要區別
在前面學習了IO之後,今天我們開始進入NIO學習環節,首先我們會NIO做一個簡單的介紹,讓大家認識NIO,然後會和IO進行一個對比認識進行區分。好了,下面我們就開始學習: 一、NIO簡介 1.概述 從JDK1.4開始,Java提供了一系列改進的輸入/輸出處理的新特性,被統稱為NIO(即N