异常与断言
1、接口方法聲明異常與實現類方法聲明異常的關系
2、異常鏈——示例
3、try-catch-finally 推薦組織結構,及其缺陷
4、斷言
-----------------------------------------------------------------------------------------------
1、接口方法聲明異常與實現類方法聲明異常的關系
接口方法聲明異常,實現類的方法可以忽略異常聲明;
接口方法沒有聲明異常,實現類的方法不能添加異常聲明!
interface IRefactorTest{/*** 假設原方法為:public void test1();* 如果重構添加方法:public void test1() throws Exception;* 兩個方法簽名相同,無法通過編譯器* * 如果重構,改變public void test1();* 為public void test1() throws Exception;* 那么以前實現的方法就沒有拋出異常(如下:test1())*///public void test1();public void test1() throws Exception;public double getNum() throws Exception; }public class InterfaceWithThrowRefactor implements IRefactorTest{/*** For this reason I prefer to define a superclass exception for a whole package * (such as SQLException for java.sql) and ensure that public methods only declare* this exception in their throws clause. That way I can define subclass exceptions * if I want to, but this won't affect a caller who knows only about the general case.*/@Overridepublic double getNum(){return 555.55;}/*** 如果接口的方法有聲明異常,實現類的方法可以忽略該異常,如下* 如果接口的方法沒有聲明異常,則實現類的方法不能有聲明的異常!*/@Overridepublic void test1(){System.out.println("Just test...");}public static void main(String[] args) {InterfaceWithThrowRefactor iwtr = new InterfaceWithThrowRefactor();iwtr.test1();try {double d = iwtr.getNum();System.out.println("d=" + d);} catch (Exception e) {e.printStackTrace();}} }2、異常鏈——示例
/*** Exception for subsystem*/ class SubsystemException extends Exception{private static final long serialVersionUID = -1395025218830523517L;public SubsystemException() {super();}public SubsystemException(String message) {super(message);} }public class ExceptionThrowChain {public static void main(String[] args) {try {normalChain();} catch (SubsystemException e) {e.printStackTrace();}try {goodChain();} catch (SubsystemException e) {e.printStackTrace();}}private static void normalChain() throws SubsystemException{try {throwException();} catch (SQLException e) {throw new SubsystemException("database error: " + e.getMessage());}}private static void goodChain() throws SubsystemException{try {throwException();} catch (SQLException e) {SubsystemException sube = new SubsystemException("database error");sube.initCause(e);throw sube;}}//Method mock throw exceptionprivate static void throwException() throws SQLException{throw new SQLException("Can't connect to database!");} }normalChain( ) 方法不能跟蹤——示例goodChain( ) 方法能跟蹤——示例
3、try-catch-finally 推薦組織結構,及其缺陷
實際代碼示例:
// Read a file as a single string:public static String read(String fileName) {StringBuilder sb = new StringBuilder();try {BufferedReader in = new BufferedReader( new FileReader(new File(fileName)) );try {String s;while ((s = in.readLine()) != null) {sb.append(s);sb.append("\n");}} finally {in.close();}} catch (IOException e) {throw new RuntimeException(e);}return sb.toString();}分析:
a、如果 BufferedReader in = ...; 拋出異常(如,文件不存在),因為沒有進入內部 try,因而不會執行內部 finally 的 in.close( ); 語句。
b、如果 BufferedReader in = ....; 成功構造,在內部 try 語句中拋出異常,則會執行內部 finally 對應的 in.close(); 語句。
c、將檢查的異常 IOException 轉換為運行時異常 RuntimeException,方法就不必聲明拋出異常了,這種構造是可跟蹤的。
缺陷:當內部 try 和 finally 都拋出異常時,finally 的異常會覆蓋 try 的異常,導致 try 內的異常丟失!
異常丟失示例(模擬):
public class TextFile{//Test exception in finally override other exceptionpublic static String read(InputStream in, int size) {StringBuilder sb = new StringBuilder();try {try {if( size==0 ){System.out.println("throw SubsystemException.");throw new SubsystemException("Exception that i want.");}} finally {in.close();}} catch (IOException e) {throw new RuntimeException(e);} catch (SubsystemException e) {throw new RuntimeException(e);}return sb.toString();} } 模擬:當傳入參數 size 為 0 時,拋出SubSystemException!public class TextFileTest {@Testpublic void testRead(){//NewInputStream easyIn = EasyMock.createMock(InputStream.class);//Call-recordtry {easyIn.close();EasyMock.expectLastCall().andThrow( new IOException("IOException of easy mock!"));} catch (IOException e) {}//ReplayEasyMock.replay( easyIn );TextFile.read(easyIn, 0);//VerifyEasyMock.verify(easyIn);} }
模擬:模擬 InputStream 對象,使其調用 close( ) 方法時拋出 IOException!
注意:EasyMock 模擬的是類而非接口,那么就必須都用模擬類的 EasyMock 來調用 expectLastCall(), reaplay(), verify() 等方法!
運行,報錯如下:
而且,有如下輸出:
由上面兩圖,可得出結論:finally 內的 IOException 覆蓋了內部 try 的 SubsystemException !
4、斷言
公有的方法(即,API)用異常,非公有方法通常使用斷言來檢查參數。
When should you choose assertions? Keep these points in mind:
1、Assertion failures are intended to be fatal, unrecoverable errors.?
2、Assertion checks are turned on only during development and testing.
示例:
public class TestAssert {public void testAssert(){int[] a = new int[]{1, 22 , 3 ,5};//length is 3//offset is 2//a.length is 4//length > a.length - offset, Error!sort( a, 2, 3 );}private void sort( int a[], int offset, int length ){assert a != null;assert offset >= 0 && offset <= a.length;assert length >= 0 && length < a.length - offset;System.out.println("Just a test!");} }測試代碼: public class TestAssertTest {@Testpublic void testTestAssert() {TestAssert ta = new TestAssert();ta.testAssert();} }Eclipse啟動運行時斷言測試結果,報錯!
總結
- 上一篇: Java加密与解密的艺术~SHA算法实现
- 下一篇: 解释器模式详解