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启动运行时断言
测试结果,报错!