編寫高質量代碼改善java程序的151個建議——[110-117]異常及Web項目中異常處理

編寫高質量代碼改善java程序的151個建議——[110-117]異常及Web項目中異常處理

原創地址:http://www.cnblogs.com/Alandre/(泥沙磚瓦漿木匠),須要轉載的,保留下! 文章宗旨:Talk is cheap show me the code.html

大成若缺,其用不弊.大盈若衝,其用不窮.  <道德經-老子>
最完滿的東西,好似有殘缺同樣,但它的做用永遠不會衰竭;最充盈的東西,好似是空虛同樣,可是它的做用是不會窮盡的

Written In The Font

 

摘要:java

學習內容:sql

思考:函數

 

何爲異常處理?

異常處理,英文名爲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);
    }
}

複製代碼

 

天然還有些什麼攔截器拋出異常,在這裏就不詳細展開討論了.

 

Editor's Note

異常對我說:」用對了地方就好,用錯了地方就很差。」
相關文章
相關標籤/搜索