第9章 Java進階編程
學習導讀
本章將介紹Java語言中的一些進階特性:異常處理、多線程機制、流式IO以及網路通訊,其中每個部分都能擴充成一個專題。讀者在本章學習到的只是其中很小的一部分,但是能夠從中瞭解一些基本概念和基本操作,為今後的深入學習打下基礎。異常處理提供對錯誤的捕捉和處理機制;多線程機制使得程式的多個子任務能夠“同時”執行;流式IO提供了對輸入輸出的讀寫機制;網路通訊特性允許Java程式通過Socket實現底層通訊,並利用Java提供的向外延展群組件實現高層服務。
教學重點與痛點:
●異常處理、多線程機制、流式IO以及網路通訊的基本概念和模型
●異常的捕獲和處理
●多線程的實現及Runnable介面的應用
●流式輸入輸出的操作、檔案讀寫及隨機訪問
●基於Socket的連線導向的網路底層通訊及高層服務
9.1 異常處理
在Java中,提供了錯誤捕捉和處理機制,即異常處理機制。在程式運行過程中發生錯誤時,Java允許其不按照正常路徑完成任務,由發現錯誤的方法拋出封裝了錯誤資訊的對象(異常)到其調用程式,發出已經發生問題的訊號,然後立即退出;而且,程式並不在調用該方法的代碼處繼續執行,而是由異常處理機制開始搜尋一個能夠處理這種特定錯誤情況的異常處理器。
異常(Exception)也稱例外、差錯、違例等,是特殊的運行錯誤對象,它對應於java中的特定的運行錯誤處理機制。Java中引入了異常和異常類。
Java中的異常處理
一般來講錯誤可以在編譯時間捕獲,但是有些錯誤得在運行期間解決,比如除0等。要考慮到這些方面對可能發生的例外狀況事件作出相應的處理。
Java採用物件導向的方法來處理異常如果一個方法在運行時產生了異常,則這個方法產生代表該異常的一個對象,並把它交給運行時系統,系統會尋找相應的代碼來處理異常。
把產生異常對象並把它交給運行時系統的過程稱為拋出(throw)異常。系統在方法的調用棧中尋找直到找到包含相應異常處理的方法為止,這一過程稱為捕獲(catch)異常。
Throwable與Exception
The Throwable class is the superclass of all
errors and exceptions in the Java language.
Java中的異常類都是java.lang.Throwable的子類,派生兩個子類:Error and Exception。其中Error系統保留,Exception供應用程式使用。
Error:JVM系統內部錯誤、資源耗盡等,應用程式不處理Error類。
Exception:其他編程錯誤等一般性問題。一般所說的異常都指Exception及其子類。
Exception也有自己的方法和屬性。它有兩個構造方法:
public Exception();
public Exception(String s);
s通常是對該例外所對應的錯誤的描述。
Exception類還繼承了父類的方法,常用的:
public String toString(); 返回描述當前Exception 類資訊的字串。
public void printStackTrace(); 在當前的輸出上列印當前例外對象的堆棧使用軌跡,即程式使用執行了哪些對象、類,使得產生了例外。
系統定義的異常:
Exception的子類分為RuntimeException和非RuntimeException。
前者是一種設計和實現時的問題,如數組越界等,這種異常可以通過編程避免。
後者是在程式運行過程中由環境原因造成的異常。
使用者定義的異常
這類異常是由Exception或其子類所派生出來的類,用於處理與具體應用相關的異常。
異常處理
捕獲和處理
異常處理機制:
1、程式執行時出現異常,會自動產生一個異常類對象,該對象被提交給java的運行是系統,此過程稱為拋出異常,也可由程式強制執行。
2、系統接收到異常對象,會尋找處理這一異常的代碼並把當前異常對象交給它處理,該過程稱為捕獲異常。
3、如果系統找不到可以捕獲異常的方法,則運行時系統將終止,程式也會退出。
異常處理
拋出異常
所有的系統定義的運行異常都可以由系統自動拋出。
使用者程式自訂的異常不能由系統自動拋出,必須 throw語句定義何種情況算是產生了異常對應的錯誤,並且應該拋出這個異常類的對象。
throw 異常對象;
註:1、throw語句一般被定義為滿足一定條件時執行。如放在if分支中。
2、使用throw語句的方法,或者調用其他類的有異常拋出的方法時,應在方法頭定義中增加throws異常類名列表。
捕獲異常
當一個異常被拋出時,應該由專門的語句來接收這個異常對象。一個異常類的對象被捕獲後,程式就會跳轉至專門的異常處理語句塊,或者直接跳出當前程式和JVM。
異常對象是依靠catch語句塊(異常處理語句塊)來不活和處理異常的。
try{
……
}
catch(異常類名 異常形參名){
……
}
catch(異常類名 異常形參名){
……
} finally{
……
}
catch語句可以有一個或多個,緊跟在try語句塊後面,每個catch必須有一個try對應。
多異常的處理:
在實際應用中,一個try塊可能產生多種不同的異常,如果希望採取不同的方法來處理,就需要使用多異常處理機制。
多異常處理通過在一個try塊後面定義若干個catch塊來實現。當try拋出異常時,程式首先轉向第一個catch,查看是否能夠接收該異常。
接收異常指異常對象與catch的參數的匹配:
1、異常對象與參數屬於相同的例外類
2、異常對象屬於參數例外類的子類
3、異常對象實現了參數所定義的介面
如果被第一個catch接收,則程式直接執行這個塊,完畢後退出當前方法,try塊中沒有執行的語句及其他catch塊將被忽略。否則,一次匹配其他的catch塊,直到找到一個可以接收該異常對象的catch塊。
如果所有的catch都不匹配,則程式會返回到調用該方法的上層方法。如果這個上層方法定義了與所產生的異常相匹配的catch塊,則會跳到這個catch塊,否則繼續回溯。如果最終都沒有找到,則這是系統一般會終止程式,然後列印除相關的
異常資訊。
如果沒有異常發生,那麼所有的catch塊都會被忽略。
設計catch
由於catch 塊是按照先後排列順序執行的,所以一般來講將處理較為具體的和常見的異常的catch 塊放在前面,而與多種異常相匹配的catch 塊放在靠後的位置。
catch 塊根據異常的不同執行不同的操作,一般來講是將異常和錯誤的資訊列印出來,方便修改偵錯工具。
finally
finally語句為異常處理提供一個統一的介面,也就是說,無論是否發生異常,程式都要執行一段代碼,那麼將這段代碼放在finally語句塊中。
finally語句是可選的,try後面至少要有一個catch或者finally塊。
異常處理
覆蓋方法中聲明異常
如果一個類的方法中聲明了throw異常,則它的子類如果要覆蓋該類的方法的時候,也可以拋出(throw)異常。
但是要注意的是:子類方法拋出的異常只能是父類方法所拋出的異常的同類或者子類,不能拋出比父類更一般的異常。
異常處理
import java.io.*;
class A{
public void methodA() throws IOException
{……}
}
class B1 extends A{
public void methodA() throws FileNotFoundException
{……}
}
class B2 extends A{
public void methodA() throws Exception //出錯
{……}
}
拋出異常補充
有時候使用者程式自己定義的異常不能依靠系統自動拋出,則必須依靠throw。比如有時try內代碼不會產生異常,而使用者自己希望它產生異常,則可以用throw拋出異常。
一種是在方法中自己處理髮生的異常,另一種是在方法之外處理異常.
建立自己的異常類:
Java軟體包中儘管已經有了很多現成的異常,但在實際編程時,也需要建立自己的異常類來處理某個應用所特有的運行錯誤
建立使用者定義的異常時,要完成:
1、聲明一個新的異常類,這個類必須繼承系統現有的異常類。
2、為新的異常類定義屬性和方法,或者重載父類的方法,使得這些屬性和方法能體現該類所對應的錯誤的資訊。
9.2 Java多線程機制
建立線程:將需要獨立啟動並執行子任務代碼放到從Thread類派生出來的類的run方法中。然後在主線程中原先調用該子任務的地方先建立一個該線程類的執行個體,再調用線程類中的start方法啟動線程。
前面介紹了如何通過建立自己的線程類來實現多線程,即將線程類(Thread)與程式的主類(Main)分離。
class CounterSubTask extends Thread
{ ...
public void run()
{ ... }
}
public class CounterMultiThread
{ public static void main(String[] args)
{ ... }
}
如果CounterSubTask類,也就是run方法所在的類已經擁有另一個超類,那將無法繼承Thread類,此時如何?多線程呢?典型的情況是,對於一個GUI程式,需要從JFrame或JApplet類派生。這時,雖然不能繼承Thread類,但是可以通過實現Runnable介面來實現多線程。
9.3 流式輸入輸出與檔案處理
在Java中,應用程式所需要讀入的資料和寫出的資料是通過I/O操作實現的。這些讀寫資料的源或目的包括檔案、記憶體、網路連接等,其中,最常用的是檔案。
Java中的輸入輸出資料流可以分為兩大類:輸入資料流和輸出資料流。輸入資料流是能夠讀取位元組的對象,而輸出資料流是能夠寫位元組序列的對象。最初設計的輸入輸出類是面向位元組流的,即能夠支援8位的位元組流,分別由派生自抽象類別InputStream和OutputStream的類層次來表示。但是隨著對國際化支援的需求出現,面向位元組的流不能很好地處理使用Unicode(每個字元使用兩個位元組)的資料,因此引入了派生自抽象類別Reader和Writer的類層次,用於讀寫雙位元組的Unicode字元,而不是單位元組字元。
9.3.1 Java輸入輸出類庫繼承關係
9.3.2基於標準輸入輸出的IO操作
在Java裡,還提供了“標準輸入資料流”和“標準錯誤輸出資料流”,分別對應於System.in和System.err。System.out和System.err已經被封裝成PrintStream對象,因此具有強大的輸出功能;但是System.in卻仍然是原始的InputStream,需要在使用的時候進行封裝。
9.3.3 檔案讀寫及隨機訪問
檔案是儲存在磁碟等二級存放裝置上的資料,由記錄組成,檔案中的一行可以看作是一條記錄。對檔案的讀寫和標準輸入輸出是十分類似的,需要注意的是要採用專門對檔案操作的流,並應該在合適的時候關閉流,否則系統資源無法得到釋放。對於輸出資料流,如果不執行關閉流的操作,則緩衝區的資料將有可能沒有寫入檔案,造成檔案損壞。
9.3.4 Java的檔案管理
對於檔案或目錄的其他動作,如重新命名、刪除、列表顯示等,需要使用Java的檔案管理File類。
在Java中,檔案和目錄都是用File對象來表示的,建立和區分方法:先建立一個File對象,並指定檔案名稱或目錄名,若指定檔案名稱或目錄名不存在,則File對象的建立並不會建立一個檔案或目錄;需要用createNewFile方法或mkdir方法來分別建立檔案或目錄。區分File對象代表的是檔案還是目錄,可以通過isFile方法和isDirectory方法來判斷。
9.4 Java網路通訊
傳統的網路編程是一項非常細節化的工作,程式員必須處理和網路有關的大量細節,如各種協議,甚至要理解網路相關的硬體知識。而Java則將底層的網路通訊細節予以屏蔽,使得使用的編程模型是一個檔案模型,也就是說,可以象操作流一樣來操作網路資料轉送。另外,由於在網路連接中,通常都需要一個伺服器同時為多個用戶端服務,因此Java的多線程機制也大派用場。
9.4.1網路基礎知識及Java網路模型
9.4.2不需連線的資料報
對於類似傳輸速度更重要的應用,使用不需連線的資料報協議UDP,即“使用者資料包通訊協定”。UDP並不刻意追求資料包會完全發送出去,也不能擔保它們抵達的順序與它們發出時一樣,因此,這是一種“不可靠協議”。由於其速度比TCP快得多,所以還是能夠在很多應用中使用。
Java對資料報的支援與它對TCP通訊端的支援大致相同,使用DatagramSocket類來表示不需連線的socket,接收和發送資料報。接收和要發送的資料報內容儲存在DatagramPacket對象中。
UDP也有自己的連接埠,和TCP連接埠是相互獨立的。也就是說,可以在連接埠8080同時運行一個TCP和UDP服務程式,兩者之間不會產生衝突。
9.4.3 Java訪問網路資源
在網際網路上,已經開發了許多服務,如WWW瀏覽、Email等,而Java也提供了相應的向外延展群組件,如對於Email應用,Java提供了JavaMail API,使用時只需要調用其提供的方法就可以完成如發送郵件的操作:
Transport.send(message);
在網際網路上,我們使用通用資源定位器URL(Uniform Resource Locator)來尋找資源。URL包含了用於尋找某個資源的資訊,如一張圖片、一個檔案等。URL資源可以包括很多種,如HTTP資源、FTP資源等。下面就是一個映像資源的URL,屬於HTTP資源。
本章小結
通過本章的學習,瞭解了Java異常處理、多線程機制、流式IO以及網路通訊的基本概念和模型。
在異常處理中,通過Java語言提供的先進的錯誤校正與恢複機制,可以有效地增強代碼的健壯程度,並使用儘可能精簡的代碼建立大型、可靠的應用程式,同時排除程式裡那些不能控制的錯誤。Java強迫遵守違例所有方面的問題,所以無論庫設計者還是客戶程式員,都能夠連續一致地使用它。
使用多線程機制的主要目的是對大量任務進行有序的管理,從而可以通過“輕度”切換來更有效地利用電腦資源,或者對使用者來說使用介面更加友好,相比進程之間的“重度”切換,效率得到很大提高。有效利用電腦資源的典型應用是在IO等候期間如何利用CPU;使用者方面的介面友好性的典型體現是如何在一個長時間的資料下載過程中靈敏地對“停止”(stop)操作進行反應。
在流式輸入輸出中,Java提供了通過控制台、檔案、記憶體塊甚至網際網路等多種不同資料來源或目的進行不同方式訪問的流庫。通過對流過濾器的正確使用,將提供靈活的I/O操作。Java不僅提供了對檔案的流式訪問,而且提供了隨機訪問和檔案管理。
在網路通訊中,Java不僅提供了連線導向和無串連資料報的底層通訊,而且還提供了高層服務,如Email和WWW服務等。通過Java提供的網路功能,可以以流的方式來進行網路資料的傳輸,而且不需要關注連網的細節問題。