從 FingBugs的錯誤來看JAVA代碼質量(一)

[b]錯誤碼:WMI_WRONG_MAP_ITERATOR[/b]
案例一:

[img]http://dl.iteye.com/upload/attachment/506017/54bde495-1b3f-39b5-86fa-43401d2e2ec0.jpg[/img]

案例二:

[img]http://dl.iteye.com/upload/attachment/506019/9854ef6f-adf3-3a21-abd4-64c754a08d14.jpg[/img]


Bug: Method JTAMainFrame.initView(JFrame) makes inefficient use of keySet iterator instead of entrySet iterator
Pattern id: WMI_WRONG_MAP_ITERATOR, type: WMI, category: PERFORMANCE

This method accesses the value of a Map entry, using a key that was retrieved from a keySet iterator. It is more efficient to use an iterator on the entrySet of the map, to avoid the Map.get(key) lookup.


[b]解釋:[/b]
很多人都這樣遍歷Map,沒錯,但是效率很低,先一個一個的把key遍歷,然後在根據key去查找value,這不是多此一舉麼,爲什麼不遍歷entry(桶)然後直接從entry得到value呢?它們的執行效率大概爲1.5:1(有人實際測試過)。
我們看看HashMap.get方法的源代碼:
1.	public V get(Object key) {  
2. if (key == null)
3. return getForNullKey();
4. int hash = hash(key.hashCode());
5. for (Entry<K,V> e = table[indexFor(hash, table.length)];
6. e != null;
7. e = e.next) {
8. Object k;
9. if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
10. return e.value;
11. }
12. return null;
13. }

從這裏可以看出查找value的原理,先計算出hashcode,然後散列表裏取出entry,不管是計算hashcode,還是執行循環for以及執行equals方法,都是CPU密集運算,非常耗費CPU資源,如果對一個比較大的map進行遍歷,會出現CPU迅速飈高的現象,直接影響機器的響應速度,在併發的情況下,簡直就是一場災難。
[b]解決方法:[/b]
1.	for (Map.Entry<String, JMenu> entry : menuList.entrySet()) {  
2. mb.add(entry.getValue());
}




[img]http://dl.iteye.com/upload/attachment/506021/d58db580-0fcf-3697-9c68-7b9e82e0afa4.jpg[/img]


[b]錯誤碼:EI_EXPOSE_REP2[/b]

案例

[img]http://dl.iteye.com/upload/attachment/506023/663bc320-8b21-3862-9715-3ae21c812da9.jpg[/img]

Bug: SingleNePollConfigDialog.collectValues(Hashtable) may expose internal representation by storing an externally mutable object into SingleNePollConfigDialog.values
Pattern id: EI_EXPOSE_REP2, type: EI2, category: MALICIOUS_CODE

This code stores a reference to an externally mutable object into the internal representation of the object. If instances are accessed by untrusted code, and unchecked changes to the mutable object would compromise security or other important properties, you will need to do something different. Storing a copy of the object is better approach in many situations.
[b]翻譯願意:[/b]
此代碼存儲到一個到對象的內部表示外部可變對象的引用。如果實例是由不受信任的代碼,並以可變對象會危及安全或其他重要的屬性選中更改訪問,你需要做不同的東西。存儲一個對象的副本,在許多情況下是更好的辦法。

[b]解釋:[/b]
DO類實例產生之後,裏面包含的Date不是原始數據類型,導致其gmtCrate屬性不光DO實例的set方法可以改變其值,外部引用修改之後也可能導致gmtCreate 被改變,會引起可能的不安全或者錯誤。
這個是一個不好的實踐,不過我們應用裏面DO都是比較簡單使用,不太會出現這種情況。

[b]解決方法: [/b]
修改成:
public Date getGmtCreate() {
if(this.gmtCreate != null)
return new Date(this.gmtCreate.getTime()); //正確值
else
return null;
}


總結:這個其實是說可變類和不可變類的問題,
可變類:當你獲得這個類的一個實例引用時,你可以改變這個實例的內容。
不可變類:當你獲得這個類的一個實例引用時,你不可以改變這個實例的內容。不可變類 的實例一但創建,其內在成員變量的值就不能被修改 ;
DO是一個可變類,但是最好是隻提供set方法才能改變其實例的的成員變量的值,減少被修改的風險。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章