原創地址:http://www.cnblogs.com/Alandre/(泥沙磚瓦漿木匠),需要轉載的,保留下! 文章宗旨:Talk is cheap show me the code.
大成若缺,其用不弊.大盈若沖,其用不窮. <道德經-老子>
最完滿的東西,好似有殘缺一樣,但它的作用永遠不會衰竭;最充盈的東西,好似是空虛一樣,但是它的作用是不會窮盡的
Written In The Font
摘要:
學習內容:
思考:
何為異常處理?
異常處理,英文名為exceptional handling, 是代替日漸衰落的error code方法的新法,提供error code 所未能具體的優勢。異常處理分離了接收和處理錯誤代碼。這個功能理清了編程者的思緒,也幫助代碼增強了可讀性,方便了維護者的閱讀和理解。
java語言中,異常處理可以確保程序的健壯性,提高系統的可用率.但是java api 提供的異常都是比較低級的,所以有了'提倡異常封裝’
提倡異常封裝
異常封裝有三個優點: 1)提高系統的友好性 2)提高性能的可維護性 3)解決java異常機制自身的缺陷
- 提高系統的友好性
系統的友好性,就像系統和開發人員等握手與交流.好的系統對象,會展現出交流時候所需要的一切.因此,良好的友好性需要良好的代碼告訴開發人員和用戶.開發人員要找需要打印出堆棧信息.
show the code:
public void doStuff() throws MyBussinessException { try { InputStream iStream = new FileInputStream("無效文件.txt"); } catch (FileNotFoundException e) { e.printStackTrace(); throw new MyBussinessException(e);//此處自定義一個MyBussinessException } }
::throw new MyBussinessException(e);
在這里,無效文件導致了兩點:文件未找到和該業務出現問題.因此,在文件未找到之后我們可以繼續根據需要告之其他異常.
- 提高性能的可維護性
如何提搞可維護性,大家不能一味的進行這樣操作,就拋出Exception,這樣會導致別人看你的代碼完全check不到異常點.下面的代碼是不可取的:
public void doStuff() { try { //do something } catch (Exception e) { e.printStackTrace(); } }
正確的做法是對異常進行分類處理,並進行封裝輸出.
show the code:
public void doStuff() { try { //do something } catch (FileNotFoundException e) { log.info("文件未找到!文件為:xxx"); } catch (SecurityException e) { log.error("無權訪問,原因:xxx"); e.printStackTrace(); } }
catch{}
catch{}
這樣子,直接在代碼層級上分析即可,代碼異常在哪里拋出.維護人員看到這樣的異常也會有了准確的判斷.
- 解決java異常機制自身的缺陷
先拋出個問題:例如注冊時,要對很多進行檢驗.像密碼,用戶名,郵箱…...這樣情況下,我們必須要在一個注冊方法里面拋出很多個異常.因此,正確的方法是封裝異常,建立異常容器,一次性對某對象進行校驗,然后返回所有異常.
show the code
異常容器:
import java.util.ArrayList; import java.util.List; public class MyException extends Exception { //容納所有異常 private List<Throwable> causes = new ArrayList<Throwable>(); //構造函數 public MyException(List<? extends Throwable> _causes) { causes.addAll(_causes); } //讀取所有的異常 public List<Throwable> getExceptions() { return causes; } }
處理異常:
public static void doStuff() throws MyException { List<Throwable> list = new ArrayList<Throwable>(); //第一個邏輯片段 try { //Do Something } catch (Exception e) { list.add(e); } //第二個邏輯片段 try { //Do Something two } catch (Exception e) { list.add(e); } //檢查是否有必要拋出異常 if(list.size() > 0) { throw new MyException(list); } }
采用異常鏈傳遞異常
我們做的JEE項目時候,一般會有三層的結構:持久層,邏輯層,展現層.異常也是如此的,當我們各個層之間傳遞異常,我們就需要先封裝,然后傳遞.簡要的就是采用異常傳遞異常:
show the code:
[摘自源碼分析]
/** * * 執行 SQL 查詢。 * * @param isCallable : 是否使用 CallableStatment 執行查詢 * @param sql : 查詢語句 * @param params : 查詢參數 * @return 結果集 * */ protected List<Object[]> query(boolean isCallable, String sql, Object ... params) { List<Object[]> result = new ArrayList<Object[]>(); ResultSet rs = null; PreparedStatement pst = null; try { Connection conn = getSession(); pst = JdbcUtil.prepareStatement(conn, sql, isCallable); JdbcUtil.setParameters(pst, params); rs = pst.executeQuery(); int cols = rs.getMetaData().getColumnCount(); while(rs.next()) { Object[] objs = new Object[cols]; for(int i = 0; i < cols; i++) objs[i] = rs.getObject(i + 1); result.add(objs); } } catch (SQLException e) { throw new JdbcException(e); } finally { JdbcUtil.closeSqlObject(pst, rs); } return result; }
從中我們可以抽取的看到:
catch (SQLException e) { throw new JdbcException(e); }
jdbc執行SQL語句查詢的時候,先拋出SQLException ,然后就像一條鏈一樣,下一步告訴別人是JDBC的異常.下面體會經典,休息下:
大直若屈,大巧若拙,大辯若訥。躁勝寒,靜勝熱,清靜為天下正。<道德經-老子>
最正直的東西,好似有彎曲一樣;最靈巧的東西,好似最笨拙的;最卓越的辯才,好似不善言辭一樣。清靜克服擾動,賽冷克服暑熱。清靜無為才能統治天下
受檢異常盡可能轉化為非受檢異常
所有受檢異常(Checked Exception)是好事,為何要盡可能轉化為非,也就是(Unchecked Exception)呢?我的理解是:受檢異常威脅到系統的安全性,穩定性,可靠性,正確性時,不能轉換為非受檢異常.
也就是說,其中存在的受檢異常有缺點,轉換成Unchecked Exception就輕松解決了.
- 受檢異常使接口聲明脆弱
show the code:
interface User { //修改用戶名密碼,拋出安全異常 public void changePass() throws MySecurityException; }
throws MySecurityException;
這里面不一定只是一個異常,然而定義了異常,會增加了接口的不穩定性,這就存在了面向對象設計的嚴重褻瀆,如果要改變的話,又破壞了封裝性.
另外,受檢異常還有兩個缺點:
- 受檢異常使代碼可讀性降低
- 受檢異常增加了開發工作量
多使用異常,把性能問題放一邊
“性能問題不是拒絕異常的借口” 就當一個常見的登錄用例.我們經常會添加一個例外的事件:”連續登錄3次登錄失敗,暫時鎖定用戶帳號.”這樣這個例外的事件就是一個異常處理.
show the code
public void login() { try { // 正常登錄 } catch (InvalidLoginNameException e) { // 用戶名無效 } catch (InvalidPasswordException e) { // 密碼錯誤 } catch (TooManyLoginsException e) { // 多次登錄失敗 } }
這樣子一來,代碼邏輯很清晰.但是這樣子就拋出了一個主意.這樣子有代價:
性能比較慢
java的異常處理機制確實比較慢,這個性能慢是相對的.相對那些基礎類型:String Integer…等.有人測試結果如下:
測試結果: (運行環境:xen虛擬機,5.5G內存,8核;jdk1.6.0_18) (10個線程,創建10000000個對象所需時間) 普通Java對象 45284 MS 普通java異常 205482 MS 改進的Java業務異常 16731 MS
相當於創建每個異常對象是普通對象的五倍.但是數量級上是 MS,在一個系統中,如此微小的性能消耗是可以允許的.
java web 中的異常處理
經驗之談:”用對了地方就好,用錯了地方就不好。”這是我的師傅跟我說的,他教會了很多.太很抽象,我想我會慢慢學會的.
實際J2EE項目中,通常一個頁面請求到達后台以后,首先是到MVC中的controller,在controller層會調用業務邏輯層service,而在service層會調用持久層dao進而獲得數據,再將獲得的數據一層一層返回到controller層,然后通過controller控制層轉發到指定頁面.
可能存在的異常:
- dao層可能會發生SQLException異常
- service層可能會發生NullPointException異常,
- controller層可能會發生IOException異常,RuntimeException異常
正確的處理方式
根據上面知識的說法:我們該用以下的方法來實現
show the code:
@RequestMapping(value = "/courseTeacherRelationAdd") public @ResponseBody String courseTeacherRelationAdd(String courseID,String teacherInfoID,CourseTeacherRelation courseTeacherRelation) { try { int sign = courseTeacherRelationService.saveOrUpdateCourseTeacherRelation(courseID,teacherInfoID,courseTeacherRelation); if( sign == Constant.RESULT_SUCCESS ) return successResponse("保存成功","../courseTeacherRelation/courseTeacherRelations" , "courseTeacherRelations"); } catch (Exception e) { throw new EntityException("Error! when save the entity",e); } return failResponse("保存失敗"); }
throw new EntityException("Error! when save the entity",e);
這里用了鏈式異常拋出:EntityException是自定義的異常類:
public class EntityException extends RuntimeException { private static final long serialVersionUID = 1L; public EntityException() { super(); } public EntityException(String message) { super(message); } public EntityException(String message, Throwable cause) { super(message, cause); } }
自然還有些什么攔截器拋出異常,在這里就不詳細展開討論了.