跳到主要內容

發表文章

目前顯示的是有「SpotBugs」標籤的文章

SpotBugs - EI_EXPOSE_REP2

Description EI2: Expose internal representation by storing an externally mutable object 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. 將mutable object直接assign,會被怎樣存取無法得知,可能會造成來源一起被修改。 Solution 在assign時,使用copy constructor去建立一份新的拷貝。 Example Before: public void setLeftDate ( Date aDate ) { mLeftDate = aDate ; } After: public void setLeftDate ( Date aDate ) { mLeftDate = aDate != null ? new Date ( aDate. getTime ( ) ) : aDate ; } 因為直接使用getTime,必須小心傳入值為null。

SpotBugs - EI_EXPOSE_REP

 Description EI: May expose internal representation by returning reference to mutable object Returning a reference to a mutable object value stored in one of the object's fields exposes 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. Returning a new copy of the object is better approach in many situations. 回傳mutable object可能會造成被外部程式修改。 Solution 拷貝一份新的回去或改用immutable object。 Example Before: public Date getLeftDate ( ) { return mLeftDate ; } After: public Date getLeftDate ( ) { if ( mLeftDate == null ) return null ; return new Date ( mLeftDate. getTime ( ) ) ; } Array的情況最簡單的方法是用Arrays.copyOf。而因為直接使用getTime,必須小心null的情況。

SpotBugs - BIT_IOR_OF_SIGNED_BYTE

Description Loads a byte value (e.g., a value loaded from a byte array or returned by a method with return type byte) and performs a bitwise OR with that value. Byte values are sign extended to 32 bits before any any bitwise operations are performed on the value. Thus, if b[0] contains the value 0xff, and x is initially 0, then the code ((x « 8) | b[0]) will sign extend 0xff to get 0xffffffff, and thus give the value 0xffffffff as the result. byte與int做or時,由於byte被轉成int,若此byte大於等於0x80,它將變成0xffffff80。 Solution 將byte與0xff做and,以去除其負數部分。 Example Before: public static short byteArrayToShort ( byte [ ] b ) { final short ret ; ret = ( short ) ( b [ 0 ] << 8 | b [ 1 ] ) ; return ret ; } After: public static short byteArrayToShort ( byte [ ] b ) { final short ret ; ret = ( short ) ( b [ 0 ] << 8 | b [ 1 ] & 0xff ) ; return ret ; } 如果將0x00與0xff傳入修改之前的程式碼,預期值為255(0x00ff),但結果會為-1(0xffff)。

SpotBugs - NP_NULL_ON_SOME_PATH

Description NP: Possible null pointer dereference 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. 在多個分支中的某個變數,可能在使用時會是null,而造成nullpointer exception。 Solution 增加null檢查或Refactoring程式碼。 Example Before: IUser user = findUser ( uid ) ; if ( user != null ) { removeUser ( uid ) ; } logger. debug ( "Remove user {}." , user. getKey ( ) ) ; After: IUser user = findUser ( uid ) ; if ( user != null ) { removeUser ( uid ) ; logger. debug ( "Remove user {}." , user. getKey ( ) ) ; } 有時被找出來只是findbugs無法確定這個是不是問題,但大都是存在一定的機率會發生。

SpotBugs - RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE

Description RCN: Redundant nullcheck of mt which is known to be null This method contains a redundant check of a known null value against the constant null. 做了無謂的nullcheck。 Solution 把它拿掉就好。 Example Before: InputStream inputStream = null ; try { inputStream = new FileInputStream ( "temp.txt" ) ; } catch ( Exception e ) { if ( inputStream != null ) { Cleaner. close ( inputStream ) ; } logger. error ( "" , e ) ; } After: InputStream inputStream = null ; try { inputStream = new FileInputStream ( "temp.txt" ) ; } catch ( Exception e ) { logger. error ( "" , e ) ; } 上面範例的close理想中要把它放到finally中。

SpotBugs - DLS_DEAD_LOCAL_STORE

Description DLS: Dead store to status This instruction assigns a value to a local variable, but the value is not read or used in any subsequent instruction. Often, this indicates an error, because the value computed is never used. Note that Sun's javac compiler often generates dead stores for final local variables. Because FindBugs is a bytecode-based tool, there is no easy way to eliminate these false positives. Assign值給一個區域變數,但這個值卻從來沒被用過。 Solution 如果確定這個值會由後面的程式assign,那初始時就值接給null。 Example Before: JSONArray jsonArray = new JSONArray ( ) ; if ( option == OK ) { jsonArray = genOkResult ( ) ; } else if ( option == WARNING ) { jsonArray = genWarnResult ( ) ; } else { jsonArray = genDefaultResult ( ) ; } After: JSONArray jsonArray = null ; if ( option == OK ) { jsonArray = genOkResult ( ) ; } else if ( option == WARNING ) { jso...

SpotBugs - OBL_UNSATISFIED_OBLIGATION

Description OBL: Method may fail to clean up java.io.InputStream This method may fail to clean up (close, dispose of) a stream, database object, or other resource requiring an explicit cleanup operation. In general, if a method opens a stream or other resource, the method should use a try/finally block to ensure that the stream or resource is cleaned up before the method returns. 沒有關閉串流或有可能在關閉之前發生問題。 Solution 使用try, catch與finally,並在finally中去close stream。 try-with-resources。 Example 無。

SpotBugs - WMI_WRONG_MAP_ITERATOR

Description WMI: Method makes inefficient use of keySet iterator instead of entrySet iterator 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. 先取得keyset去做loop,再透過key去找對應value,造成每次都需要做搜尋的動作。 Solution 要看Code的實做方式決定如何修改。如果loop內要用到value與key,可以用entrySet取代使用keySet的lookup;如果只用到value或key,我認為可以用valueset或keyset取代。 Example Before: for ( String id : users. keySet ( ) ) { IUser user = users. get ( id ) ; logger. debug ( "check user {} info." , id ) ; validateUserInfo ( result, user ) ; } After: for ( Entry < String , IUser > entry : users. entrySet ( ) ) { logger. debug ( "check user {} info." , entry. getKey ( ) ) ; validTimeRanges ( result, entry. getValue ( ) ) ; }

SpotBugs - SBSC_USE_STRINGBUFFER_CONCATENATION

Description SBSC: Method AppletComponent.getValue(String) concatenates strings using + in a loop The method seems to be building a String using concatenation in a loop. In each iteration, the String is converted to a StringBuffer/StringBuilder, appended to, and converted back to a String. This can lead to a cost quadratic in the number of iterations, as the growing string is recopied in each iteration. 使用loop連接字串時,是使用+,會造成不必要的物件轉換。 Solution Better performance can be obtained by using a StringBuffer (or StringBuilder in Java 1.5) explicitly. 透過StringBuffer將字串連接起來,再轉回String即可。 Example Before: return a + b + c ; After: StringBuffer sb = new StringBuffer ( ) ; sb. append ( a ) ; sb. append ( b ) ; sb. append ( c ) ; return sb. toString ( ) ;

SpotBugs - DM_STRING_VOID_CTOR

Description Dm: invokes inefficient new String() constructor Creating a new java.lang.String object using the no-argument constructor wastes memory because the object so created will be functionally indistinguishable from the empty string constant “”. Java guarantees that identical string constants will be represented by the same String object. Therefore, you should just use the empty string constant directly. 使用沒參數的String建構元,造成記憶體浪費。 Solution 用空字串取代new String(); Example Before: String emptyStr = new String ( ) ; After: String str = "" ;

SpotBugs - DM_STRING_CTOR

Description Dm: invokes inefficient new String(String) constructor 如果把String傳入new String中,等於多做一次額外的new String。 Solution Using the java.lang.String(String) constructor wastes memory because the object so constructed will be functionally indistinguishable from the String passed as a parameter. Just use the argument String directly. 直接把傳入值Assign過去即可。 Example Before: String str = new String ( "testingStr" ) ; After: String str = "testingStr" ; 友藏內心的獨白: 會不會有要產生不同instance但相同內容的情況?

SpotBugs - DM_NUMBER_CTOR

Description Bx: Method invokes inefficient new Integer(int) constructor; use Integer.valueOf(int) instead 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. Values between -128 and 127 are guaranteed to have corresponding cached instances and using valueOf is approximately 3.5 times faster than using constructor. For values outside the constant range the performance of both styles is the same. Unless the class must be compatible with JVMs predating Java 1.5, use either autoboxing or the valueOf() method when creating instances of Long, Integer, Short, Character, and Byte. 使用new Integer(int)等於建立新的物件,若透過Integer.valueOf(int),先前有create過就會從cache中取得。 Solution 用Integer.valueOf(int)取代new Integer(int) Example Before: currentFiles. put ( theDirectory. getAbsolutePath ( ) , new Long ( theDirectory....

SpotBugs - DM_NEXTINT_VIA_NEXTDOUBLE

Description Dm: Method uses the nextDouble method of Random to generate a random integer; using nextInt is more efficient 使用nextDouble去產生一個random integer,造成效能降低。 Solution If r is a java.util.Random, you can generate a random number from 0 to n-1 using r.nextInt(n), rather than using (int)(r.nextDouble() * n). 用nextInt(n)取代(int)(r.nextDouble() * n)即可。 Example Before: private static void delay ( ) { int d = ( int ) ( Math . random ( ) * 1000 ) ; try { Thread . sleep ( d ) ; } catch ( InterruptedException e ) { // log.. } } After: private static void delay ( ) { int d = r. nextInt ( 1000 ) ; try { Thread . sleep ( d ) ; } catch ( InterruptedException e ) { // log.. } } PS. Math.random()內也是會去產生Random物件去取得亂數值。

SpotBugs - BX_BOXING_IMMEDIATELY_UNBOXED_TO_PERFORM_COERCION

Description Bx: Primitive value is boxed then unboxed to perform primitive coercion. 簡單而言,就是將原始型態的值封裝再直接解封裝,這是沒有必要且使效能降低。 Solution A primitive boxed value constructed and then immediately converted into a different primitive type (e.g., new Double(d).intValue()). Just perform direct primitive coercion (e.g., (int) d). 強制轉型即可,以Double而言,它的intValue也只是做強制轉型。 Example Before: private int convertMiliUnit ( double aReading ) { return new Double ( aReading * 1000 ) . intValue ( ) ; } After: private int convertMiliUnit ( double aReading ) { return ( int ) aReading * 1000 ; }

SpotBugs - BX_BOXING_IMMEDIATELY_UNBOXED

Description Bx: Primitive value is boxed and then immediately unboxed 把原始型態如int,宣告為boxed的物件如Interger,並透過它提供的intValue去取得原始型態的值。 Solution A primitive is boxed, and then immediately unboxed. This probably is due to a manual boxing in a place where an unboxed value is required, thus forcing the compiler to immediately undo the work of the boxing. 這是不必要的動作,所以根本不用做boxed的動作。 Example Before: public boolean checkResult ( ) { return getResult ( ) == new Double ( 0 ) ; } getResult取得為double型態,而比較的值為0,因此只需要將new Double(0)改為0.0即可。 After: public boolean checkResult ( ) { return getResult ( ) == 0.0 ; }

SpotBugs - ICAST_IDIV_CAST_TO_DOUBLE

Description ICAST: integral division result cast to double or float This code casts the result of an integral division (e.g., int or long division) operation to double or float. Doing division on integers truncates the result to the integer value closest to zero. The fact that the result was cast to double suggests that this precision should have been retained. What was probably meant was to cast one or both of the operands to double before performing the division. 兩個整數相除會使得結果先為整數再為double。以2/5而言,結果會變為0,5/2會為2。 Solution 將分母轉型為double或float。 Example Before: public double div ( int a, int b ) { return a / b ; } After: public double div ( int a, int b ) { return a / ( double ) b ; }

SpotBugs - DMI_HARDCODED_ABSOLUTE_FILENAME

Description DMI: Code contains a hard coded reference to an absolute pathname This code constructs a File object using a hard coded to an absolute pathname (e.g., new File(“/home/dannyc/workspace/j2ee/src/share/com/sun/enterprise/deployment”); 直接將絕對路徑hard code在new File中。 Solution 最好的方法應該是直接從config讀出,也可以拉為一個static private member。 Example None

SpotBugs - NM_CLASS_NAMING_CONVENTION

Description Nm: Class names should start with an upper case letter Class names should be nouns, in mixed case with the first letter of each internal word capitalized. Try to keep your class names simple and descriptive. Use whole words-avoid acronyms and abbreviations (unless the abbreviation is much more widely used than the long form, such as URL or HTML). Class名稱小寫不合規範。 Solution 將Class名稱改為大寫。 Example None

SpotBugs - NM_METHOD_NAMING_CONVENTION

Description Nm: Method names should start with a lower case letter (NM_METHOD_NAMING_CONVENTION) Methods should be verbs, in mixed case with the first letter lowercase, with the first letter of each internal word capitalized. Method名稱不合規範,第一字母要小寫。 Solution Method名稱改為小寫即可。 Example None

FindBug / SpotBugs文章列表

Introduction 能夠幫你找到code內不好的'味道'與隱藏的bug,下面的內容是我邊修邊記錄的內容。友藏內心的獨白: 真的找的到bug! (*代表我認為最容易犯的錯!) Plugin - Find Security Bugs Fix bugs Bad Practice NM_METHOD_NAMING_CONVENTION NM_CLASS_NAMING_CONVENTION Dodgy code DMI_HARDCODED_ABSOLUTE_FILENAME ICAST_IDIV_CAST_TO_DOUBLE Performance BX_BOXING_IMMEDIATELY_UNBOXED BX_BOXING_IMMEDIATELY_UNBOXED_TO_PERFORM_COERCION DM_NEXTINT_VIA_NEXTDOUBLE DM_NUMBER_CTOR DM_STRING_CTOR DM_STRING_VOID_CTOR SBSC_USE_STRINGBUFFER_CONCATENATION WMI_WRONG_MAP_ITERATOR * EXPERIMENTAL OBL_UNSATISFIED_OBLIGATION * STYLE DLS_DEAD_LOCAL_STORE RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE CORRECTNESS NP_NULL_ON_SOME_PATH BIT_IOR_OF_SIGNED_BYTE * MALICIOUS_CODE EI_EXPOSE_REP * EI_EXPOSE_REP2 * Security XXE_DOCUMENT - XML parsing vulnerable to XXE (DocumentBuilder) SSL_CONTEXT - SSLContext needs to be compatible with TLS 1.2