> For the complete documentation index, see [llms.txt](https://yulinliu.gitbook.io/share/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://yulinliu.gitbook.io/share/fang.md).

# 預防錯誤

## **Null 判斷**

透過Null 判斷處理，可以避免後續的程式錯誤\
像是前端透過HTTP傳送到後端的參數

有些人認為從DB 或 其他 API 取得的資料，不需要做Null判斷\
這是一種錯誤的關念，因為你取得的查詢參數有可能是被造假的\
甚至有可能DB的資料被刪除了

這時你沒加Null判斷，這部分的程式在被大量執行後\
可能就會導致系統異常，甚至整個崩潰

## **參數轉型問題**

前端透過HTTP傳送參數到後端，這時後端收到的參數皆為String格式\
有些參數預期為數值型態，但只要傳入的參數為不允許的內容，將會導致型態轉換錯誤\
`Integer.parseInt("not integer")`

這時可以使用Regular expressions來幫忙過濾不允許內容，防止型態轉換錯誤\
`public static boolean isInteger(String value) {`\
&#x20;   `Pattern pattern = Pattern.compile("^[-+]?\\d+$");`\
&#x20;   `return pattern.matcher(value).matches();`\
`}`

## SQL Injection**參數攻擊**

某些駭客會利SQL Injection來攻擊你的網站系統，有些弱點掃瞄工具也有此功能

如果你查詢DB的SQL是用字串組合的方式，那就有很大的機會被攻擊\
`String query_id = request.getParameter("id");`\
`String name = request.getParameter("name");`\
`String sql = "update user set name='"+name+"' where id='"+query_id+"'";`\
\
`Statement stmt = conn.createStatement();`\
`int updateCount = stmt.executeUpdate(sql);`

這時候可以利用Regular expressions來幫忙過濾不允許內容，再使用PreparedStatement的方式\
就能將被攻擊的風險降到最低\
`String query_id = request.getParameter("id");`\
`String name = request.getParameter("name");`\
`String sql = "update user set name=? where id=?";`\
\
`PreparedStatement stmt = conn.prepareStatement(sql);`\
`stmt.setString(1, name);`\
`stmt.setInt(2, Integer.parseInt(query_id));`\
\
`int updateCount = stmt.executeUpdate();`

## **Thread Safe**

只要程式是在生命週期結束前，會被不同Thread 同時修改全域變數\
就會存在None Thread Safe的問題

網路上的文章或書籍上，教的通常是用synchronized來解決\
但這種方法僅適合在同一個jvm上執行的程式\
而且在並發量大時，就會有效能的問題

我比較喜歡將這些有問題的程式，直接改寫成不使用全域變數的方式\
既不會有None Thread Safe的問題，也不會有並發效能問題

而在不同jvm上執行的系統，無法用synchronized來解決None Thread Safe的問題\
這時就需要使用Distributed locks的機制，有很多系統有提供\
像是Redis、Infinispan、JGroups

## **Connection close**

如果您的JDBC Connection 在程式執行結束前，都未呼叫close 的method\
將會導致Connection無法被回收, 多執行幾次後DB或Connection Pool的連線資源就會被佔滿\
進而丟出一些無法取得連線的錯誤訊息，導致系統異常

有時您有寫close， 但程式出現異常，導致未正常呼叫到close 的 method\
最安全的寫法就是使用try catch finally的方式，來避免這種情形\
`Connection conn = null;`\
`try {`\
\
`} catch(Exception e) {`\
`} finally {`\
&#x20;   `if(conn != null) conn.close();`\
`}`

### 用了Spring Proxy的機制來關閉Connection，為何還是會出現 JDBC Connection 未關閉的問題？

您可以參考這篇[文章](https://www.ibm.com/developerworks/cn/java/j-lo-spring-ts3/index.html)來了解更深入的問題。

## Object Close Method

InputStream、OutputStream、HTTP Connection、Socket... \
除上方列的這些Object, 還有很多物件本身會提供close的方法\
遇到這些，最好都需要自行呼叫Close，避免無法被gc 與connection 未關閉的情形

如果有使用到Graphics 這個物件來處理圖檔，需要自行呼叫dispose，否則會有無法被gc的問題

## **Connection Timeout**

有很多程式取得資料是用網路連線的方式，像是:\
HTTP Connection、JDBC Connection、Socket、RPC、FTP、Redis...\
\
只要是透過網路取得資料就會有網路連線問題，如果您使用的Client程式未設定Connection Timeout\
就會因為網路問題或者連接主機不回應，而出現問題\
造成Client端的Thread卡住，有問題功能執行多次之後\
就會讓系統卡住無法回應

大部分的Timeout 設定，內網系統 10s  外網30s 就已經足夠\
Timeout設太長，只會增加系統卡住的機會\
當然還需要參考Server端系統的回應時間

有些Http Client library還有分 Read Timeout、Write Timeout\
這些詳細的部分都需要參考相關文件，多了解如何正確的使用\
才能避免無法預期的錯誤發生

## T**ry Catch Finally**

有些時候會因為硬體或網路環境出現異常，導致程式出現無法預期的錯誤，造成部分資源無法回收\
用這個方式能夠讓系統的穩定性提高，印出錯誤訊息，更可加快問題排除。

### 範例比較

在同樣的運作邏輯，讓程式丟出異常，何種能夠提供較好的結果？\
這邊提供一個簡單的範例來比較 (JVM 記憶體上限皆為64MB)

[NonCatchError.java](https://github.com/yulinliu/stability/blob/master/src/main/java/handle/NonCatchError.java) 未捕捉到異常\
會直接讓主要的Thread崩潰

[CatchError.java](https://github.com/yulinliu/stability/blob/master/src/main/java/handle/CatchError.java) 捕捉到異常\
雖然會印出異常，但是主要的Thread可以正常執行完
