Java來自於Sun公司的一個叫Green的項目,其原先的目的是爲家用消費電子產品開發一個分佈式代碼系統,這樣咱們能夠把E-mail發給電冰箱、電視機等家用電器,對它們進行控制,和它們進行信息交流。開始,準備採用C++,但C++太複雜,安全性差,最後基於C++開發一種新的語言Oak(Java的前身),Oak是一種用於網絡的精巧而安全的語言,Sun公司曾依此投標一個交互式電視項目,但結果是被SGI戰勝。可憐的Oak幾乎無家可歸,恰巧這時MarkArdreesen開發的Mosaic和Netscape啓發了Oak項目組成員,他們用Java編制了HotJava瀏覽器,獲得了Sun公司首席執行官ScottMcNealy的支持,觸發了Java進軍Internet。Java的取名也有一個趣聞,有一天,幾位Java成員組的會員正在討論給這個新的語言取什麼名字,當時他們正在咖啡館喝着Java(爪哇)咖啡,有一我的靈機一動說就叫Java怎樣,獲得了其餘人的讚揚,因而,Java這個名字就這樣傳開了。 java
Java是Sun公司推出的新的一代面向對象程序設計語言,特別適合於Internet應用程序開發,它的平臺無關性直接威脅到Wintel的壟斷地位。一時間,"連Internet,用Java編程",成爲技術人員的一種時尚。雖然新聞界的報導有些言過其實,但Java做爲軟件開發的一種革命性的技術,其地位已被確立,這表如今如下幾個方面: ios
1.計算機產業的許多大公司購買了Java的許可證,包括IBM,Apple,DEC,Adobe,SiliconGraphics,HP,Oracel,Toshiba,以及最不情願的Microsoft。這一點說明,Java已獲得了工業界的承認。 程序員
2.衆多的軟件開發商開始支持Java的軟件產品。例如:Borland公司正在開發的基於Java的快速應用程序開發環境Latte,預計產品會在1996年中期發佈。Borland公司的這一舉措,推進了Java進入PC機軟件市場。Sun公司本身的Java開發環境JavaWorkshop已經發布。數據庫廠商如:Illustra,Sysbase,Versant,Oracle都在開發CGI接口,支持HTML和Java。今天是以網絡爲中心的計算時代,不支持HTML和Java,應用程序的應用範圍只能限於同質的環境(相同的硬件平臺)。 數據庫
3.Intranet正在成爲企業信息系統最佳的解決方案,而其中Java將發揮不可替代的做用。Intranet的目的是把Internet用於企業內部的信息系統,它的優勢表如今:便宜,易於使用和管理。用戶無論使用何種類型的機器和操做系統,界面是統一的Intrnet瀏覽器,而數據庫、Web頁面、應用程序(用Java編的Applet)則存在WWW服務器上,不管是開發人員,仍是管理人員,抑或是用戶均可以受益於該解決方案。Java語言正在不斷髮展和完善,Sun公司是主要的發展推進者,較通用的編譯環境有JDK(JavaDevelopKit)與JWS(JavaWorkshop)。還有不少其餘公司正在開發Java語言的編譯器與集成環境,預計不久Java語言的正確性與效率都將會提升,用戶用Java編程和如今用C++編程同樣方便。 express
Java究竟是一種什麼樣的語言呢?Java是一種簡單的面象對象的分佈式的解釋的健壯的安全的結構中立的可移植的性能很優異的多線程的動態的語言。 編程
Java最初是爲對家用電器進行集成控制而設計的一種語言,所以它必須簡單明瞭。Java語言的簡單性主要體如今如下三個方面: canvas
1.Java的風格相似於C++,於是C++程序員是很是熟悉的。從某種意義上講,Java語言是C及C++語言的一個變種,所以,C++程序員能夠很快就掌握Java編程技術。 小程序
2.Java摒棄了C++中容易引起程序錯誤的地方,如指針和內存管理。 數組
3.Java提供了豐富的類庫。 瀏覽器
面向對象能夠說是Java最重要的特性。Java語言的設計徹底是面向對象的,它不支持相似C語言那樣的面向過程的程序設計技術。Java支持靜態和動態風格的代碼繼承及重用。單從面向對象的特性來看,Java相似於SmallTalk,但其它特性、尤爲是適用於分佈式計算環境的特性遠遠超越了SmallTalk。
Java包括一個支持HTTP和FTP等基於TCP/IP協議的子庫。所以,Java應用程序可憑藉URL打開並訪問網絡上的對象,其訪問方式與訪問本地文件系統幾乎徹底相同。爲分佈環境尤爲是Internet提供動態內容無疑是一項很是宏偉的任務,但Java的語法特性卻使咱們很容易地實現這項目標。
Java致力於檢查程序在編譯和運行時的錯誤。類型檢查幫助檢查出許多開發早期出現的錯誤。Java自已操縱內存減小了內存出錯的可能性。Java還實現了真數組,避免了覆蓋數據的可能。這種功能特徵大大縮短了開發Java應用程序的週期。Java提供:Null指針檢測數組邊界檢測異常出口字節代碼校驗
另外,爲了創建Java做爲網絡的一個總體,Java將它的程序編譯成一種結構中立的中間文件格式。只要有Java運行系統的機器都能執行這種中間代碼。如今,Java運行系統有Solaris2.4(SPARC),Win32系統(Windows95和WindowsNT)等.Java源程序被編譯成一種高層次的與機器無關的byte-code格式語言,這種語言被設計在虛擬機上運行,由機器相關的運行調試器實現執行。
Java的安全性可從兩個方面獲得保證。一方面,在Java語言裏,象指針和釋放內存等C++功能被刪除,避免了非法內存操做。另外一方面,當Java用來建立瀏覽器時,語言功能和一?copy;瀏覽器自己提?copy;的功能結合起來,使它更安全。Java語言在你的機器上執行前,要通過不少次的測試。它通過代碼校驗,檢查代碼段的格式,檢測指針操做,對象操做是否過度以及試圖改變一個對象的類型。
若是byte-code經過代碼校驗,沒有返回錯誤,咱們可知道:代碼沒有堆棧上溢出和下溢出全部操做代碼參數類型都是正確的沒有發生非法數據轉換,如將整數轉換成指針。訪問對象操做是合法的
ClassLoader經過將本機類與網絡資源類的名稱分開,來保持安全性。由於調入類時總要通過檢查,這樣避免了特洛伊木馬現象的出現。從網絡上下載的類被調進一個與源相關的私有的名字域。當一個私有類訪問另外一個類時,build-in(本機類)首先被檢查,而後檢查相關的類。這樣就避免了破壞本機類狀況的出現。
同體繫結構無關的特性使得Java應用程序能夠在配備了Java解釋器和運行環境的任何計算機系統上運行,這成爲Java應用軟件便於移植的良好基礎。但僅僅如此還不夠。若是基本數據類型設計依賴於具體實現,也將爲程序的移植帶來很大不便。例如在Windows3.1中整數(Integer)爲16bits,在Windows95中整數爲32bits,在DECAlpha中整數爲64bits,在Intel486中爲32bits。經過定義獨立於平臺的基本數據類型及其運算,Java數據得以在任何硬件平臺上保持一致。Java語言的基本數據類型及其表示方式以下:byte8-bit二進制補碼short16-bit二進制補碼int32-bit二進制補碼long64-bit二進制補碼float32-bitIEEE754浮點數double32-bitIEEE754浮點數char16-bitUnicode字符
在任何Java解釋器中,數據類型都是依據以上標準具體實現的。由於幾乎目前使用的全部CPU都能支持以上數據類型、8~64位整數格式的補碼運算和單/雙精度浮點運算。Java編譯器自己就是用Java語言編寫的。Java運算系統的編制依據POSIX方便移植的限制,用ANSIC語言寫成。Java語言規範中也沒有任何"同具體實現相關"的內容。
Java解釋器(運行系統)能直接運行目標代碼指令。連接程序一般比編譯程序所需資源少,因此程序員能夠在建立源程序上花上更多的時間。
若是解釋器速度不慢,Java能夠在運行時直接將目標代碼翻譯成機器指令。Sun用直接解釋器一秒鐘內可調用300,000個過程。翻譯目標代碼的速度與C/C++的性能沒什麼區別。
Java提?copy;的多線程功能使得在一個程序裏可同時執行多個小任務。線程--有時也稱小進程--是一個大進程裏分出來的小的獨立的進程。由於Java實現的多線程技術,因此比C和C++更鍵壯。多線程帶來的更大的好處是更好的交互性能和實時控制性能。固然實時控制性能還取決於系統自己(UNIX,Windows,Macintosh等),在開發難易程度和性能上都比單線程要好。任何用過當前瀏覽器的人,都感受爲調一副圖片而等待是一件很煩惱的事情。在Java裏,你可用一個單線程來調一副圖片,而你能夠訪問HTML裏的其它信息而沒必要等它。
Java的動態特性是其面向對象設計方法的擴展。它容許程序動態地裝入運行過程當中所須要的類,這是C++語言進行面向對象程序設計所沒法實現的。在C++程序設計過程當中,每當在類中增長一個實例變量或一種成員函數後,引用該類的全部子類都必須從新編譯,不然將致使程序崩潰。Java從以下幾方面採起措施來解決這個問題。Java編譯器不是將對實例變量和成員函數的引用編譯爲數值引用,而是將符號引用信息在字節碼中保存下傳遞給解釋器,再由解釋器在完成動態鏈接類後,將符號引用信息轉換爲數值偏移量。這樣,一個在存儲器生成的對象不在編譯過程當中決定,而是延遲到運行時由解釋器肯定的。這樣,對類中的變量和方法進行更新時就不至於影響現存的代碼。解釋執行字節碼時,這種符號信息的查找和轉換過程僅在一個新的名字出現時才進行一次,隨後代碼即可以全速執行。在運行時肯定引用的好處是能夠使用已被更新的類,而沒必要擔憂會影響原有的代碼。若是程序鏈接了網絡中另外一系統中的某一類,該類的全部者也能夠自由地對該類進行更新,而不會使任何引用該類的程序崩潰。Java還簡化了使用一個升級的或全新的協議的方法。若是你的系統運行Java程序時遇到了不知怎樣處理的程序,不要緊,Java能自動下載你所須要的功能程序。
Java提供了一個功能強大語言的全部功能,但幾乎沒有一點含混特徵。C++安全性很差,但C和C++仍是被你們所接受,因此Java設計成C++形式,讓你們很容易學習。Java去掉了C++語言的許多功能,讓Java的語言功能很精煉,並增長了一些頗有用的功能,Java去掉了如下幾個C和C++功能和特徵:指針運算結構typedefs#define須要釋放內存全局變量的定義這種功能都是很容易引發錯誤的地方。
Web瀏覽是如今國際網甚至局域網的主要使用方式。文檔能很容易地顯示文本和各類圖片,他還能提供超文本連接。這些瀏覽器調用HTML語言寫的文檔,HTML/WWW瀏覽器技術只限於文本和圖象。若是你想播放一種聲音或運行一個演示程序,你不得不下載那個文件並用你本機上的能理解和運行那個文件格式的程序來播放它。Java程序和它的瀏覽器HotJava,提供了可以讓你的瀏覽器運行程序的方法。你能從你的瀏覽器裏直接播放聲音。你還能播放頁面裏的動畫。Java還能告訴你的瀏覽器怎樣處理新的類型文件。當咱們能在2400baud線上傳輸視頻圖象時,HotJava將能顯示這些視頻。
Java是一種與平臺無關的語言,所以用Java開發的網絡應用系統能夠在各類平臺上運行,大大增長了開發效率,減小重複勞動。並且,Java集成的網絡功能充分有利於開發網絡應用系統。
1.Java的產生與流行是當今internet發展的客觀要求2.java是一門各方面性能都很好的編程語言,它的基本特色是簡單、面象對象、分佈式、解釋的、健壯的、安全的、結構中立的、可移植的、性能很優異的、多線程的、動態的。?reg;分適合在internet環境上開發應用系統。3.java能夠製做大部分網絡應用程序系統,並且與當今流行的WWW瀏覽器結合得很好。
Java不只提供了一個豐富的語言和運行環境,並且還提供了一個免費的Java開發工具集(JavaDevelopersKits,簡稱JDK)。編程人員和最終用戶能夠利用這個工具來開發java程序或調用Java內容。JDK包括如下工具:javacJava語言編譯器,輸出結果爲Java字節碼java,Java字節碼解釋器javapDisassembeler:Java字節碼分解程序,本程序返回Java程序的成員變量及方法等信息。javaprof資源分析工具,用於分析Java程序在運行過程當中調用了哪些資源,包括類和方法的調用次數和時間,以及各數據類型的內存使用狀況等。javahC代碼處理工具,用於從Java類調用C++代碼javaAppletViewer小應用程序瀏覽工具,用於測試並運行Java小應用程序javaDebuggerAPIJava調試工具APIPrototypeDebuggerJava調試工具原型
Java開發環境還包括Java類庫(包括I/O類庫、用戶界面類庫、網絡類庫等)和HotJavaWWW瀏覽器。其中,HotJava瀏覽器提供了在WWW環境下運行Java代碼的一個運行系統,並且還爲WWW開發人員提供了一個Java開發框架。Java解釋器是面向Java程序的一個獨立運行系統,它能夠一種穩定、高性能方式運行那些獨立於平臺的Java字節碼,Java編譯器則用於生成這種字節碼。
Java程序的編譯程序是javac.exe。javac命令將Java程序編譯成字節碼,而後你可用java解釋器java命令來解釋執行這種Java字節碼。Java程序源碼必須存放在後綴爲.java的文件裏。Java程序裏的每個類,javac都將生成與類相同名稱但後綴爲.class文件。編譯器把.class文件放在.java文件的同一個目錄裏,除非你用了-d選項。當你引用到某些本身定義的類時,必須指明它們的存放目錄,這就須要利用環境變量參數CLASSPATH。環境變量CLASSPATH是由一些被分號隔開的路徑名組成。若是傳遞給javac編譯器的源文件裏引用到的類定義在本文件和傳遞的其它文件中找不到,則編譯器會按CLASSPATH定義的路徑來搜索。例如:
CLASSPATH=.;C:\java\classes則編譯器先搜索當前目錄,若是沒搜索到,則繼續搜索C:\java\classes目錄。注意,系統老是將系統類的目錄缺省地加在CLASSPATH後面,除非你用-classpath選項來編譯。javac_g是一個用於調試的未優化的編譯器,功能與用法和javac同樣。javac的用法以下:
javac[-g][-O][-debug][-depend][-nowarn][-verbose][-classpathpath][-nowrite][-ddir]file.java...
如下是每一個選項的解釋。
選項解釋:
-classpathpath定義javac搜索類的路徑。它將覆蓋缺省的CLASSPATH環境變量的設置。路徑是由一?copy;由逗號隔開的路徑名組成,通常格式以下:.;<your_path>例如:.;C:\java\doc\classes;C:\tools\java\classes表示編譯器遇到一個新類,它先在本文件中查找它的定義,若是沒有,則在本文件所處目錄下其它文件中查找它的定義,若是尚未,則繼續搜索C:\java\doc\classes目錄中的全部文件,以此類推。
-ddirectory指明類層次的根目錄,格式以下:
javac-d<my_dir>MyProgram.java
這樣將MyProgram.java程序裏的生產的.class文件存放在my_dir目錄裏.
-g帶調試信息編譯,調試信息包括行號與使用java調試工具時用到的局部變量信息。若是編譯沒有加上-O優化選項,只包含行號信息。
-nowarn關閉警告信息,編譯器將不顯示任何警告信息。
-O優化編譯static,final,private函數,注意你的類文件可能更大。
-verbose
讓編譯器與解釋器顯示被編譯的源文件名和被加載的類名。
環境變量
CLASSPATH用來提?copy;給系統搜索用戶定義的類的缺省路徑。各路徑由分號隔開,例如:
.;C:\java\doc\classes;C:\tools\java\classes表示編譯器遇到一個新類,它先在本文件中查找它的定義,若是沒有,則在本文件所處目錄下其它文件中查找它的定義,若是尚未,則繼續搜索C:\java\doc\classes目錄中的全部文件,以此類推。
jdb導遊
在早期前Betal版的Java調試器jdb是命令行形式的,如用Sun公司的dbx調試器。用jdb來調試Java應用程序,在調試?reg;前,要確證你的應用程序是帶標誌-g編譯的。例如:javac-gHelloWorld.java
help命令將顯示jdb裏的可用命令列表。
>help<命令列表>threads[threadgroup]--列出線程thread<threadid>--設置缺省線程Suspend[threadsid(s)]--將線程掛起resume[threadid(s)]--從新啓動線程where[id]|a1|--打印線程的堆棧threadgroups--列出線程組號threadgroup<name>--設置當前線程組print<id>[id(s)]--打印對象或域dump<id>[id(s)]--打印全部對象信息locals--打印當前堆棧全部局部變量classes--列出當前所知的類methods<classid>--列出一個類的成員函數stopin<classid>.<method>--在一個成員函數裏設置斷點stopat<class.id>:<line>--在一行裏設置斷點up[nframes]--在線程堆棧裏往上移down[nframes]--在線程堆棧裏往下移clear<classid>:<line>--清除一個斷點step--執行當前行cont--從斷點處繼續執行catch<class.id>--爲指定的狀況中斷ignor<class.id>--爲指定的狀況忽略list[linenumber]--打印源程序use[Sourcefilepath]--顯示或改變源路徑memeory--報告內存使用狀況loadclassname--加載Java類以便調試run<args>--開始執行加載的類!!--重複以上的命令help(?)--列出全部的命令exit(orquit)--離開調試器
java-java語言解釋器java命令解釋java字節碼
語法:java[options]classname<args>java_g[options]classname<args>
描述:java命令由java編譯器javac輸出的Java字節碼。
classname參數是要執行的類名稱。注意任意在類名稱後的參數都將傳遞給要執行類的main函數。
java執行完main函數後推出,除非main函數建立了一個或多個線程。若是main函數建立了其它線程,java老是等到最後一個線程推出才推出。
選項:
-cs,-checksource當一個編譯過的類調入時,這個選項將比較字節碼更改時間與源文件更改時間,若是源文件更改時間靠後,則從新編譯此類並調入此新類。
-classpathpath定義javac搜索類的路徑。它將覆蓋缺省的CLASSPATH環境變量的設置。路徑是由一?copy;由逗號隔開的路徑名組成,通常格式以下:.;<your_path>例如:.;C:\java\doc\classes;C:\tools\java\classes表示解釋器遇到一個新類,它先在本文件中查找它的定義,若是沒有,則在本文件所處目錄下其它文件中查找它的定義,若是尚未,則繼續搜索C:\java\doc\classes目錄中的全部文件,以此類推。
-mxx設置最大內存分配池,大小爲x,x必須大於1000bytes。缺省爲16兆。
-msx設置垃圾回收堆的大小爲x,x必須大於1000bytes。缺省爲1兆。
-noasyncgc關閉異步垃圾回收功能。此選項打開後,除非顯式調用或程序內存溢出,垃圾內存都不回收。本選項不打開時,垃圾回收線程與其它線程異步同時執行。
-ssx每一個Java線程有兩個堆棧,一個是java代碼堆棧,一個是C代碼堆棧。-ss選項將線程理C代碼用的堆棧設置成最大爲x。
-ossx每一個Java線程有兩個堆棧,一個是java代碼堆棧,一個是C代碼堆棧。-oss選項將線程理java代碼用的堆棧設置成最大爲x。
-v,-verbose讓java解釋器在每個類被調入時,在標準輸出打印相應信息。
環境變量
CLASSPATH用來提?copy;給系統搜索用戶定義的類的缺省路徑。各路徑由分號隔開,例如:
.;C:\java\doc\classes;C:\tools\java\classes表示解釋器遇到一個新類,它先在本文件中查找它的定義,若是沒有,則在本文件所處目錄下其它文件中查找它的定義,若是尚未,則繼續搜索C:\java\doc\classes目錄中的全部文件,以此類推。
JavaWorkShop是SUN公司的一個新產品,它是一個集成的java語言開發環境,它包括如下工具:
lPortfolio和Project管理器l源文件編輯器lBuild管理工具l調試器l項目測試l?copy;展在線超文本鏈接到幫助文件
這?copy;工具在JavaWorkShop的第一頁都有相似Web頁面的超級鏈接,如圖:
注意,JavaWorkShop採用的是當今瀏覽器的界面風格,你想做什麼,只需找到相應的超級鏈接就能夠了,具體編譯及調試功能是用嵌如到HTML文檔裏的Applet實現的,所以,對習慣於用internet瀏覽方式的用戶來講,這種界面很容易接受。
Protfolios是一?copy;java應用程序或Applet的集中。它讓你更好的管理更多的projects。一個project是portfolio裏的一個子集,它包含了如下信息:
1.怎樣編譯本項目2.怎樣調試和瀏覽本項目3.怎樣運行本項目4.怎樣發佈本項目
源文件編輯器能夠從build管理器、調試器和源文件瀏覽器裏的超級鏈接進入。在這個模塊裏,你能夠輸入源文件。
本模塊是項目的編譯器,你能夠點build按鈕直接進入本模塊,若是某個文件出錯,錯誤信息會提?copy;一個超級鏈接,直接指到出錯的源文件地點。
顧名思義,本模塊能讓你可視化建造一?copy;複雜界面,若是你用過VisualBasic,你會發現它們很類似。
調試器能讓你很方便地跟蹤程序的執行與發現程序的錯誤。
Java語言有兩個開發環境,一個是免費的JDK,是命令行方式的。還有一個JavaWorkShop,是開發java程序的集成環境。本章簡要介紹了它們的使用方法。
如今你能夠複習一下Java語言的背景材料,它的基本結構象C/C++,但任何用面向過程語言編寫過程序的人均可以瞭解Java語言的大部分結構.
Java語言的源程序代碼由一個或多個編譯單元(compilationunit)組成,每一個編譯單元只能包含下列內容(空格和註釋除外):*一個程序包語句(packagestatement)*入口語句(importstatements)*類的聲明(classdeclarations)*界面聲明(interfacedeclarations)每一個Java的編譯單元可包含多個類或界面,可是每一個編譯單元最多隻能有一個類或者界面是公共的。Java的源程序代碼被編譯以後,便產生了Java字節代碼。Java的字節代碼由一種不依賴於機器的指令組成,這種指令能被Java的運行系統(runtimesystem)有效地解釋。Java的運行系統工做起來如同一臺虛擬機。在當前的Java實現中,每一個編譯單元就是一個以.java爲後綴的文件。每一個編譯單元有若干個類,編譯後,每一個類生成一個.class文件。.class文件是Java虛擬機可以識別的代碼。
三種類型以下://註釋一行/*一行或多行註釋*//**文檔註釋**/
文檔註釋通常放在一個變量或函數定義以前,指示在任何自動生成文檔系統中調入。這種註釋都是聲明條目的描述.。
變量,函數,類和對象的名稱都是標識符,程序員須要標識和使用的東西都須要標識符。在Java語言裏,標識符以字符或_,$開頭,後面能夠包含數字,標識符是大小寫有區別的,沒有長度限制。
有效的標識符mynameict_networkHello_sys_path$bill
例子:inta_number;char_onechar;float$bill;
關鍵詞abstractcontinuefornewswitch
booleandefaultgotonullsynchronized
breakdoifpackagethis
bytedoubleimplementsprivatethreadsafe
byvalueelseimportprotectedthrow
caseextendsinstanceofpublictransient
catchfalseintreturntrue
charfinalinterfaceshorttry
classfinallylongstaticvoid
constfloatnativesuperwhile
其它保留詞如下單詞被保留使用:castfuturegenericinner
operatorouterrestvar
Java使用五種基本類型:integer(整數),floating(浮點數),point(指針),Boolean(布爾變量),CharacterorString(字符或字符?reg;)。integer整數下邊給出的數據表示都是整數的例子:4,15,089,0xAD00
整數長度數據類型表示
8bitsbyte
16bitsshort
32bitsint
64bitslong
floating浮點數下邊給出的數據表示都是浮點數的例子:6.37,3.7E15,3e8
浮點數長度數據類型表示
32bitsfloat
64bitsdouble
Boolean布爾變量下邊是布爾變量的兩種可能取值:truefalse
Character字符下邊給出的都是字符的例子:a\t(tab)\u????(unicode)
String字符?reg;下邊給出的都是字符?reg;的例子:"Thisisastringliteral""中國科學院計算所"
數組你能夠定義任意類型的數組.chars[];這是字符型數組;int[]array;這是整型數組;你還能夠定義數組的數組.intblock[][]=newint[2][3];數組邊界在運行時被檢測,避免堆棧溢出和內存崩潰.
在Java裏,數組其實是一個對象,數組有一個成員變量:length。你能夠用這個成員函數來查看任意數組的長度.inta[][]=newint[10][3]a.length/*10*/a[0].length/*3*/
建立數組在Java裏建立數組,你可以使用兩種基本方法?reg;一。建立一個空數組:intlist[]=newint[50];或你能夠用初始數值填充數組.Stringnames[]={"Chenji","Yuan","Chun","Yang"};至關於下面功能:Stringnames[];names=newString[4];names[0]=newString("Chenji");names[1]=newString("Yuan");names[2]=newString("Chun");names[3]=newString("Yang");
在編譯時你不能象下例那樣建立靜態數組。intname[50];//將產生一個編譯錯誤
你也不能用new操做去填充一個沒定義大小的數組。intname[];for(inti=0;i<9;i++){name[i]=i;}
Java語言的表達式和C語言很是相似。
運算符運算符(operator)優先級從高到底排列以下:.[]()++--!~instanceof*/%+-<<>>>>><><=>\==!=&^&&||?:=op=,
整數運算符在整數運算時,若是操做數是long類型,則運算結果是long類型,不然爲int類型,毫不會是byte,short或char型。這樣,若是變量i被聲明爲short或byte,i+1的結果會是int。若是結果超過該類型的取值範圍,則按該類型的最大值取模。單目整數運算符是:
運算符操做-單目非~位補碼++加1--減1
++運算符用於表示直接加1操做。增量操做也能夠用加運算符和賦值操做間接完成。++lvalue(左值?copy;表示lvalue+=1,++lvalue也表示lvalue=lvalue+1(只要lvalue沒有反作用)。--運算符用於表示減1操做。++和--運算符既能夠做爲前綴運算符,也能夠作爲後綴運算符。雙目整數運算符是:
運算符操做**+加-減*乘/除%取模&位與|位或^位異或<<左移>>右移(帶符號)>>>添零右移
整數除法按零舍入。除法和取模遵照如下等式:(a/b)*b+(a%b)==a整數算術運算的異常是因爲除零或按零取模形成的。它將引起一個算術異常。下溢產生零,上溢致使越界。例如:加1超過整數最大值,取模後,變成最小值。一個op=賦值運算符,和上表中的各雙目整數運算符聯用,構成一個表達式。整數關係運算符<,>,<=,>=,==和!=產生boolean類型的數據。
布爾運算符布爾(boolean)變量或表達式的組合運算能夠產生新的boolean值。單目運算符!是布爾非。雙目運算符&,|和^是邏輯AND,OR和XOR運算符,它們強制兩個操做數求布爾值。爲避免右側操做數冗餘求值,用戶能夠使用短路求值運算符&&和||。用戶能夠使用==和!=,賦值運算符也能夠用&=、|=、^=。三元條件操做符?:和C語言中的同樣。
浮點運算符浮點運算符能夠使用常規運算符的組合:如單目運算符++、--,雙目運算符+、-、*和/,以及賦值運算符+=,-=,*=,和/=。此外,還有取模運算:%和%=也能夠做用於浮點數,例如:a%b和a-((int)(a/b)*b)的語義相同。這表示a%b的結果是除完後剩下的浮點數部分。只有單精度操做數的浮點表達式按照單精度運算求值,產生單精度結果。若是浮點表達式中含有一個或一個以上的雙精度操做數,則按雙精度運算,結果是雙精度浮點數。
數組運算符數組運算符形式以下:<expression>[<expression>]可給出數組中某個元素的值。合法的取值範圍是從0到數組的長度減1。取值範圍的檢查只在運行時刻實?copy;。
?reg;運算符?reg;以String對象實現。運算符"+"完成並?reg;操做,若是必要則自動把操做數轉換爲String型。若是操做數是一個對象,它可定義一個方法toString()返回該對象的String方式,例如floata=1.0print("Thevalueofais"+a+"\n");+運算符用到?reg;上的例子Strings="a="+a;+=運算符也能夠用於String。注意,左邊(下例中的s1)僅求值一次。s1+=a;//s1=s1+a//若a非String型,自動轉換爲String型。
對象運算符雙目運算符instanceof測試某個對象是不是指定類或其子類的實例。例如:if(myObjectinstanceofMyClass){MyClassanothermyObject=(MyClass)myObject;…}是斷定myObject是不是MyClass的實例或是其子類的實例。
強制和轉換Java語言和解釋器限制使用強制和轉換,以防止出錯致使系統崩潰。整數和浮點數?reg;間能夠來回強制轉換,但整數不能強制轉換成數組或對象。對象不能被強制爲基本類型。
下面幾個控制結構是從C語言借鑑的。
分支結構
if/else分支結構
if(Boolean){statemanets;}else{statements;}
switch分支結構
switch(expr1){caseexpr2:statements;break;caseexpr3:statements;break;default:statements;break;}
循環結構for循環結構
for(initexpr1;testexpr2;incrementexpr3){statements;}
While循環結構
While(Boolean){statements;}
Do循環結構
do{statements;}while(Boolean);
通常順序控制
break[label]continue[label]reutrnexpr;label:statement;
for循環例子下面是一個程序例子,畫幾條線,分別用紅,綠,藍顏色,這段程序多是Java函數的一部分:
intcount;for(count=1;count<=12;count++){switch(count%3)}case0:setColor(Color.red);break;case1:setColor(Color.blue);break;case2:setColor(Color.green);break;}g.drawLine(10,count*10,80,count*10);}
Java的類包含變量和函數。數據變量能夠是一?copy;原始的類型,如int,char等。成員函數是一?copy;可執行的過程。例如,下面程序裏:publicclassClassOne{inti;publicClassOne(){i=10;}
publicvoidAdd_i(intj){i=i+j;}}
ClassOne包含一個變量i和兩個成員函數,ClassOne(intfirst)和Add_i(intj)。
成員函數成員函數是一?copy;可被其它類或本身類調用的處理子程序。一個特殊的成員函數叫構造函數,這個函數名稱通常與本類名程相同。它沒有返回值。
構造函數和成員函數當你在Java裏定義一個類時,你可定義一個或多個可選的構造函數,當建立本類的一個對象時用某一個構造函數來初始化本對象。用前面的程序例子來講明,當ClassOne類建立一個新實例時,全部成員函數和變量被建立(建立實例)。構造函數被調用。ClassOnemc:mc=newClassOne();
關鍵詞new用來建立一個類的實例,一個類用new初始化?reg;前並不佔用內存,它只是一個類型定義,當mc對象初始化後,mc對象裏的i變量等於10。你能夠經過對象名來引用變量i。(有時稱?reg;爲實例變量)mc.i++;//mc實例變量加1由於mc有ClassOne類的全部變量和成員函數,咱們能夠使用一樣的語法來調用成員函數Add_i:Add_i(10);如今mc.i變量等於21.
結束函數Java並不支持析構函數(C++裏的定義),由於java自己提?copy;對象無用時自動清除的功能,同時它也提?copy;了一個自動拉圾箱的成員函數,在清除對象時被調用:Protectedvoidfinalize(){close();}
對象有必定的生命期並在它的生命期間使用資源,當一個對象再也不被使用時,它應釋放內存,避免內存溢出。在Java裏,收集和釋放內存是一個叫自動廢品回收站的線程的責任。這個線程監視對象有效範圍並給一個走出有效範圍的對象做上標識。
例如:Strings;//沒有分配內存s=newString("oldstring");//分配內存s="newstring";//從新分配內存(建立新對象)
咱們將在之後訪問String類時將更加明白它的工做過程,但它的快速工做過程是這樣的:1.建立一個新的String類對象並填充以"oldstring"2.建立另外一個String對象並填充以"newstring"注意咱們建立了兩個對象。Stirng對象"oldstring"Stirng對象"newstring"
在第三條語句裏,第一個包括"oldstring"的叫作s的對象已走出了有效範圍,沒有任何方法能夠再訪問他,咱們如今有一個新的對象也叫s,包含"newstring"。在下一個廢品回收線程,前一個對象將被標識並清除。
子類是利用存在的對象建立一個新對象的機制,好比,若是你有一個Horse類,你能夠建立一個Zebra子類,Zebra是Horse的一種。
classZebraextendsHorse{intnumber_OF_stripes:}
關鍵詞extends來定義對象有的子類.Zebra是Horse的子類。Horse類裏的全部特徵都將拷貝到Zebra類裏,而Zebra類裏能夠定義本身的成員函數和實例變量。Zebra稱爲Horse的派生類或繼承。另外,你也許還想覆蓋基類的成員函數。用ClassOne說明,下面是一個派生類覆蓋Add_i功能的例子.
importClassOne;publicclassNewClassextendsClassOne{publicvoidAdd_i(intj){i=i+(j/2);}}
當NewClass類的實例建立時,變量i初始化值爲10,但調用Add_i產生不一樣的結果。NewClassmnc;mnc=newNewClass();mnc.Add_i(10);
訪問控制Java裏當你建立一個新類時,你能夠標明變量和成員函數的訪問層次。
publicpublicvoidAnyOneCanAccess(){}public實例變量和成員函數能夠任意其它類調用。
protectedprotectedvoidOnlySubClasses(){}protected實例變量和成員函數只能被其子類調用.
privateprivateStringCreditCardNumber;private實例變量和成員函數只能在本類裏調用.
friendlyvoidMyPackageMethod(){}缺省的,若是沒有定義任何防火控制,實例變量或函數缺省定義成friendly,意味着能夠被本包裏的任意對象防問,但其它包裏的對象不可防問。
靜態成員函數和變量有?copy;時候,你建立一個類,但願這個類的全部實例都公用一個變量。也就是說,全部這個類的對象都只有實例變量的同一個拷貝。這種方法的關鍵詞是static,例如:
classBlock{staticintnumber=50;}
全部從Block類建立的對象的number變量值都是相同的。無任在哪一個對象裏改變了number的值,全部對象的number都跟着改變。一樣的,你能夠定義static成員函數,但這個成員函數不能訪問非static函數和變量。
classBlock{staticintnumber=50;intlocalvalue;staticvoidadd_local(){localvalue++;//沒有運行}staticvoidadd_static(){number++;//運行}}
訪問一個類的實例變量時,this關鍵詞是指向這個類自己的指針,在前面ClassOne例子中,咱們能夠增長構造函數以下:
publicclassClassOne{inti;publicClassOne(){i=10;}
publicClassOne(intvalue)this.i=value;}
publicvoidAdd_i(intj){i=i+j;}}
這裏,this指向ClassOne類的指針。若是在一個子類裏覆蓋了父類的某個成員函數,但又想調用父類的成員函數,你能夠用super關鍵詞指向父類的成員函數。
importClassOne;publicclassNewClassextendsClassOne{publicvoidAdd_i(intj){i=i+(j/2);super.Add_i(j);}}
下面程序裏,i變量被構造函數設成10,而後15,最後被父類(ClassOne)設成25。
NewClassmnc;mnc=newNewClass();mnc.Add_i(10);
至今爲止,我用在類前面只用了一個public關鍵詞,其實它有下面4種選擇:
abstract一個abstract類必須至少有一個虛擬函數,一個abstract類不能直接建立對象,必須繼承子類後才能。
final一個final類聲明瞭子類鏈的結尾,用final聲明的類不能再派生子類。
publicpublic類能被其它的類訪問。在其它包裏,若是想使用這個類必須先import,不然它只能在它定義的package裏使用。
synchronicable這個類標識表示全部?copy;類的成員函數都是同步的。
面向對象的一個最大優勢就是可以定義怎樣使用這個類而沒必要真正定義好成員函數。若是程序由不一樣的用戶實現時是頗有用的,這不需用戶使用相同的成員函數名。
在java裏Graphics類裏一個abstract類的例子以下:publicabstractclassGraphics{publicabstractvoiddrawLine(intx1,inty1,intx2,inty2);publicabstractvoiddrawOval(intx,inty,intwidth,intheight);publicabstractvoiddrawRect(intx,inty,intwidth,intheight);...}
在Graphics類裏聲明瞭幾個成員函數,但成員函數的實際代碼是在另一?copy;地方實現的。
publicclassMyClassextendsGraphics{publicvoiddrawLine(intx1,inty1,intx2,inty2){<畫線程序代碼>}}
當一個類包含一個abstract成員函數,這個類必須定義爲abstract類。然而並非abstract類的全部的成員函數都是abstract的。Abstract類不能有私有成員函數(它們不能被實現),也不能有靜態成員函數。
當你肯定多個類的操做方式都很相象時,abstract成員函數是頗有用的。但若是你須要使用這?copy;abstract成員函數,必須建立一個新類,這樣有時很繁瑣。接口提?copy;了一種抽象成員函數的有利方法。一個接口包含了在另外一個地方實現的成員函數的收集。成員函數在接口裏定義爲public和abstract。接口裏的實例變量是public,static和final。接口和抽象的主要區別是一個接口提?copy;了封裝成員函數協議的方法而沒必要強迫用戶繼承類。
例子:publicinterfaceAudiClip{//Startplayingtheclip.voidplay();//Playtheclipinaloop.voidloop();//Stopplayingtheclipvoidstop();}
想使用AudioClip接口的類使用implenents關鍵詞來提?copy;成員函數的程序代碼。classMyClassimplementsAudioClip{voidplay(){<實現代碼>}voidloop<實現代碼>}voidstop<實現代碼>}}
優勢一個接口類能夠被任意多的類實現,每一個類能夠共享程序接口而沒必要關心其它類是怎樣實現的。classMyOtherClassimplementsAudioClip{voidstop(){<實現代碼>}...}
內部成員函數Java還提?copy;了調用C和C++函數的方法。用native關鍵詞來定義C和C++的函數。
publicclassDate{intnow;publicDate(){now=time();}privatenativeinttime();
static{System.loadLibrary("time");}}
一?copy;Java代碼寫好後,就須要如下步驟執行:1.用javah來建立頭文件(.h)2.用javah來建立stub文件3.用C和C++寫native成員函數的代碼4.編譯stub文件和.C文件成一個動態可加載庫5.用java運行java程序或appletviewer運行applet
注意:Native成員函數超出了類的範圍。
包(Package)由一組類(class)和界面(interface)組成。它是管理大型名字空間,避免名字衝突的工具。每個類和界面的名字都包含在某個包中。按照通常的習慣,它的名字是由"."號分隔的單詞構成,第一個單詞一般是開發這個包的組織的名稱。
定義一個編譯單元的包編譯單元的包由package語句定義。若是使用package語句,編譯單元的第一行必須無空格,也無註釋。其格式以下:packagepackageName;若編譯單元無package語句,則該單元被置於一個缺省的無名的包中。
使用其它包中的類和界面在Java語言裏提?copy;一個包能夠使用另外一個包中類和界面的定義和實現的機制。用import關鍵詞來標明來自其它包中的類。一個編譯單元能夠自動把指定的類和界面輸入到它本身的包中。在一個包中的代碼能夠有兩種方式來定義來自其它包中的類和界面:*在每一個引用的類和界面前面給出它們所在的包的名字;//前綴包名法acme.project.FooBarobj=newacme.project.FooBar();*使用import語句,引入一個類或一個界面,或包含它們的包。引入的類和界面的名字在當前的名字空間可用。引入一個包時,則該包全部的公有類和界面都可用。其形式以下://從acme.project引入全部類importacme.project.*;這個語句表示acme.project中全部的公有類被引入當前包。如下語句從acme.project包中進入一個類Employec_List。//從acme.project而引入Employee_Listimportacme.project.Employee_list;Employee_Listobj=newEmployee_List();在使用一個外部類或界面時,必需要聲明該類或界面所在的包,不然會產生編譯錯誤。
import(引用)類包(classpackage)用import關鍵詞調入,指定package名字如路徑和類名,用*匹配符能夠調入多於一個類名。
importjava.Date;importjava.awt.*;
若是java源文件不包含package,它放在缺省的無名package。這與源文件同目錄,類能夠這樣引入:importMyClass。
Java系統包:Java語言提?copy;了一個包含窗口工具箱,實用程序,通常I/O,工具和網絡功能的包。
java.applet這個包包含量了一?copy;設計applet的類,用一個類Applet和三個接口.AppletContext;AppletStub;和AudioClip.
java.awt另外一個窗口工具箱包.awt,包含了一?copy;產生裝飾物和GUI成員的類。這個package包括:Button,Checkbox,Choice,Component,Graphics,Menu,Pane1,TextArea和TextField。
java.ioI/Opackage包含文件輸入/輸出類,FileInputStream和FileOutputStream.
java.lang這個包包含Java語言類,包含:對象,線程,異常出口,系統,整數,原點,數學,字符等。
java.net這個類支持TCP/IP網絡協議,幷包含Socket類,URL和URL相聯繫的類。
java.util這個類包含一?copy;程序的同步類,它包含Date,Dictionary類等。
當在Java程序中發生一個錯誤時,例如:一個變元的值非法,代碼會發現這個錯誤,並引起一個異常(exception)。在缺省的狀況下,異常會輸出一個錯誤消息,而後停止線程的執行。可是,程序本身能夠定義異常處理段(exceptionhandler)來截獲(catch)異常,並從錯誤中恢復。有一?copy;異常是由Java解釋器在運行時刻引起的。實際上,任何類均可以定義屬於本身的異常,並使用throw語句引起它們。一個throw(引起?copy;語句是由throw關鍵字和一個對象構成。按常規,該對象應該是Exception類的實例或其子類的實例。throw語句會引發執行轉向相應的異常處理段。當一個throw語句執行時,它下面的全部代碼再也不執行了,它所在的方法也再也不返回值。下面的例子將演示如何建立一個Exception的子類,而後引起一個異常。classMyExceptionextendsException{}classMyClass{voidoops(){if(/*不出現錯誤*/){…}else{/*出錯*/
}else{/*出錯*/thrownewMyException();}}}爲了定義一個異常處理段,程序必須用try語句把可能產生異常的代碼成組。在try語句後面跟上一個或多個catch(截獲?copy;語句,每一個異常對應一個catch語句。每一個catch語句中包含着異常處理段。例如:try{p.a=10;}catch(NullPointerExceptione){println("pwasnull");}catch(Exceptione){println("othererrorsoccured");}catch(Objectobj){println("Whothrewthatobject?");}catch語句和一個方法定義相似,只不過該方法只有一個參數,且無返回類型。參數能夠是一個類或一個界面。當一個異常發生時,嵌套的try/catch語句會尋找出與該異常類相匹配的參數。若是一個參數和指定異常匹配則:*該參數和指定的異常是同一個類,或*該參數是指定異常的子類,或*若是參數是一個界面,指定異常類實現了這個界面。第一個參數和異常匹配的try/catch語句,則與其匹配的catch語句執行。在catch語句執行完後,程序的執行被恢復。但已不可能恢復到異常發生處再次執行。例如:print("now");try{print("is");thrownewMyException();print("a");}catch(MyExceptione){print("the");}print("time\n");打印爲"nowisthetime"。正如這個例子所示,異常應該主要用於錯誤處理,若用於其它方面會使代碼晦澀難?reg;。異常處理段是能夠嵌套的,容許異常處理能夠發生在多個地方。嵌套異常處理一般用於當第一個處理程序沒法徹底從錯誤中恢復過來的時候,而不得不執行一?copy;清除代碼。爲了把異常處理控制傳遞給更高層的處理段,能夠再一次對截獲對象實?copy;throw操做。注要再次實?copy;throw異常的方法,throw語句執行完後,會終止執行。try{f.open();}catch(Exceptione){f.close();throwe;}
定局語句finally(定局?copy;語句是用於保證不管在異常是否發生的狀況下,某?copy;代碼被執行。下例說明finally語句的用法:try{//作某?copy;動做;}finally{//此後清除;}和如下代碼相似try{//作某?copy;動做}catch(Objecte){//此後清除;throwe;}
}//此後清除;即便try塊中包含return,break,continue,throw語句,finally語句也會被執行。例如:下面的代碼"finally"老是被輸出,而"aftertry"僅在a!=10時被輸出。try{if(a==10){return;}}finally{print("finally\n");}print("aftertry\n");
運行時刻異常本節列出的清單是Java解釋器引起的各類異常。當運行時刻發現各類錯誤,由解釋器引起異常。
ArithmeticException若是程序試圖除0,或用0取模,會產生ArithmeticException(算術異常?copy;,其它算術操做不會產生異常。有關Java如何處理其它算術錯誤的信息,見"整數運算符"和"浮點運算符"兩節。例如:下面的代碼將會引起ArithmeticException異常:classArith{publicstaticvoidmain(Stringargs[]){intj=0;j=j/j;}}
NullPointerException當程序試圖訪問一個空對象中的變量或方法,或一個空數組中的元素時則引起NullPointerException(空指針異常?copy;。例如,訪問長度爲0的數組a[0]。有如下類聲明,運行時會引起NullPointerException異常:classNull{publicstaticvoidmain(Stringargs[]){Stringo=null;inta[]=null;o.length();a[0]=0;}}有趣的是,若是咱們引起一個空對象,也會產一NullPointerException異常。
IncompatibleClassChangeException當一個類的定義被改變,而引用該類的其它類沒有被從新編譯時,會產生這一異常。有四種類更改會致使運行時刻引起IncompatibleClassChangException異常。*一個類中的變量聲明由static變成非static,而其它訪問該類這一變量的類沒有被從新編譯。*一個類中的變量聲明由非static變成static,而其它訪問該類這一變量的類沒有被從新編譯。*類中聲明的某個域被刪除,而其它訪問該域的類沒有被從新編譯。*類中聲明的某個方法被刪除,而其它訪問該方法的類沒有被從新編譯。
ClassCastException若是試圖把對象o強制成ClassC,而o既不是ClassC的實例,也不是ClassC子類的實例,這時便會產生ClassCastException。classClassCast{publicstaticvoidmain(Stringargs[]){Objecto=newObject();Strings=(string)o;s.length();}}
}
NagativeArraySizeException若是一個數組的長度是負數,則會引起NagativeArraySizeException(數組負下標?copy;異常。例以下面類定義的代碼在運行時引起這一異常:classNegArray{publicstaticvoidmain(Stringargs[]){inta[]=newint[-1];a[0]=0;}}
OutOfMemoryException當系統沒法再向應用程序提?copy;內存時,會引起OutOfMemoryException(內存溢出?copy;異常。這種異常只能出如今建立新對象的時候,即new被調用的時候。例如,下面一段代碼在運行時刻會引起OutOfMemoryException異常:classLink{inta[]=newint[1000000];Linkl;}ClassOutOfMem{publicstaticvoidmain(Stringargs[]){
publicstaticvoidmain(Stringargs[]){Linkroot=newlink();Linkcur=root;while(true){cur.l=newLink();cur=cur.l;}}}
NoClassDefFoundException若是一個類被引用,但在運行時刻,系統沒有找到被引用的類,這時會引起NoClassDefFoundException(未找到類定義?copy;異常。例如,NoClass類的聲明以下:classNoClass{publicstaticvoidmain(Stringargs[]){Cc=newC();}}當NoClass運行時,若是解釋器找不到C類,則會產生NoClassDefFoundException。注意,在NoClass被編譯時C類必定要存在。
IncompatibleTypeException若是試圖爲一界面做實例,則會引起IncompatibleTypeException(類型不兼容?copy;異常。例如,下面的代碼會引起一個IncompatibleTypeException。InterfaceI{}classIncompType{publicstaticvoidmain(Stringargs[]){Ir=(I)new("I");}}
ArrayIndexOutOfBoundsException試圖訪問數組中的一個非法元素時,會引起ArrayIndexOutOfBoundsException(數組索引越界?copy;異常。例如:ClassArrayOut{publicstaticvoidmain(Stringargs[]){inta[]=newint[0];a[0]=0;}}
publicstaticvoidmain(Stringargs[]){inta[]=newint[0];a[0]=0;}}UnsatisfiedLinkException若是一個方法被聲明爲本機,但該方法在運行時刻卻不能鏈接到一個例程體上去時,會產生UnsatisfiedLinkException(沒法鏈接?copy;異常。例如:ClassNoLink{staticnativevoidfoo();publicstaticvoidmain(Stringargs[]){foo();}}
InternalExceptionInternalException(內部?copy;異常是不能被引起的。只有在運行失敗做一致性檢查時,纔會引起這個異常。
1.Java語言的基本結構象C/C++。2.Java語言的源程序代碼由一個或多個編譯單元(compilationunit)組成。
學習一門新語言最好是先看幾個簡單的程序例子。下面咱們將看到幾個很是基本的程序例子。
Java應用程序是指能夠獨立運行在Java虛擬機上的程序,它是一種中間代碼(byte-code?copy;。好比你的應用程序叫my.java,程序裏有一個名稱爲app1的類,用Javac或其它編譯器,編譯後將會生成app1.class,則在命令行狀態下輸入:javaapp1就能夠運行此程序。注意,用java命令運行的類必須有main函數,不然不能執行。與普通java應用程序不一樣的另外一種另外一種Java程序叫JavaApplet。咱們把它譯成Java小程序,這種程序後綴也是.class,但它不能直接在java虛擬機上運行,也就是輸入java*.class不能運行,這種程序裏能夠沒有main函數,它必須由某個瀏覽器來運行,好比Appletviewer或Netscape2.02以上等。這種程序咱們將在後面章節中介紹。
讓咱們來看一看最簡單的Java應用程序例子,來理解一下它的結構:Filename:1.java
classmyfirst{publicstaticvoidmain(Stringargs[]){System.out,println("ThisismyfirstJavaApplication");}}
這就是一個完整的Java應用程序,將它編譯:Javac1.java在當前目錄下,它將生成myfirst.class文件,Javamyfirst屏幕上將會輸出:ThisismyfirstJavaApplication
讓咱們來一步一步分析每句話含義(1?copy;classmyfirst這一行用關鍵詞class來定義名爲myfirst的新類,myfirst是新類的名稱,必須是一個有效的標識符,有效標識符定義請見程序設計基礎章節。類的說明包括數聽說明和成員函數說明,都放在類後面的大括號裏面。通常類定義以下:class類名稱{數據定義;函數定義;}
(2)publicstaticvoidmain(Stringargs[])public是一個表示訪問權限的關鍵字,表示此成員函數是公有的,能夠被其餘類直接調用,包括java解釋器。相對應的關鍵字有private和protected,friend。private表示只能被本類訪問,protected表示只能被子類訪問,friend是缺省的訪問權限,表示能被本包(package)中任意類訪問,對其它包中的類是不可訪問的。static表示main成員函數在myfirst類的全部對象中是惟一的,所以若是本程序生成另外一個myfirst類對象,調用的main函數將是同一個函數。void表示main函數沒有返回值,若是有返回類型值,則可加上interger或boolean諸如此類,對於有返回值的函數,其函數實體的最後應加上return語句。main這個函數是這運行應用程序的入口點,所以編寫應用程序是必須有main()函數,且是惟一的。
(3?copy;System.out.println一句這一句是main函數裏的功能語句,是調用Java裏System包裏的out類的println成員函數,是標準輸入輸出。
Java語言很相似C和C++語言。在C語言裏,經過在命令行輸入參數,C程序可由main函數讀入這?copy;參數,java程序也同樣,請看下面程序:Filename:2.java
classMy2{publicstaticvoidmain(Stringargs[]){intarc=args.length;if(arc>0){for(inti=0;i<arc;i++)System.out.println(args[i])}else{System.out.println("Applicationhavenoargs!");}}}
編譯:javac2.java將在本目錄下生成My2.class文件。
運行:javaMy2輸出:Applicationhavenoargs!
運行:javaMy2arg1arg2arg3輸出:arg1arg2arg3這說明,java裏參數的傳遞是這樣的,命令行裏類名後第一個參數放在args[0]裏,第二個參數放在args[1]裏,以此類推。
Frame介紹在Java語言裏,Frame類的功能是建立圖形用戶界面(GUI)的基本窗口。在典型瀏覽器裏,Frame類是顯示GUI的父類。
類的層次結構以下:java.lang.Objectjava.awt.Componentjava.awt.Containerjava.awt.Windowjava.awt.Frame
Frame是從沒有邊框和菜單條的空白窗口?copy;充來的。這種窗口主要用在彈出式窗口方式下。Frame給窗口一個邊框,一個佈局和一個窗口條。
你能夠經過建立本身的多個Frame來建立圖形用戶界面的應用程序。咱們已知道怎樣創建獨立的java應用程序,那麼怎樣將各類迷人的圖形結合在一塊呢?咱們經過創建一個Frame來實現這個功能。Frame讓咱們訪問窗口,和applet工做區有許多相同的地方。下面是一個小的應用程序,它彈出一個框架而且在框架裏顯示信息。(信息能夠是命令行參數,也能夠是缺省的信息?copy;。若是你在窗口裏點一下,程序退出。
基本框架程序importjava.awt.*;
/**Memo.java*Asimplestand-alonegraphicalapplication*/
publicclassMemoextendsFrame{publcStringmotd;
publicMemo(Strings){//setourtitlesuper("MemoFrame");
motd=s;resize(300,300);}
publicMemo(){this("ThisisaMemo");}
publicvoidpaint(Graphicsg){g.drawString(motd,15,15);g.drawString("ClickanywheretoExit",15,25);}
publicvoidstart(){show();}
publicbooleanmouseDown(Evente,intx,inty){//hidethewindowhide();//freeupsystemresourcesdispose();//Quittheapp.System.exit(0);returnfalse;}
publicstaticvoidmain(Stringargs[]){Memom;if(args.length>){m=newMemo(args[0]);}else{m=newMemo();}m.start();}}
將Frame聯繫起來l熟悉的函數paint()和mouseDown(),看起來有點眼熟。這?copy;函數與applet的函數同樣。實際上,一個frame包含各類GUI組件與applet的形式同樣。另外一個熟悉的函數是start()。這個函數並沒必要要,由於咱們沒有覆蓋任何已存在的函數。但你想繼承applet編程的風格,你仍是能夠使用start(),stop(),init()與destroy()等函數。
l新函數咱們熟悉的函數start()調用了show()。show()函數是Window類的繼承,它顯示Fame及其全部組件。在mouseDown()函數裏咱們看到兩個函數:hide()和dispose()。hide()只簡單地使Frame不可見。你能夠在任什麼時候候調用它來隱藏窗口。dispose()函數釋放由Frame佔有的系統資源。只有在你不須要Frame時才調用它。
l構造函數Memo例子還包含了一個新函數Memo()。其實這個類有兩個Memo()函數!任何與類名相同的函數都是構造函數。它在建立一個新對象時被調用。你能夠認爲它是一個對象的init()初始化函數。爲何須要兩個構造函數呢?有了兩個構造函數,咱們能夠利用多態性的優勢,有兩種方法建立一個新Memo對象。咱們能夠簡單的建立使用缺省信息的Memo對象:m=newMemo();或者,咱們能夠本身提?copy;信息:m=newMemo("Ourmessage");
Frame控制本程序的最後功能是在main()裏創建並顯示frame。它由如下兩步實現://Step1m=newMemo();
//Step2m.start();第一步初始化memo對象。咱們象操做其它對象同樣操做m。爲顯示frame,咱們須要調用show()。這在第二步裏實現。另外一個要注意的是程序的健壯性:咱們基本的frame裏沒有包含處理標準WINDOW_DESTROY消息的函數。這樣,你將不能在窗口的控制菜單裏選中"Quit"或"Exit"。爲了增長這個功能,你須要增長如下的處理程序:
publicbooleanhandleEvent(Evente){if(e.id==Event.WINDOW_DESTROY){dispose();System.exit(1);returntrue;}else{//Goaheadanddowhatwenormallywouldhavedonereturnsuper.handleEvent(e);}}
菜單圖形界面依靠菜單來指導用戶操做。設計獨立的Java應用程序時,Java提?copy;建立和使用菜單的直接方法。象其它組件同樣,new將建立一個菜單:
MenuoptionMenu;optionsMenu=newMenu("Options");
菜單項一?copy;你建立了一個菜單,你能夠使用add()來組建菜單項:
optionsMenu.add(newMenuItem("Option1");optionsMenu.add(newMenuItem("Option2");
菜單事件當你選中某個菜單項時,你建立此菜單項的字符?reg;將在事件中返回。你能夠象測試按鈕選擇同樣測試菜單選擇:
publicbooleanaction(Evente,Objectarg){...if(e.targetinstanceofMenuItem){System.out.println((String)arg);}...}
其它菜單項除了上面描述的簡單菜單項外,你還可增長CheckBox菜單項,分割線,以及子菜單。下面是一?copy;例子:Menum,n;m=newMenu("Examples");m.add(newMenuItem("Basic"));m.add(newMenuItem("Simple"));
//addaseparatorm.add(newMenuItem("-"));
//addaCheckboxitemm.add(newCheckboxMenuItem("Check"));
//addasubmenun=newMenu("MoreExamples");n.add(newMenuItem("SubBasic"));n.add(newMenuItem("SubSimple"));m.add(n);
菜單條你建立好菜單後,你應將?reg;放在應用程序的菜單條上:mb=newMenubar();
mb.add(m);mb.add(optionsMenu);
而後你可爲applet設置菜單條:setMenuBar(mb);
爲了看一個更復雜的獨立的圖形界面應用程序,下面有一個數字轉換的例子:
importjava.awt.*;
publicclassd2xextendsFrame{intdecimalValue=0;StringbaseXValue=newString("0");TextFielddDisplay,xDisplay;
//d2xconstructorpublicd2x(){super("DecimalConverter");//setthetitleoftheframeMenuBarmb=newMenuBar();Buttond2Binary=newButton("Binary");Buttond2Octal=newButton("Octal");Buttond2Hex=newButton("Hex");Buttond2Base36=newButton("Base36");Panelp1=newPanel();Panelp2=newPanel();Panelp3=newPanel();
//addasimplemenuMenum=newMenu("Application");m.add(newCheckboxMenuItem("Base36Active");m.add(newMenuItem("Exit"));
//addmenutomenubarmb.add(m);setMenuBar(mb);//installthismenubarintheframe
//Addbuttonstotheirownpanelp3.setLayout(newFlowLayout());p3.add(d2Binary);p3.add(d2Octal);p3.add(d2Hex);p3.add(d2Base36);
//AddtextfieldsLabeldLabel=newLabel("EnterDeecimal:");LabelxLabel=newLabel("ConvertedValue:");dDisplay=newTextField(integer.toString(decimalValue),7);xDisplay=newTextField(baseXValue,32);xDisplay.setEditable(false);p1.setLayout(newFlowLayout(FlowLayout.LEFT));p2.setLayout(newFlowLayout(FlowLayout.LEFT));p1.add(dLabel);p1.add(dDisplay);p2.add(xLabel);p2.add(xDisplay);
//Addthepanelsadd("North",p1);add("Center",p2);add("South",p3);}//endd2xconstructor
publicvoidstart(){resize(400,150);show();}
publicvoidupdateXDisplay(){xDisplay.setText(baseXValue);}
publicbooleanhandleEvent(Eventevt){if(evt.targtintanceofMenuItem){if("Exit".equals(((MenuItem)evt.target).getLabel())){hide();dispose();System.exit(0);returnfalse;}retruntrue;}elseif(evt.targetinstanceofButton){Stringwhick=((Button)evt.target).getLabel();if(whick.equals("Binary")){decimalValue=Integer.parseInt(dDisplay.getText());baseXValue=Interger.toString(decimalValue,2);}if(whick.equals("Octal")){decimalValue=Integer.parseInt(dDisplay.getText());baseXValue=Interger.toString(decimalValue,8);}if(whick.equals("Hex")){decimalValue=Integer.parseInt(dDisplay.getText());baseXValue=Interger.toString(decimalValue,16);}if(whick.equals("36")){decimalValue=Integer.parseInt(dDisplay.getText());baseXValue=Interger.toString(decimalValue,36);}updateXDisplay();returntrue;}returnfalse;}
publicstaticvoidmain(Stringargs[]){d2xm=newd2x();m.start();}}
輸出結構如圖:
1.java有兩種類型的應用程序,一種是直接運行在java虛擬機上,用java命令執行;另外一種運行在瀏覽器裏,由瀏覽器調用執行,通常稱它爲Applet小程序。本書主要討論第一種應用程序。2.java應用程序是由類組成的,並且用java命令行執行的類必須有main入口函數。3.與C語言類似,java程序也可由命令行傳遞給main函數參數。4.基本窗口java程序的基本類是Frame。利用它能夠很方便地創建圖形用戶界面程序。
類是Java語言面向對象編程的基本元素,它定義了一個對象的結構和行爲。在Java程序裏,你要表達的概念封裝在某個類裏。一個類定義了一個對象的結構和它的功能接口,功能接口稱爲成員函數。當Java程序運行時,系統用類的定義建立類的實例,類的實例是真正的對象。類定義的通常形式以下:
classclassnameextendssuperclassname{typeinstance-variable1;typeinstance-variable2;.................................typeinstance-variableN;typemethodname1(parameter-list){method-body;}typemethodname2(parameter-list){method-body;}....................................................typemethodnameN(parameter-list){method-body;}}
這裏,classname和superclassname是合法的標識符。關鍵詞extends用來代表classname是superclassname派生的子類。有一個類叫作Object,它是全部Java類的根。若是你想定義Object的直接子類,你能夠省略extends子句,編譯器會自動包含它。下面是一個簡單的類的定義。
classUniversity{}
類名能夠做爲變量的類型來使用,若是一個變量的類型是某個類,那麼它將指向這個類的實例,稱爲對象實例。全部對象實例和它們的類型(某個類?copy;的子類的實例都是相容的。就象能夠把byte型的值賦給int型的變量同樣,你能夠把Object的子類的任何實例賦給一個Object型的變量。一個實例是類模板的單獨的拷貝,帶有本身的稱爲實例變量的數據集。每一個實例也能夠做爲一個對象。當你定義一個變量的類型是某個類時,它的缺省值是null,null是Object的一個實例。對象null沒有值,它和整數0不一樣。下面這個例子中,聲明變量u的類型是類University。
Universityu;
這裏,變量u的值是null。
Java經過在類定義的大括號裏聲明變量來把數據封裝在一個類裏。這裏的變量稱爲實例變量。下面的例子定義了一個叫作University的類,它有兩個實例變量:name和city。
classUniversity{Stringname,city;}
操做符new用來生成一個類的實例,下面這個例子生成了類University的一個實例,存放在變量u中。
Universityu=newUniversity();
在此例中,變量u指向這個對象,但並不真正包含這個對象。你能夠用多個變量指向同一個對象。下面的例子中,建立了一個University的對象,但建立了兩個指向它的變量。
Universityu=newUniversity();Universityu2=u;
對u2所指向的對象的任何改動都會對u所指向的對象起做用,由於它們是同一個對象。對u和u2的賦值只是把它們指向這個對象,既沒有分配內存,也沒有複製這個對象的任何部分。對u的再賦值只是簡單地去掉了u和原來對象的聯繫,並不影響對象自己,下面的例子說明了這種狀況。
Universityu=newUniversity();Universityu2=u;u=null;
儘管u被賦值爲null,u2仍指向原來由操做符new建立的對象。在前面的例子裏,咱們生成了一個對象而且指向了它兩次。這就容許兩個變量改變同一個對象。建立一個新的對象時,可直接對它的實例變量賦值。每一個對象都有它所屬類的實例變量的拷貝,每一個對象的實例變量都是和其餘對象的實例變量分離的,因此改變一個對象的實例變量不會影響其餘對象的實例變量。下面的例子建立了兩個University的對象,並對它們分別賦值:
classTwoUniversity{publicstaticvoidmain(Stringargs[]){Universityu1=newUniversity();Universityu2=newUniversity();u1.name="北?copy;大學";u1.city="北?copy;";u2.name="清華大學";u2.city="北?copy;";System.out.println("大學:"+u1.name+"城市:"+u1.city);System.out.println("大學:"+u2.name+"城市:"+u2.city);}}
這個例子建立了兩個University的對象,而且對它們的name、city分別賦了不一樣的值,這說明這兩個對象是真正分離的。下面是該程序運行後的輸出結果。
C:\>javaTwoUniversity大學:北?copy;大學城市:北?copy;大學:清華大學城市:北?copy;
點(.?copy;操做符用來接收一個對象的實例變量和成員函數。下面是用點操做符來接收實例變量的通常形式。
objectreference.variablename
這裏objectreference是一個對象實例,variablename是這個對象裏你想接收的實例變量。下面的程序段說明了怎樣用點操做符來給實例變量賦值。
u.name="北?copy;大學";u.city="北?copy;";
下面說明怎樣用點操做符來獲得實例變量的值。
System.out.println("大學:"+u.name+"城市:"+u.city);
經過向類University里加入一個成員函數main,咱們建立了一個完整的例子,它使用了new操做符來建立一個University,用點操做符來賦值,而後打印結果。
classUniversity{Stringname,city;publicstaticvoidmain(Stringargs[]){Universityu=newUniversity();u.name="北?copy;大學";u.city="北?copy;";System.out.println("大學:"+u.name+"城市:"+u.city);}}
運行這個程序後,就會獲得下面的結果。
C:\>javaUniversity大學:北?copy;大學城市:北?copy;
成員函數,是類的功能接口,是類定義裏的一個子程序,在類的定義裏和實例變量處於同一級別。你必須經過一個類的實例來調用成員函數。成員函數能夠不用點操做符而直接使用實例變量。成員函數帶有輸入參數,具備某種類型的返回值。成員函數定義的通常形式以下:
typemethodname(formal-parameter-list){method-body;}
這裏type指的是成員函數的返回值的類型,若是沒有返回值,就用無值(void?copy;類型。methodname能夠是任何合法的標識符,但不能與當前的類名相同。formal-parameter-list是用逗號分隔的類型、標識符對的序列。若是沒有參數,括號裏就是空的。仍是用咱們的University的例子,下面的成員函數用來初始化兩個實例變量。成員函數是在類的大括號?reg;內定義的,和實例變量所處的範圍相同。
classUniversity{Stringname,city;voidinit(Stringa,Stringb){name=a;city=b;}}
注意,咱們這裏直接給name和city賦值,而沒有象之前那樣用u1.name。這是由於每一個成員函數都在類的個別實例內執行。咱們建立的類的實例具備它本身的實例變量,因此成員函數可直接使用它們。
能夠用點(.?copy;操做符來調用一個類的實例的成員函數。成員函數調用的通常形式以下:
objectreference.methodname(parameter-list);
這裏,objectreference是指向某個對象的變量,methodname是objectreference所屬類的一個成員函數,parameter-list是用逗號分隔的變量或表達式的序列,它們要與該成員函數的定義的參數個數及類型匹配。在這個例子裏,咱們能夠對任何University對象調用成員函數init來給name和city賦值。下面的程序段說明了怎樣完成這個工做。
Universityu=newUniversity();u.init("北?copy;大學","北?copy;");
這個例子建立了University的一個實例,存放在u中。經過點操做符來調用這個實例的init成員函數,把"北?copy;大學"和"北?copy;"分別傳遞給參數a和b。在init成員函數內部,name和city直接指向u所指向的對象的實例變量。把name賦值爲"北?copy;大學",city賦值爲"北?copy;",而後返回。在這個例子裏,init被定義爲無值(void?copy;返回類型。在進行這個成員函數調用後,u指向這個name值和city值改變了的University對象。
Java有一個特殊的實例值叫this,它用來在一個成員函數內部指向當前的對象。在前面的例子裏,咱們調用u.init,一?copy;進入init成員函數內部,this就會指向u所指向的對象。在Java裏,在同一個範圍定義兩個相同名字的局部變量是不能夠的。有趣的是,局部變量、成員函數的參數能夠和實例變量的名字相同。前面咱們沒有用name和city做爲成員函數init的參數名字,由於這樣它們在成員函數的範圍裏就把實例變量name和city隱藏了,即name指向參數name,隱藏了實例變量name。this讓咱們能夠直接指向對象自己。下面是另外一個版本的init,用name和city做爲參數名字,用this來接收當前對象的實例變量。
voidinit(Stringname,Stringcity){this.name=name;this.city=city;}
下面是帶有新的init初始成員函數的TwoUniversity例子。
classUniversity{Stringname,city;voidinit(Stringname,Stringcity){this.name=name;this.city=city;}}
classTwoUniversityInit{publicstaticvoidmain(Stringargs[]){Universityu1=newUniversity();Universityu2=newUniversity();u1.init("北?copy;大學","北?copy;");u2.init("清華大學","北?copy;");System.out.println("大學:"+u1.name+"城市:"+u1.city);system.out.println("大學:"+u2.name+"城市:"+u2.city);}}
每建立一個類的實例都去初始化它的全部變量是乏味的。若是一個對象在被建立時就完成了全部的初始工做,將是簡單的和簡潔的。所以,Java在類裏提?copy;了一個特殊的成員函數,叫作構造函數(Constructor?copy;。一個構造函數是對象被建立時初始對象的成員函數。它具備和它所在的類徹底同樣的名字。一?copy;定義好一個構造函數,建立對象時就會自動調用它。構造函數沒有返回類型,即便是void類型也沒有。這是由於一個類的構造函數的返回值的類型就是這個類自己。構造函數的任務是初始一個對象的內部狀態,因此用new操做符建立一個實例後,馬上就會獲得一個清楚、可用的對象。下面這個例子裏,用構造函數取代了成員函數init。
classUniversity{Stringname,city;University(Stringname,Stringcity){this.name=name;this.city=city;}}
classUniversityCreate{publicstaticvoidmain(Stringargs[]){Universityu=newUniversity("北?copy;大學","北?copy;");System.out.println("大學:"+u.name+"城市:"+u.city);}}
new語句中類名後的參數是傳給構造函數的。
對於幾個意義相近的成員函數,有時使用相同的名字便於理解。所以,Java語言實現了成員函數重載,便可以建立幾個名字相同、參數不一樣的成員函數。成員函數重載提?copy;了Java的多態行爲。下面的例子用到了重載。
classUniversity{Stringname,city;University(Stringname,Stringcity){this.name=name;this.city=city;}University(){name="北?copy;大學";city="北?copy;";}}
classUniversityCreateAlt{publicstaticvoidmain(Stringargs[]){Universityu=newUniversity();System.out.println("大學:"+u.name+"城市:"+u.city);}}
這個例子建立了一個University對象,調用了第二個構造函數。下面是它的運行結果。
C:\>javaUniversityCreateAlt大學:北?copy;大學城市:北?copy;
一個構造函數能夠調用另外一個構造函數來建立實例。例如:
classUniversity{Stringname,city;University(Stringname,Stringcity){this.name=name;this.city=city;}University(){this("北?copy;大學","北?copy;");}}
第二個構造函數調用了第一個構造函數來完成實例的初始化。你也能夠用重載來建立通常的成員函數。下面這個例子裏有University類的兩個版本的samecity成員函數。samecity判斷一個大學是否在一個城市裏或一個大學和另外一個大學是否在同一個城市裏。一個成員函數用city做參數,另外一個用University對象做參數。
classUniversity{Stringname,city;University(Stringname,Stringcity){this.name=name;this.city=city;}booleansamecity(Stringcity){if(city.equals(this.city))returntrue;elsereturnfalse;}booleansamecity(Universityu){returnsamecity(u.city);}}
classUniversityCity{publicstaticvoidmain(Stringargs[]){Stringcity="上海";Universityu1=newUniversity("北?copy;大學","北?copy;");Universityu2=newUniversity("清華大學","北?copy;");System.out.println("u1="+u1.name+","+u1.city);System.out.println("u2="+u2.name+","+u2.city);System.out.println("city="+city);System.out.println("u1.samecity(u2)="+u1.samecity(u2));System.out.println("u1.samecity(city)="+u1.samecity(city));}}
下面是該程序的運行結果。
C:\>javaUniversityCityu1=北?copy;大學,北?copy;u2=清華大學,北?copy;city=上海u1.samecity(u2)=trueu1.samecity(city)=false
第二個基本的面向對象機制是繼承。繼承是關於有層次關係的類?reg;間的概念。一個類的後代能夠繼承它的祖先的全部變量和成員函數,就象建立本身的同樣。一個類的直接父親叫作它的超類(superclass?copy;。一?copy;你建立了一個象University這樣的類,建立它的子類是很簡單的。一個類的子類是它的繼承了實例變量和成員函數的特殊的版本。在這個例子裏,咱們把University類派生爲含有叫作country的第三個元素的子類。
classUniversityWorldextendsUniversity{Stringcountry;UniversityWorld(Stringname,Stringcity,Stringcountry){this.name=name;this.city=city;this.country=country;}UniversityWorld(){this("北?copy;大學","北?copy;","中國");}}
關鍵詞extends用來表示咱們要建立University的子類。name和city不需再在UniversityWorld中進行聲明,由於它們是從University中繼承的。Java容許在UniversityWorld中聲明變量name和city,但這會隱藏University中的name和city,是與使用子類的目的相矛盾的,應當避免。在UniversityWorld的實例中,name、city和country的地位是同樣的。
5.11super在UniversityWorld的例子裏,有一段代碼和它的超類University的重複,這段代碼是初始化name和city的,
this.name=name;this.city=city;
就象在University例子中用this指向第一個構造函數同樣,在Java裏有另外一個變量叫作super,它直接指向超類的構造函數。下面這個例子用super來初始化變量name和city,而後打印出這個對象的內容。
classUniversityWorldextendsUniversity{Stringcountry;UniversityWorld(Stringname,Stringcity,Stringcountry){super(name,city);//調用了構造函數University(name,city)this.country=country;}publicstaticvoidmain(Stringargs[]){UniversityWorldu=newUniversityWorld("北?copy;大學","北?copy;","中國");System.out.println("大學:"+u.name+"城市:"+u.city+"國家:"+u.country);}}
下面是運行結果。
C:\>javaUniversityWorld大學:北?copy;大學城市:北?copy;國家:中國
這個University的新的子類繼承了它的超類的成員函數samecity。但這個成員函數samecity判斷的是兩個城市的名字,這是不夠的,由於有可能兩個兩個名字同樣的城市屬於不一樣的國家,咱們要用同時判斷城市和國家的成員函數來覆蓋它。下面就是實現覆蓋的例子。
classUniversity{Stringname,city;University(Stringname,Stringcity){this.name=name;this.city=city;}booleansamecity(Stringcity){if(city.equals(this.city))returntrue;elsereturnfalse;}booleansamecity(Universityu){returnsamecity(u.city);}}
classUniversityWorldextendsUniversity{Stringcountry;UniversityWorld(Stringname,Stringcity,Stringcountry){super(name,city);this.country=country;}booleansamecity(Stringcity,Stringcountry){if(city.equals(u.city)&&country.equals(u.country))returntrue;elsereturnfalse;}booleansamecity(UniversityWorldother){returndistance(other.city,other.country);}}
classUniversityWorldCity{publicstaticvoidmain(Stringargs[]){Stringcity="上海";Stringcountry="中國";UniversityWorldu1=newUniversityWorld("北?copy;大學","北?copy;","中國");UniversityWorldu2=newUniversityWorld("清華大學","北?copy;","中國");System.out.println("u1="+u1.name+","+u1.city+","+u1.country);System.out.println("u2="+u2.name+","+u2.city+","+u2.country);System.out.println("city="+city+",country="+country);System.out.println("u1.samecity(u2)="+u1.samecity(u2));System.out.println("u1.samecity(city,country)="+u1.samecity(city,country));}}
下面是輸出結果。
C:\>javaUniversityWorldCityu1=北?copy;大學,北?copy;,中國u2=清華大學,北?copy;,中國city=上海,country=中國u1.samecity(u2)=trueu1.samecity(city,country)=false
當你用點操做符調用一個對象實例的成員函數時,對象實例所屬的類在編譯時要被檢查,以確保調用的成員函數在該類中是存在的。在運行時,對象實例能夠指向所聲明類型的子類的實例。在這?copy;狀況下,若是子類覆蓋了要調用的成員函數,Java就用實例來決定調用哪個成員函數。以下面的例子,兩個類是子類和超類的關係,子類覆蓋了超類的成員函數。
classA{voidcallme(){System.out.println("在A的callme成員函數裏");}}
classBextendsA{voidcallme(){System.out.println("在B的callme成員函數裏");}}
classDispatch{publicstaticvoidmain(Stringargs[]){Aa=newB();a.callme();}}
有趣的是,在成員函數main裏,咱們把變量a聲明爲類型A,而後把類B的一個實例存放到它上面。咱們在a上調用成員函數callme,Java編譯器肯定在類A確實有成員函數callme,可是在運行時,因爲a事實上是B的實例,因此調用B的callme,而不調用A的。下面是運行結果:
C:\>javaDispatch在B的callme成員函數裏
在缺省狀況下,全部的成員函數和實例變量均可以被覆蓋。若是你但願你的變量或成員函數再也不被子類覆蓋,能夠把它們聲明爲final。這意味着未來的實例都依賴這個定義。例如:
finalintFILE_NEW=1;finalintFILE_OPEN=2;finalintFILE_SAVE=3;fianlintFILE_SAVEAS=4;finalintFILE_QUIT=5;
final變量用大寫標識符是一個通常的約定。
若是你想要建立一個能夠在實例的外部調用的成員函數,那麼你只需聲明它爲靜態的(static?copy;,它就會正常運行。靜態成員函數只能直接調用其餘靜態成員函數,而不能以任何方式使用this或super。你也能夠把變量聲明爲靜態的。若是你想初始化一個靜態變量,你能夠用static聲明一個剛好在類調用時執行一次的程序塊。下面的例子是一個帶有一個靜態成員函數,幾個靜態變量,和一個靜態初始塊的類。
classStatic{staticinta=3;staticintb;staticvoidmethod(intx){System.out.println("x="+x);System.out.println("a="+a);System.out.println("b="+b);}static{System.out.println("靜態初始塊");b=a*4;}publicstaticvoidmain(Stringargs[]){method(42);}}
一?copy;這個類被調用,全部的靜態變量都被初始化,a被賦爲3,而後運行static塊,這將打印出一段消息,而且把b賦爲a*4,即12。而後解釋器調用main成員函數,它調用了成員函數method,參數x爲42。這三個println語句打印了兩個靜態變量a、b和局部變量x。下面是運行結果:
C:\>javaStatic靜態初始塊x=42a=3b=12
一個靜態成員函數能夠經過它所屬的類名來調用。象調用實例變量同樣,你能夠用點操做符經過類名來調用靜態成員函數和靜態變量。Java就是這樣實現了全局函數和全局變量。下面的例子裏,咱們建立了帶有一個靜態成員函數和兩個靜態變量的類。第二個類能夠經過名字直接來調用第一個類的靜態成員函數和靜態變量。
classstaticClass{staticinta=42;staticintb=99;staticvoidcallme(){System.out.println("a="+a);}}
classStaticByName{publicstaticvoidmain(Stringargs[]){StaticClass.callme();System.out.println("b="+staticClass.b);}}
下面是運行結果:
C:\>javastaticByNamea=42b=99
有時你須要定義一個給出抽象結構、但不給出每一個成員函數的完整實現的類。若是某個成員函數沒有完整實現,必需要由子類來覆蓋,你可把它聲明爲抽象(abstract?copy;型。含有抽象型成員函數的類必須聲明爲抽象的。爲了把一個類聲明爲抽象的,你只需在類定義的class關鍵詞前放置關鍵詞abstract。這?copy;類不能直接用new操做符生成實例,由於它們的完整實現尚未定義。你不能定義抽象的構造函數或抽象的靜態成員函數。抽象類的子類或者實現了它的超類的全部抽象的成員函數,或者也被聲明爲抽象的。下面例子是一個帶有抽象成員函數的類,其後是一個實現了該成員函數的類。
abstractclassA{abstractvoidcallme();voidmetoo(){system.out.println("在A的metoo成員函數裏");}}
classBextendsA{voidcallme(){System.out.println("在B的callme成員函數裏");}}
classAbstract{publicstaticvoidmain(Stringargs[]){Aa=newB();a.callme();a.metoo();}}
下面是運行結果:
C:\>javaAbstract在B的callme成員函數裏在A的metoo成員函數裏
1.類是Java語言面向對象編程的基本元素,它定義了一個對象的結構和功能。2.Java經過在類定義的大括號裏聲明變量來把數據封裝在一個類裏,這裏的變量稱爲實例變量。3.成員函數,是類的功能接口,是類定義裏的一個子程序,在類的定義裏和實例變量處於同一級別。
對一個優秀的應用程序來講,良好的圖形用戶接口是必不可少的。缺乏良好的圖形用戶接口,將會給用戶理解和使用應用程序帶來不少不便。很難想象用戶爲了學會使用一個應用程序,去記一大堆命令。Java提?copy;了生成一個良好的圖形用戶接口所須要的一?copy;基本元件:面板(Panel?copy;、按鈕(Button?copy;、標?copy;(Label?copy;、畫板(Canvases?copy;、滾動條(Scrollbar?copy;、列表框(List?copy;、文本域(TextField?copy;、文本區(TextArea?copy;。
面板提?copy;了創建應用程序的空間。你能夠把圖形元件(包括其餘面板?copy;放在一個面板上。Applet類提?copy;了一個基本的面板。
Java提?copy;了幾種佈局:順序佈局(FlowLayout?copy;、邊界佈局(BorderLayout?copy;和網格佈局(GridLayout?copy;。
順序佈局(FlowLayout?copy;是最基本的一種佈局,面板的缺省佈局就是順序佈局。順序佈局指的是把圖形元件一個接一個地?reg;平地放在面板上。下面是一個順序佈局的例子:
importjava.awt.*;importjava.applet.Applet;
publicclassmyButtonsextendsApplet{Buttonbutton1,button2,button3;publicvoidinit(){button1=newButton("肯定");button2=newButton("打開");button3=newButton("關閉");add(button1);add(button2);add(button3);}}
該程序生成的佈局以下:
圖6.1
邊界佈局包括五個區:北區、南區、東區、西區和中區。這幾個區在面板上的分佈規律是"上北下南,左西右東"。下面是一個邊界佈局的例子:
importjava.awt.*;importjava.applet.Applet;
publicclassbuttonDirextendsApplet{
ButtonbuttonN,buttonS,buttonW,buttonE,buttonC;
publicvoidinit(){setLayout(newBorderLayout());buttonN=newButton("?reg;");buttonS=newButton("火");buttonE=newButton("木");buttonW=newButton("金");buttonC=newButton("土");add("North",buttonN);add("South",buttonS);add("East",buttonE);add("West",buttonW);add("Center",buttonC);}}
下面是該程序運行的結果:
圖6.2
網格佈局把面板分紅一個個的網格,你能夠給出網格的行數和列數。下面是一個網格佈局的例子:
importjava.awt.*;importjava.applet.Applet;
publicclassbuttonGridextendsApplet{Buttonbutton1,button2,button3,button4,button5,button6,button7,button8;
publicvoidinit(){setLayout(newGridLayout(4,2));button1=newButton("乾");button2=newButton("坤");button3=newButton("艮");button4=newButton("震");button5=newButton("坎");button6=newButton("離");button7=newButton("巽");button8=newButton("兌");add(button1);add(button2);add(button3);add(button4);add(button5);add(button6);add(button7);add(button8);}}
下面是該程序運行的結果:
圖6.3
用戶點一下按鈕,就會有一個按鈕事件發生。你能夠經過覆蓋一個applet的action成員函數來捕捉按鈕事件。
publicbooleanaction(Evente,Objecto){if(e.targetinstanceofButton){system.out.println((string)o);}else{System.out.println("Non-buttonevent");}returntrue;}
Java提?copy;了標準的按壓式按鈕,同時也提?copy;了選擇式按鈕和標記式按鈕。
選擇式按鈕提?copy;了從幾個選項中選一個選項的功能。下面是從幾個市中選一個市的例子,市名放在選擇式按鈕中:
CityChooser=newChoice();
CityChooser.addItem("北?copy;");CityChooser.addItem("上海");CityChooser.addItem("天津");
add(CityChooser);
圖6.4
標記式按鈕的狀態做爲標記框事件的對象參數返回。下面是一個標記式按鈕的例子:
CheckboxfillStyleButton;fillStyleButton=newCheckbox("Solid");
publicbooleanaction(Evente,Objectarg){if(e.targetinstanceofCheckbox){System.out.println("Checkbox:"+arg);}returntrue;}
圖6.5
按鍵式按鈕是一組按鈕,用戶能夠選中其中一個,同時這一組中的其餘按鈕將被關閉。下面是一個按鍵式按鈕的例子:publicclassCheckBoxextendsApplet{CheckboxGroupcbg;
publicvoidinit(){cbg=newCheckboxGroup();add(newCheckbox("one",cbg,true));add(newCheckbox("two",cbg,false));add(newCheckbox("three",cbg,false));}}
圖6.6
Java語言的面向對象特性使咱們可以建立徹底自包含的按鈕。在自包含按鈕裏,你能夠在?copy;展按鈕類裏創建事件控制函數。下面是一個自包含按鈕的例子:
importjava.awt.*;importjava.applet.Applet;
classokButtonextendsButton{
publicokButton(){setLabel("Ok");}
publicbooleanaction(Evente,Objectarg){System.out.println("OKButton");returntrue;}}
publicclassbuttontestextendsApplet{okButtonmyOkButton;
publicvoidinit(){myOkButton=newokButton();add(myOkButton);}}
圖6.7
標?copy;是一種放到面板上的靜止的正文。下面是一個標?copy;的例子:importjava.awt.*;importjava.applet.Applet;
publicclasslabelextendsApplet{
publicvoidinit(){setLayout(newFlowLayout(FlowLayout.CENTER,10,10));Labellabel1=newLabel("你好!");Labellabel2=newLabel("另外一個標?copy;");add(label1);add(label2);}}
下面是運行結果:
圖6.8
列表框使用戶易於操做大量的選項。建立列表框的方法和Choicebutton有?copy;類似。列表框的全部條目都是可見的,若是選項不少,超出了列表框可見區的範圍,則列表框的旁邊將會有一個滾動條。首先,建立列表框:Listl=newList(4,false);這個成員函數建立了一個顯示4行的列表框。第二個參數"false"表示這個列表框是單選的,若是是"true",則表示是多選的。下面增長列表框的選項:
l.addItem("北?copy;大學");l.addItem("清華大學");l.addItem("吉林大學");l.addItem("復?copy;大學");l.addItem("南開大學");l.addItem("天津大學");l.addItem("南?copy;大學");add(l);
圖6.9
能夠用成員函數getSelectedItem()或getSelectedItems()來接收在列表框中被選的選項。在單選列表框裏,"雙擊"一個選項就能夠觸發一個可被action()成員函數捕捉到的事件。publicbooleanaction(Evente,Objectarg){...if(e.targetinstanceofList){System.out.println("Listentry:"+arg);}...}
對於多選列表框,要使你的選擇產生做用,須要使用其餘的外部事件。例如,你能夠使用按鈕事件:
圖6.10
publicbooleanaction(Evente,Objectarg){...if(e.targetinstanceofButton){...if("Ok".equals(arg)){string[]selected;selected=l.getSelectedItems();for(intI=0;I<selected.length;I++){System.out.println(selected[i]);}}}}
文本域通常用來讓用戶輸入象姓名、信用卡號這樣的信息,它是一個可以接收用戶的鍵盤輸入的小塊區域。
在建立文本域時,有四種類型?copy;你選擇:空的、空的而且具備指定長度、帶有初始文本內容的和帶有初始文本內容並具備指定長度的。下面是生成這四種文本域的代碼:
TextFieldtf1,tf2,tf3,tf4;
//空的文本域tf1=newTextField();//長度爲20的空的文本域tf2=newTextField(20);//帶有初始文本內容的文本域tf3=newTextField("你好");//帶有初始文本內容並具備指定長度的文本域tf4=newTextField("你好",30);add(tf1);add(tf2);add(tf3);add(tf4);
圖6.11
當用戶在文本域裏敲"回車"鍵時,就產生了一個文本域事件。象其餘事件同樣,你能夠以在成員函數action()中捕捉到這個事件。
publicbooleanaction(Evente,Objectarg){...if(e.targetinstanceofTextField){System.out.println("TextField:"+arg);}...}
文本區能夠顯示大段的文本。
與文本域相似,建立文本區時也有四種類型?copy;選擇,但若是指定文本區的大小,必須同時指定行數和列數。
TextAreata1,ta2;//一個空的文本區ta1=newTextArea();
//一個帶有初始內容、大小爲5x40的文本區ta2=newTextArea("你好!",5,40);
能夠用成員函數setEditable()來決定用戶是否可對文本區的內容進行編輯。
//使文本區爲只讀的ta2.setEditable(false)
圖6.12
能夠用成員函數getText()來得到文本區的當前內容。例如:System.out.println(ta1.getText());文本區自己不產生本身的事件。但你能夠用外部事件來接收文本區的內容:
publicbooleanaction(Evente,Objecto){if(e.targetinstanceofButton){if("send".equals(o)){StringtextToSend=ta1.getText();System.out.println("sending:"+textTosend);mySendFunction(textToSend);}}else{...}}
畫板可以捕捉到?copy;露事件、鼠標事件和其餘相似的事件。基本的畫板類不處理這?copy;事件,但你能夠?copy;展它來建立有你所需功能的畫板類。
importjava.awt.*;importjava.applet.Applet;
publicclasssuperGUIextendsApplet{...myCanvasdoodle;...publicvoidinit(){...//創建咱們的畫板doodle=newmyCanvas();doodle.reshape(0,0,100,100);leftPanel.add("Center",doodle);...}}
classmyCanvasextendsCanvas{publicvoidpaint(Graphicsg){g.drawRect(0,0,99,99);g.drawString("Canvas",15,40);}}
你能夠覆蓋通常的事件處理成員函數。下面是一個包含了mouseDown事件處理的例子:
importjava.awt.*;importjava.applet.Applet;
publicclasscanvasextendsApplet{
Buttonb1;
publicvoidinit(){//SetourlayoutasaBorderstylesetLayout(newBorderLayout(15,15));b1=newButton("Test");myCanvasc1=newmyCanvas(100,100);//addthecanvasandthebuttontotheappletadd("Center",c1);add("South",b1);}
publicbooleanaction(Evente,Objectarg){System.out.println("Event:"+arg);returntrue;}
publicbooleanmouseDown(Evente,intx,inty){System.out.println("Mouseworks:("+x+","+y+")");returntrue;}}
classmyCanvasextendsCanvas{privateintwidth;privateintheight;
publicmyCanvas(intw,inth){width=w;height=h;reshape(0,0,w,h);}
publicvoidpaint(Graphicsg){g.setColor(Color.blue);g.fillRect(0,0,width,height);}
publicbooleanmouseDown(Evente,intx,inty){if((x<width)&&(y<height)){System.out.println("Canvasmouseworks:("+x+","+y+")");returntrue;}returnfalse;//NotourmouseDown}}
在某?copy;程序中,須要調整線性的值,這時就須要滾動條。滾動條提?copy;了易於操做的值的範圍或區的範圍。
當建立一個滾動條時,必須指定它的方向、初始值、滑塊的大小、最小值和最大值。
publicScrollbar(intorientation,intinitialValue,intsizeOfSlider,intminValue,intmaxValue);
下面是一個例子:
ScrollbarredSlider;publicvoidinit(){redSlider=newScrollbar(Scrollbar.VERTICAL,0,1,0,255);add(redSlider);}
圖6.13
和其餘接口元件同樣,滾動條產生一個你能夠控制的事件,但和其餘事件不一樣,你必須直接使用成員函數handleEvent(),而不能使用成員函數action().
publicbooleanhandleEvent(Evente){if(e.targetinstanceofScrollbar){System.out.println("Scrollbar:"+((Scrollbar)e.target).getValue());returntrue;}returnsuper.handleEvent(e);}
若是你想顯示滑塊所在位置的值,須要加一個本身的文本域。下面是一個例子。
importjava.awt.*;importjava.applet.Applet;
publicclassredSliderextendsApplet{Scrollbarredslider;TextFieldredvalue;Labelredlabel;
publicvoidinit(){setLayout(newGridLayout(1,3));redslider=newScrollbar(Scrollbar.HORIZONTAL,0,1,0,255);redvalue=newTextField("0",5);redvalue.setEditable(false);redlable=newLabel("Red(0-255)");add(redlabel);add(redslider);add(redvalue);}
publicbooleanhandleEvent(Evente){if(e.targetinstanceofScrollbar){redvalue.setText(Integer.toString(((Scrollbar)e.target).getValue()));returntrue;}returnsuper.handleEvent(e);}
publicbooleanaction(Evente,Objectarg){System.out.println("Event"+arg);returntrue;}}
圖6.14
1.Java提?copy;了生成一個良好的圖形用戶接口所須要的一?copy;基本元件:面板(Panel?copy;、按鈕(Button?copy;、標?copy;(Label?copy;、畫板(Canvases?copy;、滾動條(Scrollbar?copy;、列表框(List?copy;、文本域(TextField?copy;、文本區(TextArea?copy;。2.大部分元件都有本身的事件,你能夠捕捉並處理它們。
多線程編程的含義是你可將程序任務分紅幾個並行的子任務。特別是在網絡編程中,你會發現不少功能是能夠併發執行的。好比網絡傳輸速度較慢,用戶輸入速度較慢,你能夠用兩個獨立的線程去完成這?copy;功能,而不影響正常的顯示或其餘功能。多線程是與單線程比較而言的,普通的WINDOWS採用單線程程序結構,其工做原理是:主程序有一個消息循環,不斷從消息隊列中讀入消息來決定下一步所要乾的事情,通常是一個子函數,只有等這個子函數執行完返回後,主程序才能接收另外的消息來執行。好比子函數功能是在讀一個網絡數據,或讀一個文件,只有等讀完這?copy;數據或文件才能接收下一個消息。在執行這個子函數過程當中你什麼也不能幹。但每每讀網絡數據和等待用戶輸入有不少時間處於等待狀態,多線程利用這個特色將任務分紅多個併發任務後,就能夠解決這個問題。
Java的設計思想是創建在當前大多數操做系統都實現了線程調度。Java虛擬機的不少任務都依賴線程調度,並且全部的類庫都是爲多線程設計的。實時上,Java支持Macintosh和Ms-dos的平臺?reg;因此遲遲未出來就是由於這兩個平臺都不支持多線程。Java利用多線程實現了整個執行環境是異步的。在Java程序裏沒有主消息循環。若是一個線程等待讀取網絡數據,它能夠運行但不中止系統的其餘線程執行。用於處理用戶輸入的線程大多時間是等待用戶敲鍵盤或擊鼠標。你還能夠使動畫的每一幀?reg;間停頓一秒而並不使系統暫停。一?copy;線程啓動後,它能夠被掛起,暫時不讓它執行。掛起的線程能夠從新恢復執行。任什麼時候間線程均可以被中止,被中止的線程就不能再從新啓動。Java語言裏,線程表現爲線程類,線程類封裝了全部須要的線程操做控制。在你內心,必須很清晰地區分開線程對象和運行線程,你能夠將線程對象看做是運行線程的控制面板。在線程對象裏有不少函數來控制一個線程是否運行,睡眠,掛起或中止。線程類是控制線程行爲的惟一的手段。一?copy;一個Java程序啓動後,就已經有一個線程在運行。你可經過調用Thread.currentThread函數來查看當前運行的是哪個線程。一?copy;你獲得一個線程的控制柄,你就能夠做一?copy;頗有趣的事情,即便單線程也同樣。下面這個例子讓你知道怎樣操縱當前線程。Filename:testthread
classtestthread{publicstaticvoidmain(Stringargs[]){Threadt=Thread.currentThread();t.setName("ThisThreadisrunning");System.out.println("Therunningthread:"+t);try{for(inti=0;i<5;i++){System.out.println("Sleeptime"+i);Thread.sleep(1000);}
}catch(InterruptedExceptione){System.out.println("threadhaswrong");}
}}
執行結果:javatestthreadTherunningthread:Thread[ThisThreadisrunning,5,main]Sleeptime0Sleeptime1Sleeptime2Sleeptime3Sleeptime4
一個線程並不激動人心,多個線程纔有實際意義。咱們怎樣建立更多的線程呢?咱們須要建立線程類的另外一個實例。當咱們構造了線程類的一個新的實例,咱們必須告訴它在新的線程裏應執行哪一段程序。你能夠在任意實現了啓動接口的對象上啓動一個線程。啓動接口是一個抽象接口,來表示本對象有一?copy;函數想異步執行。要實現啓動接口,一個類只須要有一個叫run的函數。下面是建立一個新線程的例子:
Filename:twothread.java
classtwothreadimplementsRunnable{twothread(){Threadt1=Thread.currentThread();t1.setName("Thefirstmainthread");System.out.println("Therunningthread:"+t1);Threadt2=newThread(this,"thesecondthread");System.out.println("creatanotherthread");t2.start();try{System.out.println("firstthreadwillsleep");Thread.sleep(3000);}catch(InterruptedExceptione){System.out.println("firstthreadhaswrong");}System.out.println("firstthreadexit");}publicvoidrun(){try{for(inti=0;i<5;i++){System.out.println("Sleeptimeforthread2:"+i);Thread.sleep(1000);}
}catch(InterruptedExceptione){System.out.println("threadhaswrong");}
System.out.println("secondthreadexit");}publicstaticvoidmain(Stringargs[]){newtwothread();}}
執行結果:javatwothread
Therunningthread:Thread[Thefirstmainthread,5,main]creatanotherthreadfirstthreadwillsleepSleeptimeforthread2:0Sleeptimeforthread2:1Sleeptimeforthread2:2firstthreadexitSleeptimeforthread2:3Sleeptimeforthread2:4secondthreadexit
main線程用newThread(this,"thesecondthread")建立了一個Thread對象,經過傳遞第一個參數來標明新線程來調用this對象的run函數。而後咱們調用start函數,它將使線程從run函數開始執行。
由於多線程給你提?copy;了程序的異步執行的功能,因此在必要時必須還提?copy;一種同步機制。例如,你想兩個線程通信並共享一個複雜的數據結構,你須要一種機制讓他們相互牽制並正確執行。爲這個目的,Java用一種叫監視器(monitor)的機制實現了進程間的異步執行。能夠將監視器看做是一個很小的盒子,它只能容納一個線程。一?copy;一個線程進入一個監視器,全部其餘線程必須等到第一個線程退出監視器後才能進入。這?copy;監視器能夠設計成保護共享的數據不被多個線程同時操做。大多數多線程系統將這?copy;監視器設計成對象,Java提?copy;了一種更清晰的解決方案。沒有Monitor類;每一個對象經過將他們的成員函數定義成synchronized來定義本身的顯式監視器,一?copy;一個線程執行在一個synchronized函數裏,其餘任何線程都不能調用同一個對象的synchronized函數。
一?copy;你的程序被分紅幾個邏輯線程,你必須清晰的知道這?copy;線程?reg;間應怎樣相互通信。Java提?copy;了wait和notify等功能來使線程?reg;間相互交談。一個線程能夠進入某一個對象的synchronized函數進入等待狀態,直到其餘線程顯式地將它喚醒。能夠有多個線程進入同一個函數並等待同一個喚醒消息。
在咱們的單線程應用程序裏,咱們並無看見線程,由於Java能自動建立和控制你的線程。若是你使用了理解Java語言的瀏覽器,你就已經看到使用多線程的Java程序了。你也許注意到兩個小程序能夠同時運行,或在你移動滾動條時小程序繼續執行。這並非代表小程序是多線程的,但說明這個瀏覽器是多線程的。多線程應用程序(或applet)能夠使用好幾個執行上下文來完成它們的工做。多線程利用了不少任務包含單獨的可分離的子任務的特色。每個線程完成一個子任務。
可是,每個線程完成子任務時仍是順序執行的。一個多線程程序容許各個線程儘快執行完它們。這種特色會有更好的實時輸入反應。
下面這個例子建立了三個單獨的線程,它們分別打印本身的"HelloWorld":
//Defineoursimplethreads.Theywillpauseforashorttime//andthenprintouttheirnamesanddelaytimesclassTestThreadextendsThread{privateStringwhoami;privateintdelay;
//Ourconstructortostorethename(whoami)//andtimetosleep(delay)publicTestThread(Strings,intd){whoami=s;delay=d;}
//Run-thethreadmethodsimilartomain()//Whenrunisfinished,thethreaddies.//Runiscalledfromthestart()methodofThreadpublicvoidrun(){//Trytosleepforthespecifiedtimetry{sleep(delay);}catch(InterruptedExceptione){}//NowprintoutournameSystem.out.println("HelloWorld!"+whoami+""+delay);}}/***Multimtest.Asimplemultithreadthestprogram*/publicclassmultitest{publicstaticvoidmain(Stringargs[]){TestThreadt1,t2,t3;//Createourtestthreadst1=newTestThread("Thread1",(int)(Math.readom()*2000));t2=newTestThread("Thread2",(int)(Math.readom()*2000));t3=newTestThread("Thread3",(int)(Math.readom()*2000));
//Starteachofthethreadst1.start();t2.start();t3.start();}}
程序啓動時老是調用main()函數,所以main()是咱們建立和啓動線程的地方:
t1=newTestThread("Thread1",(int)(Math.readom()*2000));
這一行建立了一個新的線程。後面的兩個參數傳遞了線程的名稱和線程在打印信息?reg;前的延時時間。由於咱們直接控制線程,咱們必須直接啓動它:t1.start();
若是建立線程正常,t1應包含一個有效的執行線程。咱們在線程的run()函數裏控制線程。一?copy;咱們進入run()函數,咱們即可執行裏面的任何程序。run()好象main()同樣。一?copy;run()執行完,這個線程也就結束了。在這個例子裏,咱們試着延遲一個隨機的時間(經過參數傳遞?copy;:sleep(delay);
sleep()函數只是簡單地告訴線程休息多少個毫秒時間。若是你想推遲一個線程的執行,你應使用sleep()函數。當線程睡眠是sleep()並不佔用系統資源。其它線程可繼續工做。一?copy;延遲時間完畢,它將打印"HelloWorld"和線程名稱及延遲時間。
咱們常常須要掛起一個線程而不指定多少時間。例如,若是你建立了一個含有動畫線程的小程序。也許你讓用戶暫停動畫至到他們想恢復爲止。你並不想將動畫線程仍調,但想讓它中止。象這種相似的線程你可用suspend()函數來控制:t1.suspend();
這個函數並不永久地中止了線程,你還可用resume()函數從新激活線程:t1.resume();
線程的最後一個控制是中止函數stop()。咱們用它來中止線程的執行:t1.stop();
注意:這並無消滅這個線程,但它中止了線程的執行。而且這個線程不能用t1.start()從新啓動。在咱們的例子裏,咱們歷來不用顯式地中止一個線程。咱們只簡單地讓它執行完而已。不少複雜的線程例子將須要咱們控制每個線程。在這種狀況下會使用到stop()函數。若是須要,你能夠測試你的線程是否被激活。一個線程已經啓動並且沒有中止被認爲是激活的。t1.isAlive()若是t1是激活的,這個函數將返回true.
下面是一個包含動畫線程的applet例子:
importjava.awt.*;importjava.awt.image.ImageProducer;importjava.applet.Applet;
publicclassatest3extendsAppletimplementsRunnable{Imageimages[];MediaTrackertracker;intindex=0;Threadanimator;
intmaxWidth,maxHeight;//Ouroff-screencomponentsfordoublebuffering.ImageoffScrImage;GraphicsoffScrGC;
//Canwepaintyes?booleanloaded=false;
//Initializetheapplet.Setoursizeandloadtheimagespublicvoidinit()[//Setupourimagemonitortracker=newMediaTracker(this);
//SetthesizeandwidthofourappletmaxWidth=100;maxHeight=100;
images=newImage[10];//Setupthedouble-bufferandresizeourapplettry{offScrImage=createImage(maxWidth,maxHeight);offScrGC=offScrImage.getGraphics();offScrGC.setColor(Color.lightGray);offScrGC.fillRect(0,0,maxWidth,maxHeight);resize(maxWidth,maxHeight);}catch(Exceptione){e.printStackTrace();}
//loadtheanimationimagesintoanarrayfor(inti=0;i<10;i++){StringimageFile=newString("images/Duke/T"+String.valueOf(i+1)+".gif");images[i]=getImage(getDocumentBase(),imageFile)://Registerthisimagewiththetrackertracker.addImage(images[i],i);}try{//Usetrackertomakesurealltheimagesareloadedtracker.waitForAll();}catch(InterruptedExceptione){}loaded=true;}
//Paintthecurrentframe.publicvoidpaint(Graphicsg){if(loaded){g.drawImage(offScrImage,0,0,this);}}
//Start,setupourfirstimagepublicvoidstart(){if(tracker.checkID(index)){offScrGC.drawImage(images[index],0,0,this);}animator=newThread(this);animator.start();}
//Run,dotheanimationworkhere.//Grabanimage,pause,grabthenext...publicvoidrun(){//GettheidofthecurrentthreadThreadme=Thread.currentThread();
//Ifouranimatorthreadexist,andisthecurrentthread...while((animatr!=null)&&(animator==me)){if(tracker.checkID(index)){//ClearthebackgroundandgetthenextimageoffScrGC.fillRect(0,0,100,100);offScrGCdrawImage(images[index],0,0,this);index++;//Loopbacktothebeginningandkeepgoingif(index>=images.length){index=0;}}//Delayheresoanimationlooksnormaltry{animator.sleep(200);}catch(InterruptedExceptione){}//Drawthenextframerepaint();}}}
多線程的一個重要特色是它們?reg;間能夠互相通信。你能夠設計線程使用公用對象,每一個線程均可以獨立操做公用對象。典型的線程間通信創建在生產者和消費者模型上:一個線程產生輸出;另外一個線程使用輸入。
buffer
讓咱們建立一個簡單的"AlphabetSoup"生產者和相應的消費者.
生產者將從thread類裏派生:classProducerextendsThread{privateSoupsoup;privateStringalphabet="ABCDEFGHIJKLMNOPQRSTUVWXYZ";
publicProducer(Soups){//Keepourowncopyofthesharedobjectsoup=s;}
publicvoidrun(){charc;//Throw10lettersintothesoupfor(inti=0;i<10;i++){c=alphabet.charAt((int)(Math.random()*26));soup.add(c);//printarecordofosradditionSystem.out.println("Added"+c+"tothesoup.");//waitabitbeforeweaddthenextlettertry{sleep((int)(Math.random()*1000));}catch(InterruptedExceptione){}}}}
注意咱們建立了Soup類的一個實例。生產者用soup.add()函數來創建字符池。
讓咱們看看消費者的程序:classConsumerextendsThread{privateSoupsoup;
publicConsumer(Soups){//keepourowncopyofthesharedobjectsoup=s;}
publicvoidrun(){charc;//Eat10lettersfromthealphabetsoupfor(intI=0;i<10;i++){//graboneletterc=soup.eat();//PrintouttheletterthatweretrievedSystem.out.println("Atealetter:"+c);//try{sleep((int)(Math.raddom()*2000));}catch(InterruptedExceptione){}}}}
同理,象生產者同樣,咱們用soup.eat()來處理信息。那麼,Soup類到底幹什麼呢?
Soup類執行監視兩個線程?reg;間傳輸信息的功能。監視是多線程中不可缺乏的一部分,由於它保持了通信的流?copy;。讓咱們看看Soup.java文件:classSoup{privatecharbuffer[]=newchar[6];privateintnext=0;//FlagstokeeptrackofourbufferstatusprivatebooleanisFull=false;privatebooleanisEmpty=true;
publicsyschronizedchareat(){//Wecan'teatifthereisn'tanythinginthebufferwhile(isEmpty==true){try{wait();//we'llexitthiswhenisEmptyturnsfalse}catch(InterruptedExceptione){}}//decrementthecount,sincewe'regoingtoeatoneletternext--;//Didweeatthelastletter?if(next==0){isEmpty=true;}//Weknowthebuffercan'tbefull,becausewejustateisFull=false;notify();//returnthelettertothethreadthatiseatingreturn(buffer[next]);}
//methodtoaddletterstothebufferpublicsynchronizedvoidadd(charc){//Waitarounduntilthere'sroomtoaddanotherletterwhile(isFull==true){try{wait();//ThiswillexitwhenisFullturnsfalse}catch(InterruptedExceptione){}}//addthelettertothenextavailablespotbuffer[next]=c;//Changethenextavailablespotnext++;//Arewefull;if(next==6){isFull=true;}isEmpty=false;notify();}}
soup類包含兩個重要特徵:數據成員buffer[]是私有的,功能成員add()和eat()是公有的。
數據私有避免了生產者和消費者直接得到數據。直接訪問數據可能形成錯誤。例如,若是消費者企圖從空緩衝區裏取出數據,你將獲得沒必要要的異常,不然,你只能鎖住進程。同步訪問方法避免了破壞一個共享對象。當生產者向soup里加入一個字母時,消費者不能吃字符,諸如此類。這種同步是維持共享對象完整性的重要方面。notify()函數將喚醒每個等待線程。等待線程將繼續它的訪問。
如今咱們有一個生產者,一個消費者和一個共享對象,怎樣實現它們的交互呢?咱們只須要一個簡單的控制程序來啓動全部的線程並確信每個線程都是訪問的同一個共享對象。下面是控制程序的代碼,SoupTest.java:classSoupTest{publicstaticvoidmain(Stringargs[]){Soups=newSoup();Producerp1=newProducer(s);Consumerc1=newConsumer(s);
p1.start();c1.start();}}
生產者/消費者模型程序常常用來實現遠程監視功能,它讓消費者看到生產者同用戶的交互或同系統其它部分的交互。例如,在網絡中,一組生產者線程能夠在不少工做站上運行。生產者能夠打印文檔,文檔打印後,一個標誌將保存下來。一個(或多個?copy;消費者將保存標誌並在晚上報告白天打印活動的狀況。另外,還有例子在一個工做站是分出幾個獨立的窗口。一個窗口用做用戶輸入(生產者?copy;,另外一個窗口做出對輸入的反應(消費者?copy;。
下面是一?copy;經常使用的線程類的方法函數列表:
類函數:如下是Thread的靜態函數,便可以直接從Thread類調用。
currentThread返回正在運行的Thread對象yield中止運行當前線程,讓系統運行下一個線程sleep(intn)讓當前線程睡眠n毫秒
對象函數:如下函數必須用Thread的實例對象來調用。
startstart函數告訴java運行系統爲本線程創建一個執行環境,而後調用本線程的run()函數。run是運行本線程的將要執行的代碼,也是Runnable接口的惟一函數。當一個線程初始化後,由start函數來調用它,一?copy;run函數返回,本線程也就終止了。stop讓某線程立刻終止,系統將刪除本線程的執行環境suspend與stop函數不一樣,suspend將線程暫停執行,但系統不破壞線程的執行環境,你能夠用resume來恢復本線程的執行resume恢復被掛起的線程進入運行狀態setPriority(intp)給線程設置優先級getPriority返回線程的優先級setName(Stringname)給線程設置名稱getName取線程的名稱
1.多線程是java語言的重要特色,java語言用Thread類封裝了線程的全部操做。2.線程的接口名爲Runnable3.線程?reg;間同步機制爲synchronized關鍵詞4.線程?reg;間通信靠wait與notify消息
"異常"指的是程序運行時出現的非正常狀況。在用傳統的語言編程時,程序員只能經過函數的返回值來發出錯誤信息。這易於致使不少錯誤,由於在不少狀況下須要知道錯誤產生的內部細節。一般,用全局變量errno來存儲"異常"的類型。這容易致使誤用,由於一個errno的值有可能在被處理?reg;前被另外的錯誤覆蓋掉。即便最優美的C語言程序,爲了處理"異常"狀況,也常求助於goto語句。Java對"異常"的處理是面向對象的。一個Java的Exception是一個描述"異常"狀況的對象。當出現"異常"狀況時,一個Exception對象就產生了,並放到產生這個"異常"的成員函數裏。
Java的"異常"處理是經過5個關鍵詞來實現的:try,catch,throw,throws和finally。用try來執行一段程序,若是出現"異常",系統拋出(throws?copy;一個"異常",你能夠經過它的類型來捕捉(catch?copy;它,或最後(finally?copy;由缺省處理器來處理。下面是"異常"處理程序的基本形式:
try{//程序塊}catch(ExceptionType1e){//對ExceptionType1的處理}catch(ExceptionType2e){//對ExceptionType2的處理throw(e);//再拋出這個"異常"}finally{}
在"異常"類層次的最上層有一個單獨的類叫作Throwable。這個類用來表示全部的"異常"狀況。每一個"異常"類型都是Throwable的子類。Throwable有兩個直接的子類。一類是Exception,是用戶程序可以捕捉到的"異常"狀況。咱們將經過產生它的子類來建立本身的"異常"。另外一類是Error,它定義了那?copy;一般沒法捕捉到的"異常"。要謹慎使用Error子類,由於它們一般會致使災難性的失敗。在Exception中有一個子類RuntimeException,它是程序運行時自動地對某?copy;錯誤做出反應而產生的。
"異常"對象是Java在運行時對某?copy;"異常"狀況做出反應而產生的。例如,下面這個小程序包含一個整數被0除的"異常"。
classExc0{publicstaticvoidmain(Stringargs[]){intd=0;inta=42/d;}}
當Java執行這個除法時,因爲分母是0,就會構造一個"異常"對象來使程序停下來並處理這個錯誤狀況,在運行時"拋出"(throw?copy;這個"異常"。說"拋出"是由於它象一個滾燙的馬鈴薯,你必須把它抓住並當即處理。程序流將會在除號操做符處被打斷,而後檢查當前的調用堆棧來查找"異常"。一個"異常"處理器是用來當即處理"異常"狀況的。在這個例子裏,咱們沒有編一個"異常"處理器,因此缺省的處理器就發揮做用了。缺省的處理器打印Exception的字符?reg;值和發生"異常"的地點。下面是咱們的小例子的輸出。
C:\>javaExc0java.lang.arithmeticException:/byzeroatExc0.main(Exc0.java:4)
一般咱們但願本身來處理"異常"並繼續運行。能夠用try來指定一塊預防全部"異常"的的程序。緊跟在try程序後面,應包含一個catch子句來指定你想要捕捉的"異常"的類型。例如,下面的例子是在前面的例子的基礎上構造的,但它包含一個try程序塊和一個catch子句。
classexc1{publicstaticvoidmain(stringargs[]){try{intd=0;inta=42/d;}catch(arithmeticexceptione){system.out.println("divisionbyzero");}}}
catch子句的目標是解決"異常"狀況,把一?copy;變量設到合理的狀態,並象沒有出錯同樣繼續運行。若是一個子程序不處理某個"異常",則返到上一級處理,直到最外一級。
在某?copy;狀況下,同一段程序可能產生不止一種"異常"狀況。你能夠放置多個catch子句,其中每一種"異常"類型都將被檢查,第一個與?reg;匹配的就會被執行。若是一個類和其子類都有的話,應把子類放在前面,不然將永遠不會到達子類。下面是一個有兩個catch子句的程序的例子。
classMultiCatch{publicstaticvoidmain(Stringargs[]){try{inta=args.length;System.out.println("a="+a);intb=42/a;intc[]={1};c[42]=99;}catch(ArithmeticExceptione){System.out.println("divby0:"+e);}catch(ArrayIndexOutOfBoundsExceptione){system.out.println("arrayindexoob:"+e);}}}
若是在程序運行時不跟參數,將會引發一個0作除數的"異常",由於a的值爲0。若是咱們提?copy;一個命令行參數,將不會產生這個"異常",由於a的值大於0。但會引發一個ArrayIndexOutOfBoundexception的"異常",由於整型數組c的長度是1,卻給c[42]賦值。下面是以上兩種狀況的運行結果。
C:\>javaMultiCatcha=0divby0:java.lang.arithmeticexception:/byzeroC:\>javaMutiCatch1a=1arrayindexoob:java.lang.ArrayIndexOutOfBoundsException:42
你能夠在一個成員函數調用的外面寫一個try語句,在這個成員函數內部,寫另外一個try語句保護其餘代碼。每當遇到一個try語句,"異常"的框架就放到堆棧上面,直到全部的try語句都完成。若是下一級的try語句沒有對某種"異常"進行處理,堆棧就會展開,直到遇到有處理這種"異常"的try語句。下面是一個try語句嵌套的例子。
classMultiNest{staticvoidprocedure(){try{intc[]={1}:c[42]=99;}catch(ArrayIndexOutOfBoundsexceptione){System.out.println("arrayindexoob:"+e);}}publicstaticvoidmain(Stringargs[]){try{inta=args.length;system.out.println("a="+a);intb=42/a;procedure();}catch(arithmeticExceptione){System.out.println("divby0:"+e);}}}
成員函數procedure裏有本身的try/catch控制,因此main不用去處理ArrayIndexOutOfBoundsException。
throw語句用來明確地拋出一個"異常"。首先,你必須獲得一個Throwable的實例的控制柄,經過參數傳到catch子句,或者用new操做符來建立一個。下面是throw語句的一般形式。
throwThrowableInstance;
程序會在throw語句後當即終止,它後面的語句執行不到,而後在包含它的全部try塊中從裏向外尋找含有與其匹配的catch子句的try塊。下面是一個含有throw語句的例子。
classThrowDemo{staticvoiddemoproc(){try{thrownewNullPointerException("de3mo");}catch(NullPointerExceptione){System.out.println("caughtinsidedemoproc");throwe;}}publicstaticvoidmain(Stringargs[]){try{demoproc();}catch(NullPointerExceptione){system.out.println("recaught:"+e);}}}
throws用來標明一個成員函數可能拋出的各類"異常"。對大多數Exception子類來講,Java編譯器會強迫你聲明在一個成員函數中拋出的"異常"的類型。若是"異常"的類型是Error或RuntimeException,或它們的子類,這個規則不起做用,由於這?copy;在程序的正常部分中是不期待出現的。若是你想明確地拋出一個RuntimeException,你必須用throws語句來聲明它的類型。這就從新定義了成員函數的定義語法:
typemethod-name(arg-list)throwsexception-list{}
下面是一段程序,它拋出了一個"異常",但既沒有捕捉它,也沒有用throws來聲明。這在編譯時將不會經過。
classThrowsDemo1{staticvoidprocedure()[System.out.println("insideprocedure");thrownewIllegalAccessException("demo");}publicstaticvoidmain(Stringargs[]){procedure();}}
爲了讓這個例子編譯過去,咱們須要聲明成員函數procedure拋出了IllegalAccessException,而且在調用它的成員函數main裏捕捉它。下面是正確的例子:
classThrowsDemo{staticvoidprocedure()throwsIllegalAccessException{System.out.println("insideprocedure");thrownewIllegalAccessException("demo");}publicstaticvoidmain(Stringargs[]){try{procedure();}catch(IllegalAccessExceptione){System.out.println("caught"+e);}}}
下面是輸出結果:
C:\>javaThrowsDemoinsideprocedurecaughtjava.lang.IllegalAccessException:demo
當一個"異常"被拋出時,程序的執行就再也不是線性的,跳過某?copy;行,甚至會因爲沒有與?reg;匹配的catch子句而過早地返回。有時確保一段代碼無論發生什麼"異常"都被執行到是必要的,關鍵詞finally就是用來標識這樣一段代碼的。即便你沒有catch子句,finally程序塊也會在執行try程序塊後的程序?reg;前執行。每一個try語句都須要至少一個與?reg;相配的catch子句或finally子句。一個成員函數返回到調用它的成員函數,或者經過一個沒捕捉到的"異常",或者經過一個明確的return語句,finally子句老是剛好在成員函數返回前執行。下面是一個例子,它有幾個成員函數,每一個成員函數用不一樣的途徑退出,但執行了finally子句。
classFinallyDemo{staticvoidprocA(){try{System.out.println("insideprocA");thrownewRuntimeException("demo");}finally{System.out.println("procA'sfinally");}}staticvoidprocB(){try{System.out.println("insideprocB");return;}finally{System.out.println("procB'sfinally");}}publicstaticvoidmain(Stringargs[]){try{procA();}catch(Exceptione);procB();}}
下面是這個例子的運行結果:
C:\>javaFinallyDemoinsideprocAprocA'sfinallyinsideprocBprocB'sfinally
1."異常"指的是程序運行時出現的非正常狀況。2.在"異常"類層次的最上層的類叫Throwable,它有兩個直接的子類:Exception和Error。3.Java的"異常"處理經過5個關鍵詞來實現:try,catch,throw,throws和finally。
全部的程序語言都提?copy;與本機文件系統交互的方式;Java也不例外。咱們將看看Java是怎樣處理標準文件輸入輸出的(包括stdin,stout,stderr)。當你在網絡上開發小程序時,你必須注意直接文件輸入輸出是不安全因素的關鍵。大多數用戶設置他們的瀏覽器,可以讓你自由的訪問他們的文件系統,但有?copy;不讓你訪問。固然,若是你開發你內部的應用程序,你也許須要直接訪問文件。
標準輸入輸出Unix的用戶,或其餘基於命令行系統的用戶(如DOS),都知道標準輸入輸出的含義。標準輸入文件是鍵盤,標準輸出文件是你的終端屏幕。標準錯誤輸出文件也指向屏幕,若是有必要,它也能夠指向另外一個文件以便和正常輸出區分。
系統類Java經過系統類達到訪問標準輸入輸出的功能。上面提到的三個文件在這個系統類中實現:StdinSystem.in做爲InputStream類的一個實例來實現stdin,你能夠使用read()和skip(longn)兩個成員函數。read()讓你從輸入中讀一個字節,skip(longn)讓你在輸入中跳過n個字節。
StoutSystem.out做爲PrintStream來實現stdout,你能夠使用print()和println()兩個成員函數。這兩個函數支持Java的任意基本類型做爲參數。
StderrSystem.err同stdout同樣實現stderr。象System.out同樣,你能夠訪問PrintStream成員函數。
這裏有一個例子,功能象Unix裏的cat或type:
importjava.io.*classmyCat{publicvoidmain(Stringargs[])throwsIOException{intb;intcount=0;while((b=System.in.read())!=-1){count++;System.out.print((char)b);}System.out.println();//blanklineSystem.err.println("counted"+count+"totalbytes.");}}
除了基本的鍵盤輸入和屏幕輸出外,咱們還須要聯繫文件的輸入輸出。咱們將學習下面幾個類:lFileInputStreamlDataInputStreamlFileOutputStreamlDataOutputStream
做爲參考,再列出一?copy;特定應用的類:lPipedInputStreamlBufferedInputStreamlPushBackInputStreamlStreamTokenizerlPipedOutputStreamlBufferedOutputStreamlRandomAccessFile
咱們不在此討論這?copy;類,但你能夠在JAVA_HOME/src/java/io目錄裏查看每一個類的成員函數定義。
在咱們進行文件操做時,須要知道一?copy;關於文件的信息。File類提?copy;了一?copy;成員函數來操縱文件和得到一?copy;文件的信息。
你可用下面三個方法來建立一個新文件對象:
FilemyFile;myFile=newFile("etc/motd");
或
myFile=newFile("/etc","motd");//moreusefulifthedirectoryorfilenamearevariables
或
FilemyDir=newfile("/etc");myFile=newFile(myDir,"motd");
這三種方法取決於你訪問文件的方式。例如,若是你在應用程序裏只用一個文件,第一種建立文件的結構是最容易的。但若是你在同一目錄裏打開數個文件,則第二種或第三種結構更好一?copy;。
一?copy;你建立了一個文件對象,你即可以使用如下成員函數來得到文件相關信息:
文件名lStringgetName()lStringgetPath()lStringgetAbslutePath()lStringgetParent()lbooleanrenameTo(FilenewName)
文件測試lbooleanexists()lbooleancanWrite()lbooleancanRead()lbooleanisFile()lbooleanisDirectory()lbooleanisAbsolute()
通常文件信息llonglastModified()llonglength()
目錄用法lbooleanmkdir()lString[]list()
這裏是一個獨立的顯示文件的基本信息的程序,文件經過命令行參數傳輸:
importjava.io.*;classfileInfo{FilefileToCheck;publicstaticvoidmain(Stringargs[])throwsIOException{if(args.length>0){for(inti=0;i<args.length;i++){fileToCheck=newFile(args[i]);info(fileToCheck);}}else{System.out.println("Nofilegiven.");}}publicvoidinfo(Filef)throwsIOException{System.out.println("Name:"+f.getName());System.out.println("Path:"=f.getPath());if(f.exists()){System.out.println("Fileexists.");System.out.print((f.canRead()?"andisReadable":""));System.out.print((f.cnaWrite()?"andisWriteable":""));System.out.println(".");System.out.println("Fileis"+f.lenght()="bytes.");}else{System.out.println("Filedoesnotexist.");}}}
InputStreamSequenceInputStreamFileInputStreamPipedInputStreamByteArrayInputStreamFileterInputStreamStringBufferInputStream
DataInputStreamLineNumberInputStreamPushbackInputStreamBufferedInputStream有好幾個類是專門用來處理文件輸入的。下面是文件輸入類的層次結構:
FileInputStream典型地表示一種順序訪問的文本文件。經過使用FileInputStream你能夠訪問文件的一個字節、幾個字節或整個文件。
爲一個文件打開輸入流FileInputStream,你必須將文件名或文件對象傳送給結構:
FileInputStreammyFileStream;myFileStream=newFileInputStream("/etc/motd");
你還能夠象下邊這樣從FileInputStream裏讀文件信息:
FilemyFile;FileInputSteammyFileStream;myFile=newFile("/etc/motd");myFileStream=newFileInputStream(myFile);
一?copy;FileInputStream輸入流打開,你就能夠從裏面讀取信息了。read()成員函數有如下幾種選項:
lintread()//readsonebyte//return-1atendofstreamlintread(byteb[])//fillsentirearray,ifpossible//returnsnumberofbytesread//returns-1ifendofstreamisreached
lintread(byteb[],intoffset,intlen)//readslenbytesintobstartingatb[offset]//Returnsnumberofbytesread,//or-1ifendofstreamisreached.
當你完成一個文件的操做,你可選兩種方法關閉它:顯式關閉和隱式關閉,隱式關閉是自動垃圾回收時的功能。
顯式關閉以下:myFileStream.close();
若是文件的訪問權限足夠,你能夠在TextArea對象裏顯示文件內容。
下面是顯示文件的程序片段:
FileInputStreamfis;TextAreata;publicvodinit(){byteb[]=newbyte[1024];intI;//makeitbigenoughorwaituntilyou//knowthesizeofthefileStrings;try{fis=newFileInputStream("/etc/motd");}catch(FileNotFoundExceptione){/*dosomethingappropriate*/}try{I=fis.read(b);}catch(IOExceptione){/*dosomethingappropriate*/}s=newString(b,0);ta=newTextArea(s,5,40);add(ta);}
DataInputStreams與FileInputStreams差很少。Data流能夠直接讀任意一種變量類型,如浮點數,整數和字符等。通常來講,對二進制文件使用DataInputStream流。
打開和關閉DataInputStreams對象時,其方法與FileInputStreams相同:
DataInputStreamsmyDataStream;FileInputStreamsmyFileStream;
//getafilehandlemyFileStream=newFileInputStream("/usr/db/stock.dbf");//open,or"chain"adatainputfilemyDataStream=newDataOutputStream(myFileStream);
//Nowwecanusebothinputstreamstoaccessourfile//j(Ifwewantto...)myFileStream.read(b);I=myDataStrea.readInt();
//closethedatafrielexplicityly//Alwaysclosethe"topmost"filestreammyDataStream.close();myFileStream.close();
當你從DataInputStreams流裏訪問文件時,你能夠使用與FileInputStream流相同的成員函數read()。但你也能夠使用其餘訪問方法來讀取不一樣種類的數據:
lbytereadByte()lintreadUnsignedByte()lshortreadShort()lintreadUnsighedShort()lcharreadChar()lintreadIntllongreadLong()lfloatreadFloat()ldoublereadDouble()lStringreadLine()
以上每個成員函數都讀取相應的數據對象。象StringreadLine()成員函數,你可以使用\n,\r,\r\n,或EOF做爲字符?reg;結束符。
讀一個長整型,例如:
longserialNo;...serialNo=myDataStream.readLong();
除了基本文件訪問外,Java還提?copy;了經過網絡使用URL訪問對象的功能。在下面這個例子裏,咱們用getDocumentBase()成員函數並顯式指定URL對象來訪問聲音和圖象。
StringimageFile=newString("images/Duke/T1.gif");images[0]=getImage(getDocumentBase(),imageFile();
若是咱們願意,能夠直接使用URL:URLimageSource;imageSource=newURL("http://555-1212.com/~info");images[0]=getImage(imageSource,"Duke/T1.gif");
咱們能夠爲相應的URL打開輸入流。例如,下面的程序裏包括一個數據文件:InputStreamis;bytebuffer[]=newbyte[24];is=newURL(getDocumentBase(),dataname).openStream();
如今咱們能夠使用is,就象使用FileInputStream對象同樣:is.read(buffer.0,buffer.length);
注意:有?copy;用戶設置了他們的瀏覽器安全屬性,能夠不讓你的程序訪問他們的文件。
上面咱們談到了讀數據,那麼如何實現寫數據呢?象輸入流同樣,輸出流也有相似的層次結構:
OutputStream
FileOutputStreamPipedOutputStreamByteArrayOutputStreamFilterOutputStream
DataOutputStreamPrintStreamBufferedOutputStream
咱們將分析FileOutputStream和DataOutputStream類來完成咱們碰到的輸出流問題。其它的輸出流包含了更多的信息和成員函數。象輸入流的源文件同樣,這?copy;文件在$JAVA_HOME/src/java/io目錄下。
FileOutputStream對象用於向一個文本文件寫數據。象輸入文件同樣,你得先打開這個文件後才能寫這個文件。
要打開一個FileOutputStream對象,象打開一個輸入流同樣,你能夠將字符?reg;或文件對象做爲參數:FileOutputStreammyFileStream;myFileStream=newFileOutputStream("/etc/motd");
象輸入流同樣,你也可這樣使用:FilemyFile;FileOutputStreammyFileStream;myFile=newFile("/etc/motd");myFileStream=newFileOutputStream(myFile);
一?copy;文件被打開,你即可以使用write()函數向文件裏寫一?copy;數據。就象輸入流的read()函數同樣,你可有三種方法:lvoidwrite(intb);//writesoutonebytelvoidwrite(byteb[]);//writesoutentirearraylvoidwrite(byteb[],intoffset,intlength);//writeoutlengthbytesofb[],startingatb[offset]
關閉輸出流和關閉輸入流方法同樣,你能夠使用顯式方法:myFileStream.close();你也可讓系統自動關閉它。
下面有一個程序,讓用戶輸入一?copy;姓名和電話號碼。每個姓名和號碼將加在文件裏。用戶經過點"Done"按鈕來告訴系統整個列表已輸入完畢。
一?copy;用戶輸入完整個列表,程序將建立一個輸出文件並顯示或打印出來。例如:
555-1212,Tom123-456-7890,PeggyL.234-5678,Marc234-5678,Ron876-4321,Beth&Brian33.1.42.45.70,Jean-Marc
下面是程序的源代碼:importjava.io.*;
//Phones.java//Asimpledatabasecreationprogram
classPhones{staticFileOutputStreamfos;publicstaticfinalintlineLength=81;publicstaticvoidmain(Stringargs[])throwsIOExciption{byte[]phone=newbyte[lineLength];byte[]name=newbyte[lineLenght];intI;fos=newFileOutputStream("phone.numbers");while(true){System.err.println("Enteraname(enter'done'toquit)");readLine(name);if("done".equalsIgnoreCase(newString(name,0,0,4))){break;}System.err.println("Enterthephonenumber");readLine(phone);for(i=0;phone[i]!=0;i++){fos.write(phone[i]);}fos.write(',');for(i=0;name[i]!=0;I++){fos.write(name[i]);}fos.write('\n');}fos.close();}
privatestaticvoidreadLine(byteline[])throwsIOException{inti=0,b=0;
while((i<lineLengh-1))&&((b=System.ini.read())!='\n')){line[i++]=(byte)b;}line[i]=(byte)0;}}
若是你處理的數據量不少,或向文件寫不少次小數據,你能夠使用一個BufferedOutput流。BufferedOutput流提?copy;和FileOutputStream類一樣的寫操做方法,但全部輸出所有存放在一個緩衝區裏。當你填滿緩衝區,它將一次性寫入磁盤。或者你主動將緩衝區寫入磁盤。
若是要建立一個BufferedOutput流,首先須要一個FileOutput流。而後將緩衝區連接到FileOutput流:FileOutputStreammyFileStream;BufferedOutputStreammyBufferStream;//getafilehandlemyFileStream=newFileOutputStream("/usr/db/stock.dbf");//chainabufferedoutputstreammyBufferSSstream=newBufferedOutputStream(myFileStream);
和普通FileOutput流同樣,向BufferedOutput流裏的每一次寫操做和寫入磁盤操做並非一一對應的。要想在程序結束?reg;前將緩衝區裏的數據寫入磁盤,除非填滿緩衝區,不然只有顯式調用flush()函數://forceleft-overdatatodiskmyBufferStream.flush();//closethedatafileexplicitly//Alwaysclosethe"topmost"filestreammyBufferStream.close();myFileStream.close();
和DataInputStream對應,Java還提?copy;了DataOutput流。使用DataOutput流,咱們能夠向文件寫入二進制數據。
打開和關閉DataOutput流對象與打開、關閉FileOutput流對象方法同樣:DataOutputStreammyDataStream;FileOutputStreammyFileStream;BufferedOutputStreammyBufferStream;
//getafilehandlemhyFileStream=newFileOutputStream("/usr/db/stock.dbf");//chainabufferedoutputstream(forefficiency);myBufferStream=newBufferedOutputStream(myFileStream);//chainadataoutputfilemyDataStream=newDataOutputStream(myBufferStream);
//Nowwecanusebothinputstreamstoaccessourfile//(iiIfwewantto...)myBufferStream.write(b);myDataStream.writeInt(i);
//closethedatafileexplicitly//Alwayscolsethe"topmost"filestreammyDataStream.close();myBuffersStream.close();myFileStream.close();
FileOutput流裏的write()函數各類方法都適用於DataOutput流。你還能夠看到DataInput流的相似函數方法:lvoidwriteBoolean(booleanv)lvoidwriteByte(intv)lvoidwriteShort(intv)lvoidwriteChar(intv)lvoidwriteInt(intv)lvoidwriteFloat(floatv)lvoidwriteDouble(doublev)lvoidwriteBytes(strings)lvoidwriteChars(strings)
對字符?reg;來講,有兩種選擇:byte和char。記住byte是8位數據而char是16位數據。若是你想利用Unicode字符的優勢,你應使用writeChars()函數。
在使用二進制數據輸出時經常使用的另一個函數是size()。這個函數返回寫入文件數據的總字節數。你也可用size()函數將數據文件分紅四字節爲單位的塊,例如:...intbytesLeft=myDataStream.size()%4;for(intI=0;I<bytesLeft;I++){myDataStrea.write(0);}...
咱們讀文件經常不是從頭到尾順序讀的。你也許想將一文本文件看成一個數據庫,讀完一個記錄後,跳到另外一個記錄,它們在文件的不一樣地方。Java提?copy;了RandomAccessFile類讓你操做這種類型的輸入輸出。
打開隨機訪問文件有兩種方法:l用文件名myRAFile=newRandomAccessFile(Stringname,Stringmode);l用文件對象myRAFile=newRandomAccessFile(Filefile,Stringmode);
mode參數決定了訪問文件的權限,如只讀'r'或讀寫'wr'等。
例如,咱們打開一個數據庫更新數據:RandomAccessFilemyRAFile;myRAFile=newRandomAccessFile("/usr/db/stock.dbf","rw");
RandomAccessFile對象的讀寫操做和DataInput/DataOutput對象的操做方式同樣。你能夠使用在DataInputStream和DataOutputStream裏出現的全部read()和write()函數。
還有幾個函數幫助你在文件裏移動指針:llonggetFilePointer();返回當前指針lvoidseek(longpos);將文件指針定位到一個絕對地址。地址是相對於文件頭的偏移量。地址0表示文件的開頭。llonglength();返回文件的長度。地址"length()"表示文件的結尾。
你能夠使用隨機訪問文件來設置成增長信息模式:myRAFile=newRandomAccessFile("/tmp/java.log","rw");myRAFile.seek(myRAFile.length());//Anysubsequentwrite()swillbeappendedtothefile
9.13.4追加信息例子下面是一個在已存在文件後面追加字符?reg;的例子:importjava.io.IOException;importjava.io.RandomAccessFile;
classraTest{publicstaticvoidmain(Stringargs[])throwsIOException{RandomAccessFilemyFAFile;Strings="InformationtoAppend\nHimom!\n";//openourrandomaccessfilemyRAFile=newRandomAccessFile("/tmp/java.log","rw");//movetotheendofthefilemyRAFile.seek(myRAFile.length());//Startappending!myRAFile.writeBytes(s);
myRAFile.close();}}
1.Java經過系統類達到訪問標準輸入輸出的功能。2.你能夠建立、讀、寫文件。