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.
*/
@Override
public double getNum(){
return 555.55;
}
/**
* 如果接口的方法有声明异常,实现类的方法可以忽略该异常,如下
* 如果接口的方法没有声明异常,则实现类的方法不能有声明的异常!
*/
@Override
public 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 exception
private 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 exception
public 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 {
@Test
public void testRead(){
//New
InputStream easyIn = EasyMock.createMock(InputStream.class);
//Call-record
try {
easyIn.close();
EasyMock.expectLastCall().andThrow( new IOException("IOException of easy mock!"));
} catch (IOException e) {}
//Replay
EasyMock.replay( easyIn );
TextFile.read(easyIn, 0);
//Verify
EasyMock.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 {
@Test
public void testTestAssert() {
TestAssert ta = new TestAssert();
ta.testAssert();
}
}
Eclipse启动运行时断言
测试结果,报错!