1.類中方法全為靜态方法時,私有化構造器
問題樣例
class StringUtils { // Noncompliant
public static String concatenate(String s1, String s2) {
return s1 + s2;
}
}
原因:當類中的方法全部都是 static 關鍵字修飾時 ,它的構造方法最好作為 private 私有化,理由是方法全是 static, 不知道的人會去new對象去調用,需要調用構造方法。 但 static的方法直接用類名調用就行!
解決樣例
class StringUtils { // Compliant
private StringUtils() {
throw new IllegalStateException("Utility class");
}
public static String concatenate(String s1, String s2) {
return s1 + s2;
}
}
2.多個異常使用多個方法塊捕獲
問題樣例
try {
/* ... */
} catch (Exception e) {
if(e instanceof IOException) { /* ... */ } // Noncompliant
if(e instanceof NullPointerException{ /* ... */ } // Noncompliant
}
原因:應使用适當類型的多個捕獲塊,而不是捕獲一般異常,然後對該類型進行測試。
解決樣例
class MyClass {
private int my_field;
}
3.命名規範
問題樣例
class MyClass {
private int my_field;
}
原因:共享一些命名約定是團隊高效協作的關鍵。此規則允許檢查字段名是否與提供的正規表達式比對。駝峰命名
解決樣例
class MyClass {
private int myField;
}
4.泛型定義具體類型
問題樣例
List myList; // Noncompliant
Set mySet; // Noncompliant
原因:提供此泛型的參數化類型。
解決樣例
List<String> myList;
Set<? extends Number> mySet;
5.移除未使用的變量、注釋的代碼
public int numberOfMinutes(int hours) {
int seconds = 0; // seconds is never used
return hours * 60;
}
public int numberOfMinutes(int hours) {
int seconds = 0; // seconds is never used
return hours * 60;
}
原因:删掉注釋的代碼行、未使用的變量、私有方法等、移除未使用的變量
public int numberOfMinutes(int hours) {
return hours * 60;
}
public int numberOfMinutes(int hours) {
return hours * 60;
}
6.常見空指針異常
String path = null;
path = UploadConfigurationRead.getInstance().getConfigItem(toUploadPath).trim();
if(path != null){
flag = "0";
}else{
flag = "1";
}
fileType = path.substring(path.length()-1,path.length());
String errNode = hyperV(document.selectSingleNode("//responseMessage"));
errNode = convterEcfErrCode(errNode);
原因:常見的空指針異常
String path = null;
path = UploadConfigurationRead.getInstance().getConfigItem(toUploadPath).trim();
if(path != null){
flag = "0";
fileType = path.substring(path.length()-1,path.length());
}else{
flag = "1";
fileType = path.substring(path.length()-1,path.length());
}
if(errNode==null) ///99999,無傳回提示
errCode = "99999-無提示";
else{
errCode=convterEcfErrCode(errNode);
}
7.靜态變量使用規範
public class MyClass {
public static final int SOME_CONSTANT = 0; // Compliant - constants are not checked
public String firstName; // Noncompliant
}
原因:靜态變量加final、駝峰命名、或者使用私有變量并提供方法通路
public class MyClass {
public static final int SOME_CONSTANT = 0; // Compliant - constants are not checked
private String firstName; // Compliant
public String getFirstName() {
return firstName;
}
8.集合使用建議
Vector cats = new Vector();
原因:Java API的早期類(如Vector、Hashtable和StringBuffer)被同步以使其線程安全。不幸的是,同步對性能有很大的負面影響,即使在從單個線程使用這些集合時也是如此。
最好使用新的非同步替換:
ArrayList或LinkedList而不是Vector
Deque而不是Stack
HashMap而不是Hashtable
StringBuilder而不是StringBuffer
即使在同步上下文中使用,在使用它之前也應該三思,因為它的用法可能很棘手。如果您确信使用是合法的,則可以安全地忽略此警告。
ArrayList cats = new ArrayList();
//這些同步類的使用在重寫方法的簽名中被忽略。
@Override
public Vector getCats() {...}
9.定義專用異常
public void foo(String bar) throws Throwable { // Noncompliant
throw new RuntimeException("My Message"); // Noncompliant
}
原因:定義并抛出專用異常,而不是使用通用異常。
public void foo(String bar) {
throw new MyOwnRuntimeException("My Message");
}
10.IO等資源的正确使用
private void readTheFile() throws IOException {
Path path = Paths.get(this.fileName);
BufferedReader reader = Files.newBufferedReader(path, this.charset);
// ...
reader.close(); // Noncompliant
// ...
Files.lines("input.txt").forEach(System.out::println); // Noncompliant: The stream needs to be closed
}
@CheckForNull
String getName(){...}
public boolean isNameEmpty() {
return getName().length() == 0; // Noncompliant; the result of getName() could be null, but isn't null-checked
}
Connection conn = null;
Statement stmt = null;
try{
conn = DriverManager.getConnection(DB_URL,USER,PASS);
stmt = conn.createStatement();
// ...
}catch(Exception e){
e.printStackTrace();
}finally{
stmt.close(); // Noncompliant; stmt could be null if an exception was thrown in the try{} block
conn.close(); // Noncompliant; conn could be null if an exception was thrown
}
原因:1.IO資源未使用try()catch包裹 2.資源未正确關閉
絕不應取消引用/通路對null的引用。這樣做将導緻引發NullPointerException。充其量,這種異常會導緻程式突然終止。最壞的情況是,它可能會暴露對攻擊者有用的調試資訊,或者允許攻擊者繞過安全措施。
請注意,當它們出現時,該規則利用JSR-305中定義的@CheckForNull和@Nonnull注釋來了解哪些值是可為null的,哪些值不可為null,除非@Nonnull用于equals的參數,根據約定,這些值應始終與null一起工作。
IO資源應該在使用後關閉。在try語句中使用了Connections, streams, files等,這些類實作了Closeable 或者AutoCloseable接口,必須在finally塊中關閉,否則,如果出現異常就可能無法關閉。對于實作了AutoCloseable接口的類,最好使用“try-with-resource”語句來自動關閉。如果不能正确地關閉資源,就會導緻資源洩漏,這可能會導緻應用程式甚至整個系統的崩潰。
關于IO資源的處理問題,以下比較三種解決方案。
- close()放在try塊中
- close()放在finally塊中
- 使用try-with-resource語句
- 方法一 jdk1.7前推薦
- PrintWriter out = null; try { out = new PrintWriter( new BufferedWriter( new FileWriter("out.txt", true))); out.println("the text"); } catch (IOException e) { e.printStackTrace(); } finally { if (out != null) { out.close(); } }
- 方法二 jdk1.8實作了AutoCloseable接推薦
- //try-with-resource statement try (PrintWriter out2 = new PrintWriter( new BufferedWriter( new FileWriter("out.txt", true)))) { out2.println("the text"); } catch (IOException e) { e.printStackTrace(); }
11.浮點數的使用
double d = 1.1;
BigDecimal bd1 = new BigDecimal(d); // Noncompliant; see comment above
BigDecimal bd2 = new BigDecimal(1.1); // Noncompliant; same result
原因:由于浮點不精确,您不太可能從BigDecimal(double)構造函數中獲得所需的值。相反,您應該使用BigDecimal.valueOf,它在封面下使用字元串來消除浮點舍入錯誤,或者使用string參數的構造函數
double d = 1.1;
BigDecimal bd1 = BigDecimal.valueOf(d);
BigDecimal bd2 = new BigDecimal("1.1"); // using String constructor will result in precise value
12.資料計算及資料類型轉換
float twoThirds = 2/3; // Noncompliant; int division. Yields 0.0
long millisInYear = 1_000*3_600*24*365; // Noncompliant; int multiplication. Yields 1471228928
long bigNum = Integer.MAX_VALUE + 2; // Noncompliant. Yields -2147483647
long bigNegNum = Integer.MIN_VALUE-1; //Noncompliant, gives a positive result instead of a negative one.
Date myDate = new Date(seconds * 1_000); //Noncompliant, won't produce the expected result if seconds > 2_147_483
原因:當對整數執行算術運算時,結果将始終是整數。您可以通過自動類型轉換将該結果配置設定給long、double或float,但如果以int或long開頭,則結果可能不是您期望的結果。例如,如果将int除法的結果配置設定給浮點變量,則在配置設定之前精度将丢失。同樣,如果乘法的結果被配置設定給long,那麼在配置設定之前它可能已經溢出。無論哪種情況,結果都不會像預期的那樣。相反,在操作發生之前,應至少将一個操作數強制轉換或提升為最終類型。總的來說,要麼強制轉換,要麼将其中一個變量提升
float twoThirds = 2f/3; // 2 promoted to float. Yields 0.6666667
float twoThirds = (float)2/3; // 2 cast to float
long millisInYear = (long)1_000*3_600*24*365; // 1_000 cast to long
long bigNum = (long)Integer.MAX_VALUE + 2;
13. ==比較
String firstName = getFirstName(); // String overrides equals
String lastName = getLastName();
if (firstName == lastName) { ... }; // Non-compliant; false even if the strings have the same value
原因:使用引用等式==或!=來比較java.lang.String或java.lang.Integer等裝箱類型的兩個執行個體幾乎總是錯誤的,因為它不是比較實際值,而是比較記憶體中的位置。
String firstName = getFirstName();
String lastName = getLastName();
if (firstName != null && firstName.equals(lastName)) { ... };
14. 不同類型的比較判斷
InvDetail invDetail = new InvDetail();
if(null != invDetail && !"".equals(invDetail)){
}
原因:删除對“equals”的調用;不相關類型之間的比較總是傳回false。
InvDetail invDetail = new InvDetail();
if(null != invDetail)){
}
15.循環中break的使用
for (int i = 0; i < 10; i++) { // noncompliant, loop only executes once
printf("i is %d", i);
break;
}
原因:一次循環建議使用if。如果在循環中使用break,建議加上判斷條件
for (int i = 0; i < 10; i++) {
if (i == x) {
break;
} else {
printf("i is %d", i);
}
}
16.條件判斷的注意事項
//什麼都沒做
if(resultMap!=null){
if(PUBParm.RT_STATE_N.equals(resultMap.get("state"))){
}else{
}
}else{
}
//做了一樣的事情
if (b == 0) { // Noncompliant
doOneMoreThing();
} else {
doOneMoreThing();
}
//條件判斷後 永遠隻進入一個分支
int b = a > 12 ? 4 : 4; // Noncompliant
switch (i) { // Noncompliant
case 1:
doSomething();
break;
case 2:
doSomething();
break;
case 3:
doSomething();
break;
default:
doSomething();
}
17.左右運算符條件一緻
if(!sts.equals("8")&&!sts.equals("8")){}
if ( a == b && a == b ) { // if the first one is true, the second one is too
doX();
}
if(flag =0 0)
原因:運算符左右條件一緻,更新判定條件
18.對于InterruptedExceptions的處理
public void run () {
try {
while (true) {
// do stuff
}
}catch (InterruptedException e) { // Noncompliant; logging is not enough
LOGGER.log(Level.WARN, "Interrupted!", e);
}
}
try {
while (true) {
// do stuff
}
latch.await();
} catch (Exception e) {
Either re-interrupt this method or rethrow the "InterruptedException" that can be caught here.
e.printStackTrace();
throw new ServiceException(e.getMessage());
}
return ipg;
原因:代碼中決不應忽略InterruptedExceptions,在這種情況下,隻需将異常計數記錄為“忽略”即可。抛出InterruptedException将清除線程的中斷狀态,是以如果異常處理不當,則線程被中斷的資訊将丢失。相反,InterruptedExceptions應該立即或在清理方法狀态後重新抛出,或者通過調用thread.crupt()來重新中斷線程,即使這是一個單線程應用程式。任何其他操作過程都有延遲線程關閉的風險,并丢失線程被中斷的資訊——可能沒有完成任務。
同樣,也應傳播ThreadDeath異常。根據其JavaDoc:如果ThreadDeath被某個方法捕獲,則必須對其進行重新處理,以使線程實際死亡。
public void run () {
try {
while (true) {
// do stuff
}
}catch (InterruptedException e) {
LOGGER.log(Level.WARN, "Interrupted!", e);
// Restore interrupted state...
Thread.currentThread().interrupt();
}
}
19.fianlly塊中不應使用retrun、break等語句
public static void main(String[] args) {
try {
doSomethingWhichThrowsException();
System.out.println("OK"); // incorrect "OK" message is printed
} catch (RuntimeException e) {
System.out.println("ERROR"); // this message is not shown
}
}
public static void doSomethingWhichThrowsException() {
try {
throw new RuntimeException();
} finally {
for (int i = 0; i < 10; i ++) {
//...
if (q == i) {
break; // ignored
}
}
/* ... */
return; // Noncompliant - prevents the RuntimeException from being propagated
}
}
原因:使用finally塊中的return、break、throw等将抑制try或catch塊中引發的任何未處理Throwable的傳播。當跳轉語句(break、continue、return、throw和goto)将強制控制流離開finally塊時,此規則會引發問題。
public static void main(String[] args) {
try {
doSomethingWhichThrowsException();
System.out.println("OK");
} catch (RuntimeException e) {
System.out.println("ERROR"); // "ERROR" is printed as expected
}
}
public static void doSomethingWhichThrowsException() {
try {
throw new RuntimeException();
} finally {
for (int i = 0; i < 10; i ++) {
//...
if (q == i) {
break; // ignored
}
}
/* ... */
}
}
20.根據函數傳回的狀态進行操作
if (zipFile.exists()) {
zipFile.delete();
}
zipFile.createNewFile();
原因:
當函數調用的傳回值包含操作狀态代碼時,應測試該值以確定操作成功完成。
當忽略以下項的傳回值時,此規則會引發問題:
java.io.傳回狀态代碼的檔案操作(mkdirs除外)
疊代器hasNext()
枚舉.hhasMoreElements()
Lock.tryLock()
非void Condition.await*方法
CountDownLatch.await(long,TimeUnit)
Semaphore.try擷取
BlockingQueue:提供,删除
可以直接根據調用函數傳回的狀态碼值進行下一步操作
if (!zipFile.delete();) {
zipFile.createNewFile();
}
21.compareTo方法在判斷中的使用建議
if (myClass.compareTo(arg) == -1) { // Noncompliant
// ...
}
原因:雖然大多數compareTo方法傳回-1、0或1,但有些方法不傳回,并且将compareTo的結果與0以外的特定值進行測試可能會導緻錯誤否定。
if (myClass.compareTo(arg) < 0) {
// ...
}
22.可能存在的1/0異常
User.numFormat(Math.abs((value - lastValue) / lastValue) * 100,2)).append("%,");
原因:要确定 lastValue不為0
if (lastValue!=0) {
User.numFormat(Math.abs((value - lastValue) / lastValue) * 100,2)).append("%,");
}
23.重寫“equals()”,是以也應重寫“hashCode()
class MyClass { // Noncompliant - should also override "hashCode()"
@Override
public boolean equals(Object obj) {
/* ... */
}
}
//代碼中的具體使用
public boolean equals(Object obj) {
return true;
}
原因:此類重寫“equals()”,是以也應重寫“hashCode()”。
class MyClass { // Compliant
@Override
public boolean equals(Object obj) {
/* ... */
}
@Override
public int hashCode() {
/* ... */
}
}
24.自我指派
public void setName(String name) {
name = name;
}
原因:沒有理由将變量重新配置設定給它自己。要麼是多餘的,應該删除,要麼重新指派是錯誤的,而另一個值或變量用于指派。
public void setName(String name) {
this.name = name;
}
25.建立變量給參數重新指派
public void doTheThing(String str, int i, List<String> strings) {
str = Integer.toString(i); // Noncompliant
for (String s : strings) {
s = "hello world"; // Noncompliant
}
26.調用函數有傳回值要接收
propDeTypes.replace("|", "");
原因:調用函數有傳回值要接收,當滿足以下兩個條件時,此規則不會産生問題:
方法調用位于帶有關聯catch子句的try塊中。
方法名稱以“parse”、“format”、“decode”或“valueOf”開頭,或者方法是String.getBytes(Charset)。
public void handle(String command){
String formattedCommand = command.toLowerCase();
...
}
private boolean textIsInteger(String textToCheck) {
try {
Integer.parseInt(textToCheck, 10); // OK
return true;
} catch (NumberFormatException ignored) {
return false;
}
}
27.Random應當抽取使用
public void doSomethingCommon() {
Random rand = new Random(); // Noncompliant; new instance created with each invocation
int rValue = rand.nextInt();
//...
原因:每次需要随機值時建立一個新的Random對象是低效的,并且可能會産生不随機的數字,具體取決于JDK。為了獲得更好的效率和随機性,請建立一個Random,然後存儲并重用它。Random()構造函數每次都嘗試用不同的值設定種子。然而,不能保證種子是随機的,甚至是均勻分布的。有些JDK将使用目前時間作為種子,這使得生成的數字完全不是随機的。可不修改
private Random rand = SecureRandom.getInstanceStrong(); // SecureRandom is preferred to Random
public void doSomethingCommon() {
int rValue = this.rand.nextInt();
//...
28.Calendar等類不應當定義為靜态變量
public class MyClass {
private static SimpleDateFormat format = new SimpleDateFormat("HH-mm-ss"); // Noncompliant
private static Calendar calendar = Calendar.getInstance(); // Noncompliant
原因:并非标準Java庫中的所有類都是線程安全的。以多線程方式使用它們極有可能在運作時導緻資料問題或異常。當Calendar、DateFormat、javax.xml.xpath.XXPath或javax.xml.validation.SchemaFactory的執行個體标記為靜态時,此規則會引發問題。
DateUtil工具中使用月曆變量的,建議在代碼中新增,而不使用靜态月曆變量
public class MyClass {
private SimpleDateFormat format = new SimpleDateFormat("HH-mm-ss");
private Calendar calendar = Calendar.getInstance();
29.正規表達式的使用規範
private static final String CHECK_TEL_VALUE ="/^[+]{0,1}(\\d){1,3}[ ]?([-]?((\\d)|[ ]){1,12})+$/";
Pattern.compile("$[a-z]+^"); // Noncompliant
原因:在正規表達式中,邊界^和\A隻能在輸入的開頭比對(或者,如果^與MULTILINE标志組合,則為行的開頭),$、\Z和\Z隻能在結尾比對。這些模式可能會被誤用,例如,通過意外切換^和$來建立一個永遠無法比對的模式。
Pattern.compile("^[a-z]+#34;);
30.hibernate.hbm2ddl.auto使用
<session-factory>
<property name="hibernate.hbm2ddl.auto">update</property> <!-- Noncompliant -->
</session-factory>
原因:對hibernate.hbm2ddl.auto使用除“validate”以外的任何值都可能導緻應用程式使用的資料庫架構被更改、删除或清除所有資料。簡而言之,使用該屬性是有風險的,并且隻有在生産中使用“validate”選項時,才能使用該屬性
<session-factory>
<property name="hibernate.hbm2ddl.auto">validate</property> <!-- Compliant -->
</session-factory>
or
<session-factory>
<!-- Property deleted -->
</session-factory>