异常与断言

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启动运行时断言

测试结果,报错!




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值