異常
26.?finally與中斷
- //該方法返回false ??
- static ? boolean ?f()?{??
- ? try ?{??
- ?? return ? true ;??
- ?}? finally ?{??
- ?? return ? false ;??
- ?}??
- }??
不要用return、break、continue或throw來退出finally語句塊,并且千萬不要允許受檢查的異常傳播到finally語句
塊之外。也就是說不要在finally塊內終止程序,而是執行完finally塊后,要將控制權移交給try塊,由try最終決定
怎樣結束方法的調用。
?
對于任何在finally語句塊中可能拋出的受檢查異常都要進行處理,而不是任其傳播,下面流拷貝程序在關閉流時沒有
防止異常的傳播,這會有問題:
- static ? void ?copy(String?src,?String?dest)? throws ?IOException?{??
- ?InputStream?in?=? null ;??
- ?OutputStream?out?=? null ;??
- ? try ?{??
- ??in?=? new ?FileInputStream(src);??
- ??out?=? new ?FileOutputStream(dest);??
- ?? byte []?buf?=? new ? byte [ 1024 ];??
- ?? int ?n;??
- ?? while ?((n?=?in.read(buf))?>=? 0 )?{??
- ???out.write(buf,? 0 ,?n);??
- ??}??
- ?}? finally {??
- ?? //這里應該使用try-catch將每個close包裝起來 ??
- ?? if (in?!=? null ){in.close();}??
- ?? if (in?!=? null ){out.close();}??
- ?}??
- }??
catch塊中的return語句是不會阻止finally塊執行的,那么catch塊中的continue和break能否阻止?答案是不會的,
與return一樣,finally語句塊是在循環被跳過(continue)和中斷(break)之前被執行的:
- int ?i?=? 0 ;??
- System.out.println( "--continue--" );??
- while ?(i++?<=? 1 )?{??
- ? try ?{??
- ??System.out.println( "i=" ?+?i);??
- ?? continue ;??
- ?}? catch ?(Exception?e)?{??
- ?}? finally ?{??
- ??System.out.println( "finally" );??
- ?}??
- }??
- System.out.println( "--break--" );??
- while ?(i++?<=? 3 )?{??
- ? try ?{??
- ??System.out.println( "i=" ?+?i);??
- ?? break ;??
- ?}? catch ?(Exception?e)?{??
- ?}? finally ?{??
- ??System.out.println( "finally" );??
- ?}??
- }??
27.?catch捕獲異常規則
捕獲RuntimeException、Exception或Throwable的catch語句是合法,不管try塊里是否拋出了這三個異常。但如果try
塊沒有拋出或不可能拋出檢測性異常,則catch不能捕獲這些異常,如IOException異常:
- public ? class ?Test?{??
- ? public ? static ? void ?main(String[]?args)?{??
- ?? try {??
- ??? //... ??
- ??} catch ?(Exception?e)?{??
- ?????
- ??} catch ?(Throwable?e)?{??
- ?????
- ??}??
- ????
- ?? /*?!!?編譯出錯 ?
- ???try{ ?
- ????//... ?
- ???}catch?(IOException?e)?{ ?
- ???? ?
- ???} ?
- ???*/ ??
- ?}??
- }??
28.?重寫時方法異常范圍
重寫或實現時不能擴大異常的范圍,如果是多繼承,則異常取所有父類方法異常的交集或不拋出異常:
- interface ?I1?{??
- ? void ?f()? throws ?Exception;??
- }??
- ??
- interface ?I2?{??
- ? void ?f()? throws ?IOException;??
- }??
- ??
- interface ?I3? extends ?I1,?I2?{}??
- ??
- class ?Imp? implements ?I3?{??
- ? //?不能編譯通過,多繼承時只能取父類方法異常交集,這樣就不會擴大異常范圍 ??
- ? //?!!?void?f?()?throws?Exception; ??
- ? //?void?f();//?能編譯通過 ??
- ? //?能編譯通過,Exception與IOException的交集為IOException ??
- ? public ? void ?f()? throws ?IOException?{??
- ?}??
- }??
29.?靜態與非靜態final常量不能在catch塊中初始化
?
靜態與非靜態塊中如果拋出了異常,則一定要使用try-catch塊來捕獲。
- public ? class ?Test?{??
- ? static ? final ? int ?i;??
- ? static ?{??
- ?? try ?{??
- ???i?=?f();??
- ??}? catch ?(RuntimeException?e)?{??
- ???i?=? 1 ;??
- ??}??
- ?}??
- ??
- ? static ? int ?f()?{??
- ?? throw ? new ?RuntimeException();??
- ?}??
- }??
上面的程序編譯不能通過。表面上是可以的,因為i第一次初始化時可能拋出異常,所以拋異常時可以在catch塊中初
始化,最終還是只初始化一次,這正是空final所要求的,但為什么編譯器不知道這些呢?
?
要確定一個程序是否不止一次地對一個空final進行賦值是很困難的問題。語言規范在這一點上采用了保守的方式。
30.?System.exit()與finally
- try ?{??
- ?System.out.println( "Hello?world" );??
- ?System.exit( 0 );??
- ? //?或者使用Runtime退出系統 ??
- ? //?Runtime.getRuntime().exit(0); ??
- }? finally ?{??
- ?System.out.println( "Goodbyte?world" );??
- }??
上面的程序會打印出"Goodbyte world"嗎?不會。
?
System.exit將立即停止所有的程序線程,它并不會使finally語句塊得到調用,但是它在停止VM之前會執行關閉掛鉤
操作(這此掛鉤操作是注冊到Runtime.addShutdownHook上的線程),這對于釋放VM之外的資源很有幫助。使用掛鉤程
序修改上面程序:
- System.out.println( "Hello?world" );??
- Runtime.getRuntime().addShutdownHook( new ?Thread()?{??
- ? public ? void ?run()?{??
- ??System.out.println( "Goodbyte?world" );??
- ?}??
- });??
- System.exit( 0 );??
?
另外,對象回收時,使用VM調用對象的finalize()方法有兩種:
System.runFinalization():該方法讓虛擬機也只是盡最大努力去完成所有未執行的finalize()終止方法,但不一定
會執行。
System.runFinalizersOnExit(true):該方法一定會回收,但不安全,已被廢棄。因為它可能對正在使用的對象調用
終結方法,而其他線程同時正在操作這些對象,從而導致不正確的行為或死鎖。
?
為了加快垃圾回收,使用System.gc(),但不一定馬上執行加收動作,由虛擬機決定,實質上是調用
Runtime.getRuntime().gc()。
?
System的很多方法都是調用Runtime類的相關方法來實現的。
31.?遞歸構造
- public ? class ?S??{??
- ? private ?S?instance?=? new ?S();??
- ? public ?S()?{}??
- }??
如果在程序外面構造該類的實例,則會拋出java.lang.StackOverflowError錯誤。其原因是實例變量的初始化操作將
先于構造器的程序體而運行。
32.?構造器中的異常
如果父類構造器拋出了檢測異常,則子類也只能拋出,而不能采用try-catch來捕獲:
- public ? class ?P?{??
- ? public ?P()? throws ?Exception?{}??
- }??
- ??
- class ?S? extends ?P?{??
- ? public ?S()? throws ?Exception?{??
- ?? try ?{??
- ??? //?不能在try塊中明確調用父類構造器,因為構造的 ??
- ??? //?明確調用只能放在第一行 ??
- ??? //?!!?super(); ??
- ?? //try-catch不能捕獲到父類構造器所拋出的異常,子類只能拋出 ??
- ??}? catch ?(Exception?e)?{??
- ??}??
- ?}??
- }??
?
如果初使化實例屬性時拋出了異常,則構造器只能拋出異常,在構造器中捕獲不起作用:
- public ? class ?A?{??
- ???? private ?String?str?=?String. class .newInstance();??
- ??
- ???? public ?A()? throws ?InstantiationException,?IllegalAccessException?{}??
- ??
- ???? public ?A( int ?i)? throws ?Exception?{??
- ???????? try ?{ //即使這里捕獲了,方法簽名還是得要拋出 ??
- ??
- ????????}? catch ?(Exception?e)?{??
- ??
- ????????}??
- ????}??
- ??
- ???? /* ?
- ?????*?!!編譯不能通過,因為str2為靜態的,他不能通過構造器來捕獲,所以只 ?
- ?????*?能使用靜態方法來捕獲。即初始化靜態成員時不能拋出捕獲性異常。? ?
- ?????*/ ??
- ???? //!!private?static?String?str2?=?String.class.newInstance(); ??
- ??????
- ???? //?只能使用靜態方法來捕獲異常,如果是拋出的運行時異常則不需要捕獲 ??
- ???? private ? static ?String?str2?=?newInstance();??
- ??
- ???? private ? static ?String?newInstance()? throws ?RuntimeException?{??
- ???????? try ?{??
- ???????????? return ?String. class .newInstance();??
- ????????}? catch ?(Exception?e)?{??
- ????????????e.printStackTrace();??
- ????????}??
- ???????? return ? null ;??
- ????}??
- }??
33.?StackOverflowError
Java虛擬機對棧的深度限制到了某個值,當超過這個值時,VM就拋出StackOverflowError。一般VM都將棧的深度限制
為1024,即當方法調用方法的層次超過1024時就會產生StackOverflowError。
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
