關於java程式碼質量的問題
解釋:
很多人都這樣遍歷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. }
-
1. for (Map.Entry<String, JMenu> entry : menuList.entrySet()) {
-
2. mb.add(entry.getValue());
-
}
-
public Date getGmtCreate() {
-
if(this.gmtCreate != null)
-
return new Date(this.gmtCreate.getTime()); //正確值
-
else
-
return null;
-
}
-
二、
-
錯誤碼:DM_FP_NUMBER_CTOR
Bug: Method OnlineLicenseDAOTest.testUpdateOnlineLicenseByOnlineMerchantId() invokes inefficient Double.valueOf(double) constructor; use OnlineLicenseDAOTest.java:[line 81] instead
Pattern id: DM_FP_NUMBER_CTOR, type: Bx, category: PERFORMANCE
Using new Double(double) is guaranteed to always result in a new object whereas Double.valueOf(double) allows caching of values to be done by the compiler, class library, or JVM. Using of cached values avoids object allocation and the code will be faster.
Unless the class must be compatible with JVMs predating Java 1.5, use either autoboxing or the valueOf() method when creating instances of Double and Float.
解釋:
採用new Ddouble(double)會產生一個新的物件,採用Ddouble.valueOf(double)在編譯的時候可能通過快取經常請求的值來顯著提高空間和時間效能。
解決方法:
採用Ddouble.valueOf方法
類似的案例
類似的還有:
錯誤碼:DM_NUMBER_CTOR
new Integer(int) 和 Integer.valueOf(int)
bug描述:
[Bx] Method invokes inefficient Number constructor; use static valueOf instead [DM_NUMBER_CTOR]
Using new Integer(int) is guaranteed to always result in a new object whereas Integer.valueOf(int) allows caching of values to be done by the compiler, class library, or JVM. Using of cached values avoids object allocation and the code will be faster.
說明:
[參考]http://www.cnblogs.com/hyddd/articles/1391318.html
FindBugs推薦使用Integer.ValueOf(int)代替new Integer(int),因為這樣可以提高效能。如果當你的int值介於-128~127時,Integer.ValueOf(int)的效率比Integer(int)快大約3.5倍。
下面看看JDK的原始碼,看看到Integer.ValueOf(int)裡面做了什麼優化: -
public static Integer valueOf(int i) {
-
final int offset = 128;
-
if (i >= -128 && i <= 127) { // must cache
-
return IntegerCache.cache[i + offset];
-
}
-
return new Integer(i);
-
}
-
private static class IntegerCache {
-
private IntegerCache(){}
-
static final Integer cache[] = new Integer[-(-128) + 127 + 1];
-
static {
-
for(int i = 0; i < cache.length; i++)
-
cache = new Integer(i - 128);
-
}
-
}
-
從原始碼可以知道,ValueOf對-128~127這256個值做了快取(IntegerCache),如果int值的範圍是:-128~127,在ValueOf(int)時,他會直接返回IntegerCache的快取給你。
所以你會看到這樣的一個現象: -
public static void main(String []args) {
-
Integer a = 100;
-
Integer b = 100;
-
System.out.println(a==b);
-
Integer c = new Integer(100);
-
Integer d = new Integer(100);
-
System.out.println(c==d);
-
}
-
結果是:
true
false
因為:java在編譯的時候 Integer a = 100; 被翻譯成-> Integer a = Integer.valueOf(100);,所以a和b得到都是一個Cache物件,並且是同一個!而c和d是新建立的兩個不同的物件,所以c自然不等於d。
再看看這段程式碼: -
public static void main(String args[]) throws Exception{
-
Integer a = 100;
-
Integer b = a;
-
a = a + 1; //或者a++;
-
System.out.println(a==b);
-
}
-
結果是:false
因為在對a操作時(a=a+1或者a++),a重新建立了一個物件,而b對應的還是快取裡的100,所以輸出的結果為false。
錯誤碼:DM_BOOLEAN_CTOR
Bug: Bad attempt to compute absolute value of signed 32-bit hashcode
Pattern id: RV_ABSOLUTE_VALUE_OF_HASHCODE, type: RV, category: CORRECTNESS
This code generates a hashcode and then computes the absolute value of that hashcode. If the hashcode is Integer.MIN_VALUE, then the result will be negative as well (since Math.abs(Integer.MIN_VALUE) == Integer.MIN_VALUE).
One out of 2^32 strings have a hashCode of Integer.MIN_VALUE, including "polygenelubricants" "GydZG_" and ""DESIGNING WORKHOUSES".
解釋:
此程式碼產生的雜湊碼,然後計算該雜湊碼的絕對值。如果雜湊碼是Integer.MIN_VALUE的,那麼結果將是負面的,以及(因為Math.abs(Integer.MIN_VALUE的)==Integer.MIN_VALUE的)。
解決方法:
在使用之前判斷一下是否是為Integer.MIN_VALUE -
int iTemp = sellerId.hashCode();
-
if(iTemp != Integer.MIN_VALUE) {
-
number = Math.abs(iTemp) % 12;
-
} else {
-
number = Integer.MIN_VALUE % 12;
-
}
-
三、
-
錯誤碼:SE_NO_SERIALVERSIONID
Bug: WindowHandlerManager$MySingleSelectionModel is Serializable; consider declaring a serialVersionUID
Pattern id: SE_NO_SERIALVERSIONID, type: SnVI, category: BAD_PRACTICE
This class implements the Serializable interface, but does not define a serialVersionUID field. A change as simple as adding a reference to a .class object will add synthetic fields to the class, which will unfortunately change the implicit serialVersionUID (e.g., adding a reference to String.class will generate a static field class$java$lang$String). Also, different source code to bytecode compilers may use different naming conventions for synthetic variables generated for references to class objects or inner classes. To ensure interoperability of Serializable across versions, consider adding an explicit serialVersionUID.
解釋:
實現了Serializable介面,卻沒有實現定義serialVersionUID欄位,序列化的時候,我們的物件都儲存為硬碟上的一個檔案,當通過網路傳輸或者其他類載入方式還原為一個物件時,serialVersionUID欄位會保證這個物件的相容性,考慮兩種情況:
1. 新軟體讀取老檔案,如果新軟體有新的資料定義,那麼它們必然會丟失。
2. 老軟體讀取新檔案,只要資料是向下相容的,就沒有任何問題。
序列化會把所有與你要序列化物件相關的引用(包括父類,特別是內部類持有對外部類的引用,這裡的例子就符合這種情況)都輸出到一個檔案中,這也是為什麼能夠使用序列化能進行深拷貝。這種序列化演算法給我們的忠告是,不要把一些你無法確定其基本資料型別的物件引用作為你序列化的欄位(比如JFrame),否則序列化後的檔案超大,而且會出現意想不到的異常。
解決方法:
定義serialVersionUID欄位
錯誤碼:DM_GC
bug: DBExportTask2.exportDBRecords(DBExportProperty, String) forces garbage collection; extremely dubious except in benchmarking code
Pattern id: DM_GC, type: Dm, category: PERFORMANCE
解釋:
有兩點:
1. System.gc()只是建議,不是命令,JVM不能保證立刻執行垃圾回收。
2. System.gc()被顯示呼叫時,很大可能會觸發Full GC。
GC有兩種型別:Scavenge GC和Full GC,Scavenge GC一般是針對年輕代區(Eden區)進行GC,不會影響老年代和永生代(PerGen),由於大部分物件都是從Eden區開始的,所以Scavenge GC會頻繁進行,GC演算法速度也更快,效率更高。但是Full GC不同,Full GC是對整個堆進行整理,包括Young、Tenured和Perm,所以比Scavenge GC要慢,因此應該儘可能減少Full GC的次數。
解決方法:
去掉System.gc()
錯誤碼:DP_DO_INSIDE_DO_PRIVILEGED
Bug: com.taobao.sellerservice.core.test.BaseTestJunit.autoSetBean() invokes reflect.Field.setAccessible(boolean), which should be invoked from within a doPrivileged block
Pattern id: DP_DO_INSIDE_DO_PRIVILEGED, type: DP, category: BAD_PRACTICE
This code invokes a method that requires a security permission check. If this code will be granted security permissions, but might be invoked by code that does not have security permissions, then the invocation needs to occur inside a doPrivileged block.
此程式碼呼叫一個方法,需要一個安全許可權檢查。如果此程式碼將被授予安全許可權,但可能是由程式碼不具有安全許可權呼叫,則需要呼叫發生在一個doPrivileged的塊。
解決方法:一般情況下,我們並不能對類的私有欄位進行操作,利用反射也不例外,但有的時候,例如要序列化的時候,我們又必須有能力去處理這些欄位,這時候,我們就需要呼叫AccessibleObject上的setAccessible()方法來允許這種訪問,而由於反射類中的Field,Method和Constructor繼承自AccessibleObject,因此,通過在這些類上呼叫setAccessible()方法,我們可以實現對這些欄位的操作。但有的時候這將會成為一個安全隱患,為此,我們可以啟用java.security.manager來判斷程式是否具有呼叫setAccessible()的許可權。預設情況下,核心API和擴充套件目錄的程式碼具有該許可權,而類路徑或通過URLClassLoader載入的應用程式不擁有此許可權。例如:當我們以這種方式來執行上述程式時將會丟擲異常
增加:} catch (SecurityException e) {
錯誤碼:NP_NULL_ON_SOME_PATH
Bug: Possible null pointer dereference of busCatId
Pattern id: NP_NULL_ON_SOME_PATH, type: NP, category: CORRECTNESS
There is a branch of statement that, if executed, guarantees that a null value will be dereferenced, which would generate a NullPointerException when the code is executed. Of course, the problem might be that the branch or statement is infeasible and that the null pointer exception can't ever be executed; deciding that is beyond the ability of FindBugs.
解釋:
方法中存在空指標
解決方法:
增加欄位busCatId為空的判斷 -
四、
-
五、
-
REC_CATCH_EXCEPTION
Bug: Exception is caught when Exception is not thrown
Pattern id: REC_CATCH_EXCEPTION, type: REC, category: STYLE
This method uses a try-catch block that catches Exception objects, but Exception is not thrown within the try block, and RuntimeException is not explicitly caught. It is a common bug pattern to say try { ... } catch (Exception e) { something } as a shorthand for catching a number of types of exception each of whose catch blocks is identical, but this construct also accidentally catches RuntimeException as well, masking potential bugs.
這想寫會無意中把RuntimeException也捕獲了,有可能導致潛在的bug。 JVM對RuntimeException有統一的捕獲機制,讓JVM來處理它。
在try/catch塊中捕獲異常,但是異常沒有在try語句中丟擲而RuntimeException又沒有明確的被捕獲
1.比較推薦的寫法一般如下:
2.捕獲了異常,一定要處理異常
還有人在catch裡面什麼都不寫,就寫上
3.避免在大的語句塊裡面寫try,catch,因為本身也比較耗費時間,而