開發經驗分享 一、 編碼
1. 約束自己,規範編碼習慣
充足的代碼注釋、標準縮排的格式、注意命名規範。參考《開發管理規範》
"看上去"專業能促進代碼品質。越是難看的代碼,在它的演化過程中會越來越差。因為當你看到你要bugfix的代碼很亂,那麼在bugfix的時候一般也會草草了事。一個方法有400行,在修改這個方法的時候大家不會在意再加上幾十行代碼。因為它看起來很差,它就是很差,我沒必要美化它。反過來說,如果待改的代碼很整潔很規範,那麼修改者也會比較小心。
2. 避免冗長的方法和類
應將方法設計成簡要的、功能性單元,用它描述和實現一個不連續的類介面部分。理想情況下,方法應簡明扼要。若長度很大,可考慮通過某種方式將其分割成較短的幾個方法。這樣做也便於類內代碼的重複使用(有些時候,方法必須非常大,但它們仍應只做同樣的一件事情)。
3. 不要向標準輸出寫無用資料
System.out.println(); 此語句在程式中隨處可見,都是在調試時使用的,在程式正式運行時也沒有將其去掉,結果就是日誌中有大量的無用資料,不僅使得日誌不易分析,也增加了系統的開銷。
如要調試可用LogWriter,將WEB-INF/ini/merpserver.ini中loglevel設為4
4. 調用方法前注意參數校正,判斷參數是否為空白或無意義的值
5. 使用對象前,檢查對象是否為空白
if(names!=null&&names.length>0){
String[] nameArray = names.split(‘,’);
}
if(userEn!=null){
String name = userEn.getName();
}
6. 避免過多過常的建立java對象
盡量避免在經常調用的方法、迴圈中new對象,由於系統不僅要花費時間來建立對象,而且還要花時間對這些對象進行記憶體回收和處理,在我們可以控制的範圍內,最大限度的重用對象,最好能用基本的資料類型或數組來替代對象。
7. 盡量避免隨意使用類成員變數
當某個對象被定義為stataic變數所引用,那麼gc通常是不會回收這個對象所佔有的記憶體的。此時類成員變數的生命週期與類同步,如果類不卸載,那麼該對象會常駐記憶體,直到程式終止
8. 減少對變數的重複計算
如
for(int i=0;i<list.size();i++)
應該改為
for(int i=0,len=list.size();i<len;i++)
並且在迴圈中應該避免使用複雜的運算式,在迴圈中,迴圈條件會被反覆計算,如果不使用複雜運算式,而使迴圈條件值不變的話,程式將會啟動並執行更快
9. 避免不必要的建立對象
如
A a = new A();
if(i==1){
list.add(a);
}
應該改為
if(i==1){
A a = new A();
list.add(a);
}
10. 原則上迴圈裡面不要聲明對象,一律在迴圈外面聲明
for(int i=0;i<size;i++){
String title = “標題”;
}
改為
String title = null;
for(int i=0;i<size;i++){
title = “標題”;
}
11. 盡量在finally塊中釋放資源
程式中使用到的資源應當被釋放,以避免資源泄漏。這最好在finally塊中去做。不管程式執行的結果如何,finally塊總是會執行的,以確保資源的正確關閉。
12. 使用StringBuilder和StringBuffer進行字串串連
StringBuffer提供了同步機制,所以並發線程訪問是安全執行緒的,適合多線程。
StringBuilder沒有提同步機制,所以線程不安全,適合單線程,但如果是單線程的話,要比StringBuffer快。
13. 遍曆HashMap使用entrySet
當需要遍曆HashMap的時候,請盡量使用entrySet,而不要用keySet,entrySet的效率要比keySet高,實際上使用entrySet是只需要遍曆一次hash,即將key和value的映射關係放入到entry中,再取之;而keySet需要兩次遍曆hash,第一次取所有的key,第二次用key去取出對應的value。
Iterator iter = hashMap.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
String key = String.valueOf(entry.getKey());
String val = String.valueOf(entry.getValue());
}
14. 盡量緩衝經常使用的對象
儘可能將經常使用的對象進行緩衝,可以使用數組,或HashMap的容器來進行緩衝,但這種方式可能導致系統佔用過多的緩衝,效能下降。
推薦參考《開發指南》,使用EHCache
15. 使用統一的工具類
使用hanwebcommon.jar中的通用方法,具體參考《開發指南》
使用項目中已經存在的工具類,不要重複創造功能近似的類和方法,如果必要可進行擴充
如:接收參數使用Convert.getParameter(request, 參數名);
16. 減少不必要的空格和空行,參考《開發管理規範》
17. java代碼中不要出現黃色警告。注釋或刪除未使用的變數;儲存時去掉多餘的import;…
18. 前台接收Stirng型別參數,要進行跨站指令碼和sql注入過濾
Convert.getParameter(request,"keyword","",true,true);
19. 不要在jsp中實現商務邏輯,放到類中去完成
分層設計實現了軟體之間的解耦;便於進行分工;便於維護;提高軟體組件的重用;便於替換某種產品,比如持久層用的是hibernate,需要更換產品用toplink,就不用改其他業務代碼,直接把配置一改;便於產品功能的擴充;便於適用使用者需求的不斷變化。
20. 避免在迴圈體中使用try-catch 塊,最好在迴圈體外使用try--catch 塊以提高系統性
21. oracle大欄位操作
先插入一個空的clob類型 empty_clob(),然後再單獨更新clob欄位
InsertSql insql = new InsertSql( strTableName );
insql.addString("vc_name", name);
if (("oracle").equals(SysInit.getM_strDB_Type())){
insql.addClob("vc_adress");
insql.addClob("vc_path");
}else{
insql.addString("vc_adress", address);
insql.addString("vc_path", path);
}
boolean bl = Manager.doExcute(strAppID , insql.getSql());
if(bl){
if (("oracle").equals(SysInit.getM_strDB_Type())){
String[] strFieldValue = {address, path};
String[] strFieldName ={"vc_adress","vc_path"};
Manager.doClob(strAppID, strFieldName, strFieldValue, strTableName, " WHERE i_id = " + getMaxId());
}
}
22. 使用統一的<!DOCTYPE>,保證不同瀏覽器下的頁面相容
建議使用:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
23. HTML結構要完整、正確
標準的HTML文檔結構:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Insert title here</title>
……
</head>
<body>
……
</body>
</html>
其它:
<ul>
<li>……</li>
<li>……</li>
</ul>
<table>
<tr>
<td>……</td>
</tr>
</table>
24. HTML標籤要完整
標籤名稱及屬性統一使用小寫,標籤要成對出現,如:
<div id=”search”>………</div>
不用於包含內容的標籤,可在標籤結尾使用“/”標記結束,如:
<input type=”text” name=”username” value=”tony” />
<br/>
25. 標籤屬性值必須用雙引號包住
26. HTML代碼使用標準縮排
27. 指令碼每一條語句都要以分號結尾
28. 具有獨特性、不需要重複使用的樣式,使用內嵌樣式:
<div style=”title”>標題<div>
能夠重複使用的樣式,在樣式表中定義:
<li class=”menu”>菜單</li>
頁面內使用的樣式,使用內嵌樣式表:
<style type="text/css">
.menu{
color:black;
font-size:13px;
}
</style>
多個頁面公用的樣式使用鏈入外部樣式表:
<link href="../global.css" rel="stylesheet" type="text/css" />
29. 頁面內使用的指令碼函數,在head中定義:
<script type=”text/javascript”>
function checkForm(){
……
}
</script>
重複使用的指令碼函數或代碼較多的指令碼,寫到外本指令檔中,在head中連結:
<script type=”text/javascript” src=”../checkform.js” />
不影響頁面本身展示的指令碼可考慮放在body結束的位置調用,如廣告代碼:
……
<script type=”text/javascript” src=”../adv.js” />
</body>
30. 風格統一,儲存時要對代碼進行格式化,Ctrl+Shift+F
31. 養成程式最佳化的意識
現在我們經常遇到的一個問題是,程式在開發階段,執行的完全正常,找測試人員測試也沒問題,但是一到上線後,效能馬上出問題了,運行速度像蝸牛,客戶不堪忍受,為什麼。簡單的說,開發人員自測,估計也就幾條資料,測試人員測試,估計也就幾十上百條資料,一般的程式碼,在這個數量級,效能瓶頸根本就表現不出來。但是上線後,客戶的資料一般都上升到10000層級的,如果程式碼中沒有做到細微之處都很嚴謹的話,問題就馬上暴露出來了。
二、 資料庫
1. SQL語句中保留字、函數名要大寫,表明、欄位名全部小寫
如:SELECT vc_name,vc_sex,i_age FROM user WHERE i_id = 100 AND i_type = 2
2. 使用標準SQL語句,防止資料庫相容問題
3. 迴圈裡面(包括迴圈調用方法內)避免過多操作資料庫
4. 選擇最有效率的表名順序
ORACLE 的解析器按照從右至左的順序處理FROM子句中的表名,FROM子句中寫在最後的表(基礎資料表 driving table)將被最先處理,在FROM子句中包含多個表的情況下,必須選擇記錄條數最少的表作為基礎資料表。如果有3個以上的表串連查詢, 那就需要選擇交叉表(intersection table)作為基礎資料表, 交叉表是指那個被其他表所引用的表
5. 注意WHERE子句中的串連順序
ORACLE採用自下而上的順序解析WHERE子句,根據這個原理,表之間的串連必須寫在其他WHERE條件之前, 那些可以過濾掉最大數量記錄的條件必須寫在WHERE子句的末尾
6. SELECT子句中避免使用*
ORACLE在解析的過程中, 會將'*' 依次轉換成所有的列名, 這個工作是通過查詢資料字典完成的, 這意味著將耗費更多的時間
7. 減少訪問資料庫的次數,盡量大量操作資料庫,如大量刪除
ORACLE在內部執行了許多工作: 解析SQL語句, 估算索引的利用率, 綁定變數 , 讀資料區塊等
8. 避免在WHERE子句中使用in,not in,or 或者having
可以使用 exist 和not exist代替 in和not in
9. 用WHERE子句替換HAVING子句
避免使用HAVING子句, HAVING 只會在檢索出所有記錄之後才對結果集進行過濾. 這個處理需要排序,總計等操作. 如果能通過WHERE子句限制記錄的數目,那就能減少這方面的開銷。
10. 用好資料庫事務
事務是指作為單個邏輯工作單元執行的一系列操作。通過將一組相關操作組合為一個要麼全部成功要麼全部失敗的單元,可以簡化錯誤恢複並使應用程式更加可靠。
Transaction ts = null;
try{
ts = new Transaction(appId);
…
…
boolean bl = ts.execute(sql);
if(bl){
bl = ts.commit();
}
if(!bl){
throw new Exception();
}
}catch(Exception e){
if(ts!=null)
ts.roolback();
bl = false;
}
11. 注意SQL執行效率,考慮單表記錄10W以上的運行效果
setup中,日誌顯示層級為警告以上時,執行時間超過300ms的SQL語句,會在日誌中輸出warning
12. 索引
參考《開發管理規範》
三、 項目開發
1. 需求:
1) 需求最終需要開發人員在產品中實現,開發不合理的設計會浪費時間,開發技術無法實現的設計帶來最大的痛苦:失敗。所以,開發人員要重視需求以及需求評審,提出自己能夠想到的所有異議;
2) 開發人員不但要做好需求分析,還要做出精確的估計。因為編碼工作保質保量的按時完成需要多方的準備工作,技術痛點需要進行充分的技術預言,不熟悉的依賴平台或類庫要進行熟悉;
2. 計劃:一棟樓很難估算重量,但是一塊磚頭可以精確估算重量。一個項目的時間很難準確的估計,但把項目開發劃分為不能再進行分割的模組功能點,對每個點的估計是可以更精準的估時的,由此由上至下,由下至上,可以得出近乎準確的開發時間。
3. 設計:
1) 一圖勝萬言,模組結構以及流程等很難用用文字描述,即使用文字描述出來也很難看懂,所以在設計中,要善用用圖;
2) 詳細設計過程中有思考的痛苦,繁瑣的痛苦,但是繞過這些痛苦,編碼期間將會面臨更大的痛苦,要以快樂的心態面對。
4. 編碼:
1) 對於一個實現可以有很多解決方案,花些時間精力選取你認為最好的解決方案可以總體上提高工作成效,往往還可以得到使用者更好的體驗效果;
2) 細緻認真嚴謹的工作即是對工作負責,更是對自己負責,讓這些成為習慣。任何一次,任何時候所進行的編碼工作,在邏輯、風格、簡單有效等方面都儘可能的做到最好,既能更好為公司實現價值,同時更有利自己在技能,崗位的進步;
3) 簡單是美,在有效前提下,越是簡單的處理方法越是珍貴的,代碼編寫也是,簡單的代碼便於理解維護,同時不容易產生錯誤
4) 慎做改動,當然不是說不做改動或不鼓勵改動,而是不做倉促、草率的代碼改動。沒有洞察全域,考慮全面,而倉促進行的改動往往沒有達到改動的目的卻帶來了其他問題
5) 模組的效能不是減少一行或幾行執行代碼所能提高的,效能的最佳化首先是從演算法上考慮,降低時間複雜度,然後從執行邏輯入手,減少迴圈執行代碼的執行次數
6) 關鍵地方要列印日誌輸出到檔案中,在運行過程中不斷檢查日誌,發現任何異常都要檢查原因並修改
5. 測試:
1) 事出有因,任何bug都是由於代碼的疏漏造成的,利用排除法或跟蹤調試代碼等方法找到疏漏所在;
2) 遇到自身模組相關問題首先檢查自己,相互推諉只會浪費時間以及減弱在其他同事對你的信任;
3) 站的高看得遠,不同的視角有不同的風景。遇到比較難解決的問題而苦苦沒有思路時,轉換思路或把問題的考慮範圍放的更廣一點,往往可以找到解決方案
4) 功能提交測試前或bug修複提交驗證前,開發人員都要自己詳細的測試一下,驗證無誤再提交。(參考附錄2--測試常見問題)
6. 其它:
1) 善於及時的溝通。在項目的整個流程過程中,遇到他人的問題或自己解決不了的問題,切忌堆在自己心裡,要及時找問題解決方進行溝通,尋求解決方案
2) 善於發現並學習別人的長處。作為開發人員,我們在追求接近完美的同時,也需要學會欣賞別人的長處,發現別人的優點,並學習別人的優點,轉化為自己的潛質,這樣,我們才可以進步的更快,更全面
3) 善於協助他人解決問題以及進行知識經驗的分享,更有利於自己的提高,同時還可以獲得他人的尊重 四、 關於測試
1. 在整個專案計劃中,測試時間安排的合理性,對測試階段的情況應作充分預計,不可為了趕發布點而忽略品質。
2. 務必清楚產品包、更新包、bug包的提交規範。具體請參照《開發規範手冊》。不要出現測試過程中提交多個bug包,或者提交安裝包給測試人員更新的情況。
3. 提包時請先檢查,路徑是否正確、檔案是否完整、設定檔是否應該提交,原始碼修改記錄描述是否完善。之前經常出現,提包不檢查就直接提交來測試,導致出錯後環境不斷地還原或者重建新的環境。CVS使用不熟悉,提交檔案反覆出錯。
4. 產品品質需要嚴格控制,自己承認是問題的情況下,請不要試圖和測試商談希望可以不被追究。
5. 請注意提高修改bug的品質,目前,修改一個bug而引發更多的bug的情況特別多。
6. bug修改完成後,在提交測試前請自己先驗證通過後再提交,請不要修改完成後不驗證就直接提交給測試人員,防止導致bug被反覆reopen的情況。
7. 原則上不允許不通過CVS而直接提交檔案給測試人員調試問題、尋找原因。如果確有必要,測試人員可以協助調試,但次數不宜過多,防止測試環境版本難以控制。
8. bug是否存在的衡量標準以測試環境為準,不建議出現“我這邊是好的”這樣的解釋。
9. 修改bug時請修改完整,可能會有多個小問題提交在一個BUG裡面,BUG修改時多個小問題的地方均需修改。
10. 更新檔案必須通過配置發布,無論是否經過測試都不允許直接發給項目或直接更新客戶服務器
五、 開發環境
1. Eclipse記憶體溢出、崩潰、無響應、啟動慢
1) 建立多個workspace
2) 將workspace中暫時不用的工程close
3) 修改eclipse設定檔eclipse.ini,參考知識庫:
-Xms512m
-Xmx512m
-XX:PermSize=96m
-XX:MaxPermSize=96m
2. Eclipse統一字元集為UTF-8
1) 進入Eclipse->Window->Preferences->General->Workspace介面,在“Text file encoding”中選擇“other”並在下拉式清單中選擇“UTF-8”,並點擊【Apply】後,點擊【OK】確認儲存
2) 修改jsp檔案頭部
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
將字元集改為“UTF-8”
3) 進入Eclipse->Window->Preferences->Web->JSP Files介面,將“Encoding”屬性修改為“ISO 10646/Unicode(UTF-8)”
4) 已建立的項目,右鍵點擊項目名稱,進入Properties->Resource介面,選擇“Text file encoding”編碼為“UTF-8”
3. 開啟Eclipse記憶體監視及回收外掛程式
進入Eclipse->Window->Preferences->General介面,勾選“Show heap status”,點擊【Apply】後,點擊【OK】確認儲存。Eclipse右下角,會顯示JVM記憶體使用量情況。
點擊垃圾桶表徵圖,可進行手動回收
4. 設定斷點,進行調試
1) 行斷點
行斷點是最普通的斷點。只要在Eclipse Java 編輯區的行頭雙擊就會得到一個斷點,代碼會運行到此處時停止
2) 條件斷點
條件斷點,顧名思義就是一個有一定條件的斷點,只有滿足了使用者佈建的條件,代碼才會在運行到斷點處時停止。
在斷點處點擊滑鼠右鍵,選擇最後一個"Breakpoint Properties"
斷點的屬性介面及各個選項的意思如下圖,
3) 變數斷點
斷點不僅能打在語句上,變數也可以接受斷點
下圖就是一個變數的打的斷點,在變數的值初始化,或是變數值改變時可以停止,當然變數斷點上也是可以加條件的,和條件斷點的設定是一樣的
4) 方法斷點
方法斷點就是將斷點打在方法的入口處,方法斷點的特別之處在於它可以打在 JDK的源碼裡,由於 JDK 在編譯時間去掉了調試資訊,所以普通斷點是不能打到裡面的,但是方法斷點卻可以,可以通過這種方法查看方法的調用棧
5) 類型載入斷點
可以查看什麼時候載入的類
6) 異常斷點
當異常發生時,代碼會停在異常發生處,定位問題時應該比較有協助
開啟Breakpoints視圖,點擊按鈕,增加異常斷點
5. 修改代碼字型
進入Eclipse->Window->Preferences->General->Appearance->Colors and Fonts介面,選擇Basic->Text Font,點擊右側【Edit…】,選擇。點擊【Apply】後,點擊【OK】確認儲存
6. Eclipse快速鍵,見“附錄1—Eclipse快速鍵”
六、 Linux
見《Linux_命令詳解》
常用命令:
cd 變換工作目錄
cd / 返回根目錄 cd ../ 返回上層目錄
ls 顯示指定工作目錄下的內容
mv 將一個檔案移至另一個檔案,或將數個檔案移至另一個目錄
mv /data/m3 /root 將/data/m3轉移到/root目錄下
mkdir 建立指定的目錄名
mkdir /root/temp 在root目錄下建立temp目錄
rm 刪除檔案及目錄
rm –rf 檔案/檔案夾 徹底刪除檔案/檔案夾
rmdir 刪除空的目錄
kill 終止進程
kill -9 2342 殺掉進程標識號PID為2342的進程
ps 報告進程狀況
ps –ef|grep java 查看java進程
tail 從指定點開始將指定的檔案寫到標準輸出
tail –f nohup.out 即時讀取日誌nohup.out的更新內容
su 變更為其它使用者的身份
su – user1 切換到使用者user1
cp 複製
cp config.xml /data/ 將config.xml檔案複製到/data/目錄下
reboot 重啟伺服器
halt 關閉伺服器
top 顯示目前正在系統中執行的程式
free 顯示系統記憶體與swap使用的情況
java –version 查看JDK版本
chromd 777 /temp/runthread.sh 賦予使用者/temp/runthread.sh最大許可權
cat /dev/null > nohup.out 清空日誌nohup.out檔案內容
df –hl 查看磁碟空間與使用方式
du 顯示目錄下所有檔案並列出檔案大小
pwd 查看當前所在路徑
export lang=zh_CN 修改環境變數lang值,解決linux亂碼
./startup.sh tomcat啟動
nohup ./startWeblogic.sh & weblogic啟動(不掛斷運行,防止登出後weblogic停止)
./startServer.sh server1 websphere啟動
七、 常見問題
1. jsp出現getOutputStream() has already been called for this response
jsp中出現此錯誤一般都是在jsp中使用了輸出資料流(如輸出圖片驗證碼,檔案下載等),沒有妥善處理好造成的。
jsp編譯成servlet之後在函數</