毫无疑问,抛出异常是程序动态执行过程中捕获错误的一种很好的手段,但凡事有益有弊,抛出异常实际上由某种语言在你的代码里面插入了某些代码,在你的代码的基础上,增加了某些控制,这些控制一般比较复杂。其实可以这样认为,抛出的异常越多,越影响程序的执行效率。
所以,应该只在需要捕获异常的地方捕获异常。最好能精确到某个点,如函数内,函数外或者让异常扩散到合适的位置。本文举一个例子,说明一个不恰当捕获异常的范例。
/*
从 url 列表爬取公司详细信息
*/
private static int foundAndSyncToDB() throws Exception
{
DBInit dBInit = new DBInit("db.init");
if(!dBInit.init())
{
System.out.println("数据库初始化失败");
return 0;
}
final Map
urls = new HashMap
(){
{
put("xxx",1); //第一个元素是公司 url,第二个元素是该 url 页面的公司的类型
put("xxxx",1);
put("xxxxx",1);
put("xxxxxx",1);
put("yyy",2);
put("zzz",3);
put("zzzzz",3);
}
};
Connection con = dBInit.getConnection();
PreparedStatement pre;
pre = con.prepareCall("insert into tbl_company_stock values (?,?,?,?)");
int founderCount = 0;
HashMap
> stocks;
Set
keys = urls.keySet(); for (String url : keys) { AutoStockFounderImpl skn = AutoStockFounderImpl.RemoteFileStock(url); stocks = skn.getStocks(); Set
cols = stocks.keySet(); pre.setInt(3, urls.get(url)); for (String col : cols) { pre.setString(1, col); pre.setString(2, stocks.get(col).get(0)); pre.setString(4, stocks.get(col).get(1)); if(!pre.execute() && 0 != pre.getUpdateCount()) { ++founderCount; } } } pre.close(); return founderCount; }
上面的函数的功能是由从几个公司页面爬取公司详细信息。我们看到,整个函数捕获但不处理异常,直接将异常扩散到外层,由外层去处理。这种写法不会错,但从功能上来讲,不会是我们想要的。为什么呢?在整个函数里面,可能抛出异常的地方有两个,
一是 pre 的初始化处,包括 con.prepareCall 和 pre.set 一系列函数;二是 pre 执行查询处。可以说,一个是静态错误,一个是运行异常。这两者是不同的。只要前者抛出异常,说明必有很多个异常;后者抛出异常,末必有多个。什么意思呢?就是说,pre 构造一旦出错,后面的运行,以及往后再构造也都会出错,所以,只要抛出异常,就应该终止运行,手段有两种:在函数内部捕获到异常后报告并直接退出函数,或者不作任何处理,直接退出函数。而执行查询时发生的运行异常,不能因为发生一次,就不给后面执行机会,直接退出函数,而应该在异常点捕获并处理,让后面还有机会执行查询,上面的代码应该改成如下的样子。
/*
从 url 列表爬取公司详细信息
*/
private static int foundAndSyncToDB() throws Exception
{
DBInit dBInit = new DBInit("db.init");
if(!dBInit.init())
{
System.out.println("数据库初始化失败");
return 0;
}
final Map
urls = new HashMap
(){
{
put("xxx",1); //第一个元素是公司 url,第二个元素是该 url 页面的公司的类型
put("xxxx",1);
put("xxxxx",1);
put("xxxxxx",1);
put("yyy",2);
put("zzz",3);
put("zzzzz",3);
}
};
Connection con = dBInit.getConnection();
PreparedStatement pre;
pre = con.prepareCall("insert into tbl_company_stock values (?,?,?,?)");
int founderCount = 0;
HashMap
> stocks;
Set
keys = urls.keySet(); for (String url : keys) { AutoStockFounderImpl skn = AutoStockFounderImpl.RemoteFileStock(url); stocks = skn.getStocks(); Set
cols = stocks.keySet(); pre.setInt(3, urls.get(url)); for (String col : cols) { pre.setString(1, col); pre.setString(2, stocks.get(col).get(0)); pre.setString(4, stocks.get(col).get(1)); try { if(!pre.execute() && 0 != pre.getUpdateCount()) { ++founderCount; } } catch (SQLException) { //处理数据库查询发生的异常,不向外层扩散。 } } } pre.close(); return founderCount; }