1. Resources should be closed
IO資源應該在使用後關閉。在try語句中使用了Connections, streams, files等,這些類實現了Closeable 或者AutoCloseable接口,必須在finally塊中關閉,否則,如果出現異常就可能無法關閉。對於實現了AutoCloseable接口的類,最好使用“try-with-resource”語句來自動關閉。如果不能正確地關閉資源,就會導致資源泄漏,這可能會導致應用程序甚至整個系統的崩潰。
關於IO資源的處理問題,以下比較三種解決方案。
- close()放在try塊中
- close()放在finally塊中
- 使用try-with-resource語句
close()放在try塊中
//close() is in try clause
try {
PrintWriter out = new PrintWriter(
new BufferedWriter(
new FileWriter("out.txt", true)));
out.println("the text");
out.close();
} catch (IOException e) {
e.printStackTrace();
}
這種方式容易造成IO資源的泄露,因爲對於IO資源來說不管操作的結果如何都必須關閉。
close()放在finally塊中
//close() is in finally clause
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.7
之前,推薦使用這種方式,但是,這種方式還是有問題,因爲,在try塊和finally塊中可能都會發生Exception。
使用try-with-resource語句
//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();
}
這種方式可能是最好的,Java官方推薦使用這種方式,但是,使用的前提是你的jdk版本在1.7以上。
因爲不管什麼情況下(異常或者非異常)資源都必須關閉,在jdk1.6之前,應該把close()放在finally塊中,以確保資源的正確釋放。
如果使用jdk1.7以上的版本,推薦使用try-with-resources語句。
2. "InterruptedException" should not be ignored
在代碼中不應該忽略中斷異常,只在日誌中打印異常日誌,就像“忽略”一樣。拋出中斷異常會清除線程的中斷狀態,因此如果異常處理不當,那麼線程被中斷的信息將會丟失。相反,中斷應該被重新拋出——立即或者在清理方法的狀態之後——或者應該通過調用Thread.interrupt()來重新中斷線程,即使這是單線程的應用程序。任何其他的操作過程都有延遲線程關閉的風險,並且丟失了線程被中斷的信息——線程很可能沒有完成它的任務。
類似地,也應該傳播ThreadDeath異常。根據它的JavaDoc:
如果ThreadDeath異常被一個方法捕獲,那麼它被重新拋出是很重要的,這樣線程就會結束。
不符合要求的代碼如下:
public void run () {
try {
while (true) {
// do stuff
}
}catch (InterruptedException e) { // Noncompliant; logging is not enough
LOGGER.log(Level.WARN, "Interrupted!", e);
}
}
catch塊中只是打印了異常日誌,相當於忽略了這個異常。
處理建議爲拋出異常:
public void run () {
try {
while (true) {
// do stuff
}
}catch (InterruptedException e) {
LOGGER.log(Level.WARN, "Interrupted!", e);
// Restore interrupted state...
Thread.currentThread().interrupt();
}
}
catch塊中記錄中斷狀態之後將線程中斷,正確的處理了中斷異常。
3.Null pointers should not be dereferenced/accessed.
對值爲null的指針調用任何方法,就會引發空指針異常(java.lang.NullPointerException)。在最好的情況下,這樣的異常會導致程序終止。在最壞的情況下,它可能暴露出對攻擊者有用的調試信息,或者它可能允許攻擊者繞過安全措施。
以下爲不符合要求的代碼舉例:
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
}
private void merge(@Nonnull Color firstColor, @Nonnull Color secondColor){...}
public void append(@CheckForNull Color color) {
merge(currentColor, color); // Noncompliant; color should be null-checked because merge(...) doesn't accept nullable parameters
}
void paint(Color color) {
if(color == null) {
System.out.println("Unable to apply color " + color.toString()); // Noncompliant; NullPointerException will be thrown
return;
}
...
}
4.Non-thread-safe fields should not be static
並不是標準Java庫中的所有類都爲線程安全的。多線程時,有些類很可能會在運行時引起數據問題或異常。
在Calendar,DateFormat,javax.xml.xpath.XPath或 javax.xml.validation.SchemaFactory生成靜態的實例時會出問題。
缺陷舉例:
public class MyClass {
static private SimpleDateFormat format = new SimpleDateFormat("HH-mm-ss"); // Noncompliant
static private Calendar calendar = Calendar.getInstance(); // Noncompliant
public class MyClass {
private SimpleDateFormat format = new SimpleDateFormat("HH-mm-ss");
private Calendar calendar = Calendar.getInstance();