JAVA程序設(shè)計(jì)技能教程第7章_第1頁
JAVA程序設(shè)計(jì)技能教程第7章_第2頁
JAVA程序設(shè)計(jì)技能教程第7章_第3頁
JAVA程序設(shè)計(jì)技能教程第7章_第4頁
JAVA程序設(shè)計(jì)技能教程第7章_第5頁
已閱讀5頁,還剩37頁未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

第7章異常處理和多線程

任務(wù)一:掌握J(rèn)ava中對異常的處理

任務(wù)三:實(shí)訓(xùn)七異常處理與多線程編程實(shí)訓(xùn)

任務(wù)二:掌握程序?qū)Χ嗑€程的處理

7.1任務(wù)一掌握J(rèn)ava中對異常的處理

7.1.1異常類和異常處理機(jī)制1.異常類

Java語言采用面向?qū)ο蟮姆椒▉硖幚懋惓#總€異常都是由異常類產(chǎn)生的對象,所有的異常類(java.lang.Exception)均繼承自java.lang.Object中的java.lang.Throwable類。它提供了一些方法讓我們能夠了解異常產(chǎn)生的原因和一些相關(guān)的信息。Java中的異常類具有層次組織,Throwable類是Object類的直接子類,Throwable類又有Error類(錯誤類)和Exception類(異常類)兩個直接子類,這兩個子類又有許多各自的子類,如圖7-1。圖7-1異常類的繼承結(jié)構(gòu)2.異常處理機(jī)制Java語言提供的異常處理機(jī)制,由捕獲異常和處理異常兩部分組成。在Java程序的執(zhí)行過程中,如果出現(xiàn)了異常事件,就會生成一個異常對象。生成的異常對象將傳遞給Java運(yùn)行系統(tǒng),這一異常的產(chǎn)生和提交過程稱為拋出(throw)異常。當(dāng)Java系統(tǒng)得到一個異常對象時,它將會尋找處理這一異常的代碼。找到能夠處理這種類型的異常的方法后,系統(tǒng)把當(dāng)前異常對象交給這個方法進(jìn)行處理,這一過程稱為捕獲(catch)異常。如果Java運(yùn)行時系統(tǒng)找不到可以捕獲異的方法,則運(yùn)行時系統(tǒng)將終止,相應(yīng)的Java程序也將退出。7.1.2程序中異常處理方法

1.捕獲異常在Java中用try-catch-finally結(jié)構(gòu)來捕獲和處理異常,其語法結(jié)構(gòu)為:

try {可能產(chǎn)生異常的程序代碼塊;} catch(<要捕獲的異常類型1><變量名稱1>)//要處理的第一種異常

{處理捕獲到的異常的代碼塊;} catch(<要捕獲的異常類型2><變量名稱2>)//要處理的第二種異常

{處理捕獲到的異常的代碼塊;} …… finally//最終處理語句

{無論是否拋出異常都要執(zhí)行的代碼;}說明:

try后面的{}用來選定捕獲異常的范圍,就是我們覺得哪段語句可能會出現(xiàn)異常我們就把這部分語句放到try后面的{}里,如果這一范圍內(nèi)的某條語句發(fā)生異常,程序就會跳出try部分,不再繼續(xù)try塊中剩余的語句,根據(jù)異常的類型來執(zhí)行相應(yīng)的catch語句塊,去處理相應(yīng)的異常。catch語句可以有多個,構(gòu)成多重catch語句,處理不同類型的異常。如果有一個catch語句指定的異常類型與發(fā)生的異常類型相符,那么就會執(zhí)行這個catch語句,其他的catch語句則會被跳過不被執(zhí)行。如果沒有拋出異常,那么try代碼塊就會結(jié)束,并且會跳過所有catch語句,從最后一個catch后面的第一個語句繼續(xù)執(zhí)行。因此,只有在有異常拋出時,才會執(zhí)行catch語句。catch語句中定義變量的方法與在方法中定義參數(shù)相同,只不過這個變量對應(yīng)的是一個對象實(shí)例。不論前面catch語句執(zhí)不執(zhí)行finally后面{}中的語句都會執(zhí)行。【例7-3】加入異常處理的多異常程序importjavax.swing.JOptionPane;public

classArrayTest2{

static

voidarraylong() {

intinputvalue,i; Stringinput;

intarray[];

do { input=JOptionPane.showInputDialog("請輸入數(shù)組長度:");//顯示輸入對話框

inputvalue=Integer.parseInt(input); //將輸入的數(shù)據(jù)轉(zhuǎn)換為整型

try { array=new

int[inputvalue];

for(i=0;i<5;i++)

{ System.out.println("輸出array["+i+"]"+"="+array[i]+"i="+i); } }

catch(NegativeArraySizeExceptione) { JOptionPane.showMessageDialog(null,"數(shù)組長度不能為負(fù)數(shù)請重新輸入"); //輸出異常處理信息

}

catch(ArrayIndexOutOfBoundsExceptionf) { JOptionPane.showMessageDialog(null,"數(shù)組長度越界請重新輸入"); } }while(inputvalue<5);//條件不符合時返回重新輸入

JOptionPane.showMessageDialog(null,"數(shù)組長度是"+inputvalue,"數(shù)組長度是",JOptionPane.INFORMATION_MESSAGE); }

public

static

voidmain(String[]args) { Arraylong();}

}【例7-3】執(zhí)行結(jié)果2.異常的捕獲順序

在程序中捕獲的NegativeArraySizeException異常類和ArrayIndexOutOfBoundsException異常類都是Exception的子類,然而父子類之間是可以有自動類型轉(zhuǎn)換運(yùn)算的,這就導(dǎo)致了異常捕獲時有先后順序,如果我們將catch(NegativeArraySizeExceptione)改為catch(Exceptione),當(dāng)產(chǎn)生ArrayIndexOutOfBoundsException異常時第一個catch就會先將這個異常捕獲并轉(zhuǎn)換為Exception,后一個catch就不起作用了,程序就會報(bào)錯如圖7-5。

圖7-5需要指出的是Java系統(tǒng)本身給我們提供了幾種顯示信息的方法如:toString()方法會顯示異常類的名稱和產(chǎn)生的原因。getLocalizedMessage()方法和getMessage()方法會顯示異常發(fā)生的原因但不會顯示異常的名稱。其中g(shù)etLocalizedMessage()方法顯示的信息會以該程序所在平臺的語系所使用的文字為主。printStackTrace()方法會顯示產(chǎn)生這個異常的相關(guān)類名稱以及是第幾行程序代碼產(chǎn)生的這個異常。3.拋出異常(1)throws關(guān)鍵字的使用在方法中聲明使用throws的具體格式為:返回類型方法名(參數(shù)1,參數(shù)2)throws異常類型1,異常類型2……{……}返回類型是方法的返回?cái)?shù)據(jù)類型,參數(shù)是方法的參數(shù),異常類型可以有多個用逗號隔開。我們可以將例7-3中在arraylong()方法中可能出現(xiàn)的異常拋出,由調(diào)用它的main()方法來處理?!纠?-4】聲明拋出異常程序importjavax.swing.JOptionPane;publicclassArrayTest3{ staticvoidarraylong()throwsNegativeArraySizeException,ArrayIndexOutOfBoundsException { intinputvalue,i; Stringinput; intarray[]; array=newint[5];input=JOptionPane.showInputDialog("請輸入數(shù)組長度(必須為小于5的正整數(shù)):"); inputvalue=Integer.parseInt(input); for(i=0;i<inputvalue;i++) {System.out.println("輸出array["+i+"]"+"="+array[i]+"i="+i);} } publicstaticvoidmain(String[]args) { try {arraylong();} catch(NegativeArraySizeExceptione) {JOptionPane.showMessageDialog(null,"數(shù)組長度不能為負(fù)數(shù)"); } catch(ArrayIndexOutOfBoundsExceptionf) {JOptionPane.showMessageDialog(null,"數(shù)值大于5數(shù)組長度越界");} } }(2)throw關(guān)鍵字的使用throw關(guān)鍵字主要是用在try塊中,用來明確的拋出一個“異?!?。throw關(guān)鍵字后面跟隨一個從Throwable類中派生的異常對象,用來說明發(fā)出的異常類型。throw語句促使程序立即停止運(yùn)行,并且執(zhí)行最近能夠處理指定對象的catch語句。如果異常在程序的其他地方產(chǎn)生,throw語句也可以放在try語句的后面。為了把異常處理控制傳遞給更高層的處理模塊,還可以對截獲的異常對象再一次實(shí)施throw操作。Throw語句的通常形式為:

throw異常類名;【例7-5】用throw語句拋出異常程序publicclassThrowtest1{staticvoidA() {System.out.println("方法A執(zhí)行中");}

publicstaticvoidmain(String[]args){ try{A();thrownewRuntimeException("方法A出現(xiàn)異常"); } catch(Exceptione) {System.out.println("捕獲方法A出現(xiàn)的異常");} finally {System.out.print("方法A結(jié)束");} }}說明:如果拋出異常而沒有找到處理這個異常的代碼,或者沒有再次被拋出或者catch語句中捕獲異常的類型與拋出的不一致則程序會報(bào)錯。

throw與throws的區(qū)別在于:throw是語句拋出一個異常,throws是方法聲明拋出一個異常;throw不能單獨(dú)使用,throw要么和try-catch-finally語句配套使用,要么與throws配套使用,而throws可以單獨(dú)使用,然后再由處理異常的方法捕獲。

4.自定義異常類實(shí)現(xiàn)自定義異常類有兩種方法:繼承Throwable類或者Exception類。自定義異常類之間也可以有繼承關(guān)系,在定義時需要為自定義異常類設(shè)計(jì)構(gòu)造方法,以方便構(gòu)造自定義異常對象。由于Exception是Throwable類的子類,所以我們自定義的異常類可以獲得Throwable定義的方法。我們也可以在創(chuàng)建的異常類中覆蓋一個或多個這樣的方法。自定義異常類的基本形式如下所示:

class自定義異常extends父異常類名

{類體;}

【例7-6】自定義異常類程序classZiDingextendsException//自定義異常類{ inti; ZiDing(inta)//構(gòu)造函數(shù)

{ i=a;} publicStringtoString()//覆蓋父類方法

{ returni+"是非法操作數(shù)"; } }

publicclassFirstException{staticvoidjiancha(inta)throwsZiDing{if(a>100)thrownewZiDing(a);//拋出自定義類System.out.println(a+"是合法操作數(shù)");System.out.println("程序結(jié)束");}publicstaticvoidmain(Stringargs[]){try{jiancha(50);jiancha(101);}catch(ZiDinge)//捕獲自定義類{System.out.println("捕獲了異常"+e);}}}7.2.1多線程的概念多線程是相對于單線程而言的,指的是在一個程序中可以定義多個線程并同時運(yùn)行它們,每個線程可以執(zhí)行不同的任務(wù)。多線程的意義在于一個應(yīng)用程序的多個邏輯單元可以并發(fā)地執(zhí)行。但是多現(xiàn)程并不意味著多個用戶進(jìn)程在執(zhí)行,操作系統(tǒng)也不把每個線程作為獨(dú)立的進(jìn)程來分配獨(dú)立的系統(tǒng)資源。進(jìn)程之間切換系統(tǒng)開銷大,線程之間切換開銷小。7.2任務(wù)二掌握程序?qū)Χ嗑€程的處理7.2.2實(shí)現(xiàn)多線程的兩種方法我們有兩種方法來實(shí)現(xiàn)多線程,一種方法是從Thread類繼承,寫一個Thread類的子類,在子類中重寫run()方法覆蓋掉Thread類中的run()方法,每個線程都是通過某個特定Thread對象的run()方法來完成其操作的,run()方法稱為線程體。線程所要運(yùn)行的程序代碼就是run()方法中的程序代碼,格式為:class類名extendsThread{……publicvoidrun(){方法體}……}【例7-7-1】繼承Thread類publicstaticclassThread1extendsThread{publicvoidrun(){

System.out.println("A");

}

}

另一種方法是提供一個實(shí)現(xiàn)接口Runnable的類作為一個線程的目標(biāo)對象,在初始化一個Thread類或者Thread子類的線程對象時,把目標(biāo)對象傳遞給這個線程實(shí)例,由該目標(biāo)對象提供線程體run()格式為:class類名implementsRunnable{……publicvoidrun(){方法體}……}【例7-7-2】實(shí)現(xiàn)Runnable接口publicstaticclassThread2implementsRunnable{publicvoidrun(){

System.out.println("1");

}}說明:這兩種構(gòu)造線程體方法各有優(yōu)缺點(diǎn),使用Runnable接口可以將CPU、代碼和數(shù)據(jù)分開,形成清晰的模型,還可以從其他類繼承。直接繼承Thread類時,由于Java不支持多重繼承所以不能再從其他類繼承,但編寫簡單可以直接操縱線程。

7.2.3線程的生命周期和線程的控制1.線程的生命周期線程的生命周期可以分為5種狀態(tài):創(chuàng)建狀態(tài)(newThread)、可運(yùn)行狀態(tài)(Runnable)、運(yùn)行狀態(tài)(Running)、阻塞態(tài)(Blocked)、死亡狀態(tài)(Dead)。

2.線程的控制線程的控制主要是通過Thread中提供的線程狀態(tài)轉(zhuǎn)換的方法實(shí)現(xiàn)的。(1)start()方法start()方法使新生成的線程實(shí)體從新生狀態(tài)轉(zhuǎn)入可運(yùn)行狀態(tài),如:

ThreadA=newThread();A.start();需要說明的是,一個thread對象只能調(diào)用一次strat方法,如果對一個已經(jīng)啟動的線程再調(diào)用start方法就會產(chǎn)生異常。

(2)sleep()方法

從sleep()方法的名字上就可以看出這個方法是讓線程對象睡一會覺,想讓線程對象睡多長時間我們只需把時間參數(shù)傳入就可以了,只不過這個參數(shù)是以千分之一秒為單位的,如果想停一秒鐘,就要輸入1000。sleep()方法有兩種格式sleep(longmillis)和sleep(longmillis,intnanos),nanos為附加時間單位為納秒。當(dāng)運(yùn)行sleep()方法后線程就會暫停相應(yīng)的時間,當(dāng)暫停的時間到了以后,線程會轉(zhuǎn)入可運(yùn)行狀態(tài)等待運(yùn)行,而不是直接進(jìn)入運(yùn)行狀態(tài),傳入的時間參數(shù)只是保證線程對象至少會停止的時間。需要指出的是在線程暫停過程中可能遇到外部中斷異常,導(dǎo)致程序中斷,所以需要進(jìn)行異常處理?!纠?-9】start()方法與

sleep()方法應(yīng)用程序publicclassSleepTestextendsThread{Stringlight;SleepTest(Stringlight){ this.light=light;}publicvoidrun(){ try{ for(inti=1;i<=5;i++) {System.out.println(light+i+"次"); sleep(100); } } catch(Exceptione){ System.err.print(e); }}publicstaticvoidmain(String[]args){ newSleepTest("1號燈亮").start(); newSleepTest("2號燈亮").start();}}(3)yield()方法yield()方法使當(dāng)前進(jìn)程放棄運(yùn)行,進(jìn)入到可運(yùn)行狀態(tài),重新排隊(duì)等待運(yùn)行,給其他可運(yùn)行的進(jìn)程一個運(yùn)行的機(jī)會,但并不是一定就會輪到其他的線程運(yùn)行,因?yàn)槿绻麅?yōu)先級相同調(diào)用yield()方法的線程有可能又被選中獲得執(zhí)行的機(jī)會,我們可以把例7-9中的sleep()換成yield()看一下執(zhí)行結(jié)果,yield()方法不帶參數(shù)。(4)wait()和notify()方法wait()方法的格式為wait(longtime)或wait(longtime,intnanos),time為等待的時間,單位為毫秒,nanos為附加等待時間,單位為納秒。wait()方法使當(dāng)前進(jìn)程進(jìn)入阻塞狀態(tài)直到被喚醒或是等待的時間已到。wait()等價(jià)于wait(0),它使線程無限等待直到被喚醒。notify()方法用來喚醒一個正在等待的線程,一條notify();語句只能隨機(jī)喚醒一個進(jìn)程。使用notifyAll()方法可以喚醒所有在等待的進(jìn)程。需要指出的是這三個方法只能用在synchronized方法里。(5)isAlive()方法isAlive()方法用來判斷一個線程的run()方法是否還在執(zhí)行,如果是則返回true,反之返回false。(6)join()方法如果一個線程在執(zhí)行過程中需要等到另一個線程執(zhí)行完才能繼續(xù)執(zhí)行就需要另一個進(jìn)程調(diào)用join()方法,該方法也要捕獲異常。join()方法也可以象wait()方法一樣帶參數(shù)表示需要另一個進(jìn)程運(yùn)行多長時間?!纠?-10】join()方法應(yīng)用,地鐵建設(shè)工程程序publicclassJianZhuextendsThread{//建筑隊(duì)類

publicvoidrun() { System.out.println("地鐵建設(shè)中"); System.out.println("水泥用完了,通知運(yùn)輸隊(duì)運(yùn)輸"); Threadyunshu=newYunShu();//創(chuàng)建運(yùn)輸隊(duì)線程

yunshu.start(); System.out.println("建筑隊(duì)等待水泥運(yùn)到"); try{ yunshu.join(); }//等待運(yùn)輸?shù)竭_(dá)

catch(Exceptione) {//防止在等待中出現(xiàn)異常中斷

System.err.println("運(yùn)輸隊(duì)途中遇阻"); System.err.println("建設(shè)工程停工"); } System.out.println("建設(shè)工程繼續(xù)"); } }publicclassYunShuextendsThread{//運(yùn)輸隊(duì)類

publicvoidrun(){ System.out.println("運(yùn)輸隊(duì)出發(fā),水泥運(yùn)回需要三小時"); try{ for(inti=1;i<=3;i++)//運(yùn)輸所需時間

{ Thread.sleep(3000); System.out.print("\n等待"+i+"小時"); } } catch(Exceptione) { System.err.println("運(yùn)輸隊(duì)途中遇阻"); } System.out.println("\n水泥運(yùn)到"); } }publicclassDiTei{//地鐵建設(shè)類

publicstaticvoidmain(Stringargs[]) {Threadjianzhu=newJianZhu();//創(chuàng)建建筑線程

jianzhu.start(); }}例7-10中建立了三個類,JianZhu類的線程在執(zhí)行過程中需要YunShu類線程加入,線程yunshu調(diào)用了jion()方法,在線程yunshu執(zhí)行完后線程jianzhu才繼續(xù)執(zhí)行,結(jié)果如圖7-8。圖7-8(7)getPriority()和setPriority()方法我們可以用setPriority()方法來改變一個線程的優(yōu)先級,如:A.setPriority(Thread.MAX_PRIORITY-3)。用getPriority()方法返回一個線程的優(yōu)先級。數(shù)值越大優(yōu)先權(quán)就越高越先執(zhí)行。每隔幾毫秒,操作系統(tǒng)的調(diào)度器就會確定一次系統(tǒng)中等待執(zhí)行的所有線程的優(yōu)先級,并將時間片分配給優(yōu)先級最高的線程。如果兩個或更多的線程有相同的優(yōu)先值,執(zhí)行哪個線程將是不確定的。假設(shè)有A、B、C、D四個線程優(yōu)先值相同,如果時間片先分給了線程A那么當(dāng)這個時間片用完,操作系統(tǒng)就會將A轉(zhuǎn)入可運(yùn)行狀態(tài),然后從相同優(yōu)先級的A、B、C、D四個線程中任選一個執(zhí)行,A有可能又被重新選中獲得執(zhí)行。優(yōu)先級低的線程也不是沒有機(jī)會,只是機(jī)會要小一些。還有就是盡量不要通過改變進(jìn)程的優(yōu)先級來調(diào)度進(jìn)程,這樣做比較容易產(chǎn)生錯誤。7.2.4線程的同步由于同一進(jìn)程的多個線程共享同一片存儲空間,在帶來方便的同時,也帶來了訪問沖突這個嚴(yán)重的問題。為有效避免了同一個數(shù)據(jù)對象被多個線程同時訪問,Java語言提供了專門機(jī)制以解決這種沖突。由于我們可以通過private關(guān)鍵字來保證數(shù)據(jù)對象只能被方法訪問,所以我們只需針對方法提出一套機(jī)制,這套機(jī)制就是synchronized關(guān)鍵字,它包括兩種用法:synchronized方法和synchronized塊。

(1)synchronized方法:通過在方法聲明中加入synchronized關(guān)鍵字來聲明synchronized方法。如:publicsynchronizedvoid方法名(參數(shù)1,參數(shù)2);synchronized方法控制對類成員變量的訪問:每個類實(shí)例對應(yīng)一把鎖,每個synchronized方法都必須獲得調(diào)用該方法的類實(shí)例的鎖方能執(zhí)行,否則所屬線程阻塞,方法一旦執(zhí)行,就獨(dú)占該鎖,直到從該方法返回時才將鎖釋放,此后被阻塞的線程方能獲得該鎖,重新進(jìn)入可執(zhí)行狀態(tài)。這種機(jī)制確保了同一時刻對于每一個類實(shí)例,其所有聲明為synchronized的成員函數(shù)中至多只有一個處于可執(zhí)行狀態(tài),從而有效避免了類成員變量的訪問沖突?!纠?-12】wait()和notify()方法與synchronized修飾符應(yīng)用,大炮發(fā)射程序publicclassCannons{//大炮類privatebooleanshells=false;privateinti,j=0;synchronizedvoidshot(){//發(fā)射炮彈

while(!shells){ try{wait(); } catch(InterruptedExceptione){ System.out.print("程序中斷");}} shells=false; i++; if(i>5)//發(fā)射5次后退出

{System.exit(1);} System.out.println("大炮第"+i+"次發(fā)送完畢,請裝填炮彈"); notify();//喚醒裝填手 }synchronizedvoidload(){ while(shells){ try{wait();} catch(InterruptedExceptione){ System.out.print("程序中斷");}} shells=true; j++; if(j>5) {System.exit(1);} System.out.println("炮彈裝填完畢"); notify();//通知炮手

} }classArtilleryextendsThre

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論