




版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
項目7異常處理和多線程
任務一掌握Java中對異常的處理任務二掌握程序對多線程的處理項目實戰一異常處理編程項目實戰二多線程編程7.1任務一掌握Java中對異常的處理
7.1.1異常類和異常處理機制1.異常類
Java語言采用面向對象的方法來處理異常,每個異常都是由異常類產生的對象,所有的異常類(java.lang.Exception)均繼承自java.lang.Object中的java.lang.Throwable類。它提供了一些方法讓我們能夠了解異常產生的原因和一些相關的信息。Java中的異常類具有層次組織,Throwable類是Object類的直接子類,Throwable類又有Error類(錯誤類)和Exception類(異常類)兩個直接子類,這兩個子類又有許多各自的子類,如圖7-1。圖7-1異常類的繼承結構2.異常處理機制Java語言提供的異常處理機制,由捕獲異常和處理異常兩部分組成。在Java程序的執行過程中,如果出現了異常事件,就會生成一個異常對象。生成的異常對象將傳遞給Java運行系統,這一異常的產生和提交過程稱為拋出(throw)異常。當Java系統得到一個異常對象時,它將會尋找處理這一異常的代碼。找到能夠處理這種類型的異常的方法后,系統把當前異常對象交給這個方法進行處理,這一過程稱為捕獲(catch)異常。如果Java運行時系統找不到可以捕獲異的方法,則運行時系統將終止,相應的Java程序也將退出。7.1.2程序中異常處理方法
1.捕獲異常
在Java中用try-catch-finally結構來捕獲和處理異常,其語法結構為:
try {可能產生異常的程序代碼塊;} catch(<要捕獲的異常類型1><變量名稱1>)//要處理的第一種異常 {處理捕獲到的異常的代碼塊;} catch(<要捕獲的異常類型2><變量名稱2>)//要處理的第二種異常 {處理捕獲到的異常的代碼塊;} …… finally//最終處理語句 {無論是否拋出異常都要執行的代碼;}說明:try后面的{}用來選定捕獲異常的范圍,就是我們覺得哪段語句可能會出現異常我們就把這部分語句放到try后面的{}里,如果這一范圍內的某條語句發生異常,程序就會跳出try部分,不再繼續try塊中剩余的語句,根據異常的類型來執行相應的catch語句塊,去處理相應的異常。catch語句可以有多個,構成多重catch語句,處理不同類型的異常。如果有一個catch語句指定的異常類型與發生的異常類型相符,那么就會執行這個catch語句,其他的catch語句則會被跳過不被執行。如果沒有拋出異常,那么try代碼塊就會結束,并且會跳過所有catch語句,從最后一個catch后面的第一個語句繼續執行。因此,只有在有異常拋出時,才會執行catch語句。catch語句中定義變量的方法與在方法中定義參數相同,只不過這個變量對應的是一個對象實例。不論前面catch語句執不執行finally后面{}中的語句都會執行?!纠?-3】加入異常處理的多異常程序importjavax.swing.JOptionPane;public
classArrayTest2{
static
voidarraylong() {
intinputvalue,i; Stringinput;
intarray[];
do { input=JOptionPane.showInputDialog("請輸入數組長度:");//顯示輸入對話框 inputvalue=Integer.parseInt(input); //將輸入的數據轉換為整型
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,"數組長度不能為負數請重新輸入"); //輸出異常處理信息 }
catch(ArrayIndexOutOfBoundsExceptionf) { JOptionPane.showMessageDialog(null,"數組長度越界請重新輸入"); } }while(inputvalue<5);//條件不符合時返回重新輸入 JOptionPane.showMessageDialog(null,"數組長度是"+inputvalue,"數組長度是",JOptionPane.INFORMATION_MESSAGE); }
public
static
voidmain(String[]args) { Arraylong();}
}【例7-3】執行結果2.異常的捕獲順序在程序中捕獲的NegativeArraySizeException異常類和ArrayIndexOutOfBoundsException異常類都是Exception的子類,然而父子類之間是可以有自動類型轉換運算的,這就導致了異常捕獲時有先后順序,如果我們將catch(NegativeArraySizeExceptione)改為catch(Exceptione),當產生ArrayIndexOutOfBoundsException異常時第一個catch就會先將這個異常捕獲并轉換為Exception,后一個catch就不起作用了,程序就會報錯如圖7-5。
圖7-5需要指出的是Java系統本身給我們提供了幾種顯示信息的方法如:toString()方法會顯示異常類的名稱和產生的原因。getLocalizedMessage()方法和getMessage()方法會顯示異常發生的原因但不會顯示異常的名稱。其中getLocalizedMessage()方法顯示的信息會以該程序所在平臺的語系所使用的文字為主。printStackTrace()方法會顯示產生這個異常的相關類名稱以及是第幾行程序代碼產生的這個異常。3.拋出異常(1)throws關鍵字的使用在方法中聲明使用throws的具體格式為:返回類型方法名(參數1,參數2)throws異常類型1,異常類型2……{……}返回類型是方法的返回數據類型,參數是方法的參數,異常類型可以有多個用逗號隔開。我們可以將例7-3中在arraylong()方法中可能出現的異常拋出,由調用它的main()方法來處理?!纠?-4】聲明拋出異常程序importjavax.swing.JOptionPane;publicclassArrayTest3{ staticvoidarraylong()throwsNegativeArraySizeException,ArrayIndexOutOfBoundsException { intinputvalue,i; Stringinput; intarray[]; array=newint[5];input=JOptionPane.showInputDialog("請輸入數組長度(必須為小于5的正整數):"); 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,"數組長度不能為負數"); } catch(ArrayIndexOutOfBoundsExceptionf) {JOptionPane.showMessageDialog(null,"數值大于5數組長度越界");} } }(2)throw關鍵字的使用throw關鍵字主要是用在try塊中,用來明確的拋出一個“異?!?。throw關鍵字后面跟隨一個從Throwable類中派生的異常對象,用來說明發出的異常類型。throw語句促使程序立即停止運行,并且執行最近能夠處理指定對象的catch語句。如果異常在程序的其他地方產生,throw語句也可以放在try語句的后面。為了把異常處理控制傳遞給更高層的處理模塊,還可以對截獲的異常對象再一次實施throw操作。Throw語句的通常形式為:throw異常類名;【例7-5】用throw語句拋出異常程序publicclassThrowtest1{staticvoidA() {System.out.println("方法A執行中");}
publicstaticvoidmain(String[]args){ try{A();thrownewRuntimeException("方法A出現異常"); } catch(Exceptione) {System.out.println("捕獲方法A出現的異常");} finally {System.out.print("方法A結束");} }}說明:
如果拋出異常而沒有找到處理這個異常的代碼,或者沒有再次被拋出或者catch語句中捕獲異常的類型與拋出的不一致則程序會報錯。throw與throws的區別在于:throw是語句拋出一個異常,throws是方法聲明拋出一個異常;throw不能單獨使用,throw要么和try-catch-finally語句配套使用,要么與throws配套使用,而throws可以單獨使用,然后再由處理異常的方法捕獲。
4.自定義異常類實現自定義異常類有兩種方法:繼承Throwable類或者Exception類。自定義異常類之間也可以有繼承關系,在定義時需要為自定義異常類設計構造方法,以方便構造自定義異常對象。由于Exception是Throwable類的子類,所以我們自定義的異常類可以獲得Throwable定義的方法。我們也可以在創建的異常類中覆蓋一個或多個這樣的方法。自定義異常類的基本形式如下所示:class自定義異常extends父異常類名{類體;}
【例7-6】自定義異常類程序classZiDingextendsException//自定義異常類{ inti; ZiDing(inta)//構造函數 { i=a;} publicStringtoString()//覆蓋父類方法 { returni+"是非法操作數"; } }
publicclassFirstException{staticvoidjiancha(inta)throwsZiDing{if(a>100)thrownewZiDing(a);//拋出自定義類System.out.println(a+"是合法操作數");System.out.println("程序結束");}publicstaticvoidmain(Stringargs[]){try{jiancha(50);jiancha(101);}catch(ZiDinge)//捕獲自定義類{System.out.println("捕獲了異常"+e);}}}7.2.1多線程的概念
多線程是相對于單線程而言的,指的是在一個程序中可以定義多個線程并同時運行它們,每個線程可以執行不同的任務。
多線程的意義在于一個應用程序的多個邏輯單元可以并發地執行。但是多現程并不意味著多個用戶進程在執行,操作系統也不把每個線程作為獨立的進程來分配獨立的系統資源。
進程之間切換系統開銷大,線程之間切換開銷小。7.2任務二掌握程序對多線程的處理7.2.2實現多線程的兩種方法
我們有兩種方法來實現多線程,一種方法是從Thread類繼承,寫一個Thread類的子類,在子類中重寫run()方法覆蓋掉Thread類中的run()方法,每個線程都是通過某個特定Thread對象的run()方法來完成其操作的,run()方法稱為線程體。線程所要運行的程序代碼就是run()方法中的程序代碼,格式為:class類名extendsThread{……publicvoidrun(){方法體}……}【例7-7-1】繼承Thread類publicstaticclassThread1extendsThread{publicvoidrun(){
System.out.println("A");
}
}
另一種方法是提供一個實現接口Runnable的類作為一個線程的目標對象,在初始化一個Thread類或者Thread子類的線程對象時,把目標對象傳遞給這個線程實例,由該目標對象提供線程體run()格式為:class類名implementsRunnable{……publicvoidrun(){方法體}……}【例7-7-2】實現Runnable接口publicstaticclassThread2implementsRunnable{publicvoidrun(){
System.out.println("1");
}}說明:
這兩種構造線程體方法各有優缺點,使用Runnable接口可以將CPU、代碼和數據分開,形成清晰的模型,還可以從其他類繼承。
直接繼承Thread類時,由于Java不支持多重繼承所以不能再從其他類繼承,但編寫簡單可以直接操縱線程。
7.2.3線程的生命周期和線程的控制1.線程的生命周期線程的生命周期可以分為5種狀態:創建狀態(newThread)、可運行狀態(Runnable)、運行狀態(Running)、阻塞態(Blocked)、死亡狀態(Dead)。
2.線程的控制
線程的控制主要是通過Thread中提供的線程狀態轉換的方法實現的。
(1)start()方法start()方法使新生成的線程實體從新生狀態轉入可運行狀態,如:ThreadA=newThread();A.start();需要說明的是,一個thread對象只能調用一次strat方法,如果對一個已經啟動的線程再調用start方法就會產生異常。
(2)sleep()方法
從sleep()方法的名字上就可以看出這個方法是讓線程對象睡一會覺,想讓線程對象睡多長時間我們只需把時間參數傳入就可以了,只不過這個參數是以千分之一秒為單位的,如果想停一秒鐘,就要輸入1000。sleep()方法有兩種格式sleep(longmillis)和sleep(longmillis,intnanos),nanos為附加時間單位為納秒。當運行sleep()方法后線程就會暫停相應的時間,當暫停的時間到了以后,線程會轉入可運行狀態等待運行,而不是直接進入運行狀態,傳入的時間參數只是保證線程對象至少會停止的時間。需要指出的是在線程暫停過程中可能遇到外部中斷異常,導致程序中斷,所以需要進行異常處理?!纠?-9】start()方法與
sleep()方法應用程序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()方法使當前進程放棄運行,進入到可運行狀態,重新排隊等待運行,給其他可運行的進程一個運行的機會,但并不是一定就會輪到其他的線程運行,因為如果優先級相同調用yield()方法的線程有可能又被選中獲得執行的機會,我們可以把例7-9中的sleep()換成yield()看一下執行結果,yield()方法不帶參數。(4)wait()和notify()方法wait()方法的格式為wait(longtime)或wait(longtime,intnanos),time為等待的時間,單位為毫秒,nanos為附加等待時間,單位為納秒。wait()方法使當前進程進入阻塞狀態直到被喚醒或是等待的時間已到。wait()等價于wait(0),它使線程無限等待直到被喚醒。notify()方法用來喚醒一個正在等待的線程,一條notify();語句只能隨機喚醒一個進程。使用notifyAll()方法可以喚醒所有在等待的進程。需要指出的是這三個方法只能用在synchronized方法里。(5)isAlive()方法isAlive()方法用來判斷一個線程的run()方法是否還在執行,如果是則返回true,反之返回false。(6)join()方法如果一個線程在執行過程中需要等到另一個線程執行完才能繼續執行就需要另一個進程調用join()方法,該方法也要捕獲異常。join()方法也可以象wait()方法一樣帶參數表示需要另一個進程運行多長時間?!纠?-10】join()方法應用,地鐵建設工程程序publicclassJianZhuextendsThread{//建筑隊類 publicvoidrun() { System.out.println("地鐵建設中"); System.out.println("水泥用完了,通知運輸隊運輸"); Threadyunshu=newYunShu();//創建運輸隊線程 yunshu.start(); System.out.println("建筑隊等待水泥運到"); try{ yunshu.join(); }//等待運輸到達 catch(Exceptione) {//防止在等待中出現異常中斷 System.err.println("運輸隊途中遇阻"); System.err.println("建設工程停工"); } System.out.println("建設工程繼續"); } }publicclassYunShuextendsThread{//運輸隊類 publicvoidrun(){ System.out.println("運輸隊出發,水泥運回需要三小時"); try{ for(inti=1;i<=3;i++)//運輸所需時間 { Thread.sleep(3000); System.out.print("\n等待"+i+"小時"); } } catch(Exceptione) { System.err.println("運輸隊途中遇阻"); } System.out.println("\n水泥運到"); } }publicclassDiTei{//地鐵建設類 publicstaticvoidmain(Stringargs[]) {Threadjianzhu=newJianZhu();//創建建筑線程jianzhu.start(); }}例7-10中建立了三個類,JianZhu類的線程在執行過程中需要YunShu類線程加入,線程yunshu調用了jion()方法,在線程yunshu執行完后線程jianzhu才繼續執行,結果如圖7-8。圖7-8(7)getPriority()和setPriority()方法我們可以用setPriority()方法來改變一個線程的優先級,如:A.setPriority(Thread.MAX_PRIORITY-3)。用getPriority()方法返回一個線程的優先級。數值越大優先權就越高越先執行。每隔幾毫秒,操作系統的調度器就會確定一次系統中等待執行的所有線程的優先級,并將時間片分配給優先級最高的線程。如果兩個或更多的線程有相同的優先值,執行哪個線程將是不確定的。假設有A、B、C、D四個線程優先值相同,如果時間片先分給了線程A那么當這個時間片用完,操作系統就會將A轉入可運行狀態,然后從相同優先級的A、B、C、D四個線程中任選一個執行,A有可能又被重新選中獲得執行。優先級低的線程也不是沒有機會,只是機會要小一些。還有就是盡量不要通過改變進程的優先級來調度進程,這樣做比較容易產生錯誤。7.2.4線程的同步
由于同一進程的多個線程共享同一片存儲空間,在帶來方便的同時,也帶來了訪問沖突這個嚴重的問題。為有效避免了同一個數據對象被多個線程同時訪問,Java語言提供了專門機制以解決這種沖突。由于我們可以通過private關鍵字來保證數據對象只能被方法訪問,所以我們只需針對方法提出一套機制,這套機制就是synchronized關鍵字,它包括兩種用法:synchronized方法和synchronized塊。
(1)synchronized方法:通過在方法聲明中加入synchronized關鍵字來聲明synchronized方法。如:publicsynchronizedvoid方法名(參數1,參數2);synchronized方法控制對類成員變量的訪問:每個類實例對應一把鎖,每個synchronized方法都必須獲得調用該方法的類實例的鎖方能執行,否則所屬線程阻塞,方法一旦執行,就獨占該鎖,直到從該方法返回時才將鎖釋放,此后被阻塞的線程方能獲得該鎖,重新進入可執行狀態。這種機制確保了同一時刻對于每一個類實例,其所有聲明為synchronized的成員函數中至多只有一個處于可執行狀態,從而有效避免了類成員變量的訪問沖突?!纠?-12】wait()和notify()方法與synchronized修飾符應用,大炮發射程序publicclassCannons{//大炮類privatebooleanshells=false;privateinti,j=0;synchronizedvoidshot(){//發射炮彈 while(!shells){ try{wait(); } catch(InterruptedExceptione){ System.out.print("程序中斷");}} shells=false; i++; if(i>5)//發射5次后退出 {System.exit(1);} System.out.println("大炮第"+i+"次發送完畢,請裝填炮彈"); notify();//喚醒裝填手 }synchronizedvoidload(){ while(shells){ try{wait();} catch(InterruptedExceptione){ System.out.print("程序中斷");}} shells=true; j++; if(j>5) {System.exit(1);} System.out.println("炮彈裝填完畢"); notify();//通知炮手 } }classArtilleryextendsThread{//炮手類 Cannonscannon; Artillery(Cannonscannon){ this.cannon=cannon;} publicvoidrun(){ while(true){ cannon.shot();} } }classFillerextendsThread{//裝填手類 Cannonscannon; Filler(Cannonscannon){ this.cannon=cannon;} publicvoidrun(){ while(true){ cannon.load();} }}publicclassFight{//戰斗類publicstaticvoidmain(Stringargs[]){ Cannonscannon=newCannons();
newFiller(cannon).start();//裝填手線程 newArtillery(cannon).start();//炮手線程 }}例7-12執行結果7.3項目實戰一異常處理編程1.實戰內容編寫一個簡單的程序,能夠捕獲數組越界和除數為零兩種異常,并能被自定異常類捕獲。2.實戰目的通過實戰掌握異常處理的原理與方法,掌握自定義異常類和拋出異常的方法。3.實戰過程本實戰比較簡單實現的方法也有很多,我們可以首先定義兩個異常類,然后定義兩個方法分別拋出這兩個異常,被自定義異常類捕獲。參考程序:classChuLingextendsException{//自定義除零異常類 publicStringtoString(){ return":除數為零";}}classYueJieextendsException{ //自定義數組越界異常類 publicStringtoString(){ return":數組越界"; } }publicclassShiXun1{ publicstaticvoidmain(String[]args){ try{ chufa(5,1);//調用方法 shuzu(6); }//調用方法 catch(ChuLinge) { System.out.println("捕獲了自定義異常"+e);}//捕獲自定義異常 catch(YueJief) { System.out.println("捕獲了自定義異常+f);}}publicstaticvoidchufa(inta,intb)throwsChuLing{//聲明除零異常 intc=0; if(b==0) thrownewChuLing();//拋出除零異常 c=a/b; System.out.println(a+"/"+b+"的商為:"+c); } publicstaticvoidshuzu(inta)throwsYueJie{//聲明越界異常 intb[]; b=newint[5]; if(a>5) thrownewYueJie();//拋出越界異常 for(inti=0;i<a;i++){ b[i]=i; } } }7.4項目實戰二多線程編程1.實戰內容編寫一個“生產-消費”程序,設置庫存上限為50,當庫存低于上限時生產者可以生產產品,當庫存不為零時消費者可以消費。生產者和消費者在生產和消費一個產品后隨機休息一段時間。當消費者消費掉50個產品后程序退出。2.實戰目的通過實戰掌握實現多線程的方法,能夠處理好多線程間的數據同步問題。3.實戰過程本實戰可以先定義一個倉庫類,包括增加和減少庫存兩個方法,然后定義生產者和消費者兩個類對這兩個方法進行操作。classStorage{//倉庫類 privateintcount; privateintsize=50; privateinti=0;publicStorage() { super(); } publicsynchronizedvoidshengchan(Stringn){//生產方法 while(count==size){ try{ wait(); } catch(InterruptedExceptione){System.out.println("程序意外中斷"
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 社區慢病管理方法
- 2025年德育個人工作方案幼兒園演講稿
- 護理學休克病人的急救護理
- 合同履行監督與評估指南
- 術后譫妄護理個案
- 保育員培訓配合教育活動
- 神達電腦人力資源機構組織
- 濱州職業學院《功能高分子》2023-2024學年第二學期期末試卷
- 內蒙古鴻德文理學院《電視演播室》2023-2024學年第二學期期末試卷
- 安徽衛生健康職業學院《形勢與政策Ⅲ》2023-2024學年第一學期期末試卷
- 2025-2030中國集裝箱化和模塊化數據中心行業市場發展趨勢與前景展望戰略分析研究報告
- 2025-2030中國防腐新材料行業市場深度調研及發展策略與投資前景預測研究報告
- 2025年超高功率大噸位電弧爐項目發展計劃
- 2025年護工考試試題及答案
- DB32T 5076-2025 奶牛規?;B殖設施設備配置技術規范
- 2024年四川省高等職業教育單獨考試招生文化素質考試中職英語試卷
- 全國第9個近視防控月活動總結
- 人教A版必修第二冊高一(下)數學6.3.2-6.3.3平面向量正交分解及坐標表示【課件】
- 2025至2030年中國快速換模系統數據監測研究報告
- 航空業勞動力安全保障措施
- 《肺功能康復鍛煉》課件
評論
0/150
提交評論