字符串連接誤用
錯誤的寫法
String s =
for (Person p : persons) {
s +=
}
s = s
正確的寫法
StringBuilder sb = new StringBuilder(persons
for (Person p : persons) {
if (sb
sb
}
錯誤的使用StringBuffer
錯誤的寫法
StringBuffer sb = new StringBuffer();
sb
sb
sb
String s = sb
問題在第三行
正確的寫法
StringBuilder sb = new StringBuilder(
sb
sb
sb
String s = sb
或者這樣寫
String s =
測試字符串相等性
錯誤的寫法
if (pareTo(
if (name ==
if (name
if (
上面的代碼沒有錯
正確的寫法
if (
if (name
if (name
數字轉換成字符串
錯誤的寫法
new Integer(set
正確的寫法
String
利用不可變對象(Immutable)
錯誤的寫法
zero = new Integer(
return Boolean
正確的寫法
zero = Integer
return Boolean
請使用XML解析器
錯誤的寫法
int start = xml
int end = xml
String name = xml
正確的寫法
SAXBuilder builder = new SAXBuilder(false);
Document doc = doc = builder
String name = doc
請使用JDom組裝XML
錯誤的寫法
String name =
String attribute =
String xml =
+
+
正確的寫法
Element root = new Element(
root
root
Document doc = new Documet();
doc
XmlOutputter out = new XmlOutputter(Format
String xml = out
XML編碼陷阱
錯誤的寫法
String xml = FileUtils
因為xml的編碼在文件中指定的
未指定字符編碼
錯誤的寫法
Reader r = new FileReader(file);
Writer w = new FileWriter(file);
Reader r = new InputStreamReader(inputStream);
Writer w = new OutputStreamWriter(outputStream);
String s = new String(byteArray); // byteArray is a byte[]
byte[] a = string
這樣的代碼主要不具有跨平台可移植性
正確的寫法
Reader r = new InputStreamReader(new FileInputStream(file)
Writer w = new OutputStreamWriter(new FileOutputStream(file)
Reader r = new InputStreamReader(inputStream
Writer w = new OutputStreamWriter(outputStream
String s = new String(byteArray
byte[] a = string
未對數據流進行緩存
錯誤的寫法
InputStream in = new FileInputStream(file);
int b;
while ((b = in
}
上面的代碼是一個byte一個byte的讀取
正確的寫法
InputStream in = new BufferedInputStream(new FileInputStream(file));
無限使用heap內存
錯誤的寫法
byte[] pdf = toPdf(file);
這裡有一個前提
正確的寫法
File pdf = toPdf(file);
另外
不指定超時時間
錯誤的代碼
Socket socket =
nnect(remote);
InputStream in = socket
int i = in
這種情況在工作中已經碰到不止一次了
正確的寫法
Socket socket =
nnect(remote
InputStream in = socket
socket
int i = in
另外
頻繁使用計時器
錯誤代碼
for (
long t = System
long t = System
Date d = new Date();
Calendar c = new GregorianCalendar();
}
每次new一個Date或Calendar都會涉及一次本地調用來獲取當前時間(盡管這個本地調用相對其他本地方法調用要快)
如果對時間不是特別敏感
正確的寫法
Date d = new Date();
for (E entity : entities) {
entity
entity
}
如果循環操作耗時較長(超過幾ms)
private volatile long time;
Timer timer = new Timer(true);
try {
time = System
timer
public void run() {
time = System
}
}
for (E entity : entities) {
entity
entity
}
} finally {
timer
}
捕獲所有的異常
錯誤的寫法
Query q =
Person p;
try {
p = (Person) q
} catch(Exception e) {
p = null;
}
這是EJB
正確的寫法
Query q =
Person p;
try {
p = (Person) q
} catch(NoResultException e) {
p = null;
}
忽略所有異常
錯誤的寫法
try {
doStuff();
} catch(Exception e) {
log
}
doMoreStuff();
這個代碼有兩個問題
正確的寫法
try {
doStuff();
} catch(Exception e) {
throw new MyRuntimeException(
}
重復包裝RuntimeException
錯誤的寫法
try {
doStuff();
} catch(Exception e) {
throw new RuntimeException(e);
}
正確的寫法
try {
doStuff();
} catch(RuntimeException e) {
throw e;
} catch(Exception e) {
throw new RuntimeException(e
}
try {
doStuff();
} catch(IOException e) {
throw new RuntimeException(e
} catch(NamingException e) {
throw new RuntimeException(e
不正確的傳播異常
錯誤的寫法
try {
} catch(ParseException e) {
throw new RuntimeException();
throw new RuntimeException(e
throw new RuntimeException(e
throw new RuntimeException(e);
}
主要是沒有正確的將內部的錯誤信息傳遞給調用者
正確的寫法
try {
} catch(ParseException e) {
throw new RuntimeException(e
}
用日志記錄異常
錯誤的寫法
try {
} catch(ExceptionA e) {
log
throw e;
} catch(ExceptionB e) {
log
throw e;
}
一般情況下在日志中記錄異常是不必要的
異常處理不徹底
錯誤的寫法
try {
is = new FileInputStream(inFile);
os = new FileOutputStream(outFile);
} finally {
try {
is
os
} catch(IOException e) {
/* we can
}
}
is可能close失敗
正確的寫法
try {
is = new FileInputStream(inFile);
os = new FileOutputStream(outFile);
} finally {
try { if (is != null) is
try { if (os != null) os
}
捕獲不可能出現的異常
錯誤的寫法
try {
} catch(SomeException e) {
// never happens
}
正確的寫法
try {
} catch(SomeException e) {
// never happens hopefully
throw new IllegalStateException(e
}
transient的誤用
錯誤的寫法
public class A implements Serializable {
private String someState;
private transient Log log = LogFactory
public void f() {
log
}
}
這裡的本意是不希望Log對象被序列化
正確的寫法
public class A implements Serializable {
private String someState;
private static final Log log = LogFactory
public void f() {
log
}
}
public class A implements Serializable {
private String someState;
public void f() {
Log log = LogFactory
log
}
}
不必要的初始化
錯誤的寫法
public class B {
private int count =
private String name = null;
private boolean important = false;
}
這裡的變量會在初始化時使用默認值:
正確的寫法
public class B {
private int count;
private String name;
private boolean important;
}
最好用靜態final定義Log變量
private static final Log log = LogFactory
這樣做的好處有三
可以保證線程安全
靜態或非靜態代碼都可用
不會影響對象序列化
選擇錯誤的類加載器
錯誤的代碼
Class clazz = Class
Class clazz = getClass()
這裡本意是希望用當前類來加載希望的對象
正確的寫法
ClassLoader cl = Thread
if (cl == null) cl = MyClass
Class clazz = cl
反射使用不當
錯誤的寫法
Class beanClass =
if (beanClass
這裡的本意是檢查beanClass是否是TestBean或是其子類
正確的寫法
Class beanClass =
if (TestBean
不必要的同步
錯誤的寫法
Collection l = new Vector();
for (
l
}
Vector是ArrayList同步版本
正確的寫法
Collection l = new ArrayList();
for (
l
}
錯誤的選擇List類型
根據下面的表格數據來進行選擇
ArrayList LinkedList add (append) O(HashMap size陷阱
錯誤的寫法
Map map = new HashMap(collection
for (Object o : collection) {
map
}
這裡可以參考guava的Maps
正確的寫法
Map map = new HashMap(
對Hashtable
這裡主要需要了解HashMap和Hashtable的內部實現上
這一切的根源都是由於JDK內部沒有提供一套高效的Map和Set實現
對List的誤用
建議下列場景用Array來替代List:
list長度固定
對list頻繁的遍歷
需要對數字進行包裝(主要JDK沒有提供基本類型的List)
比如下面的代碼
錯誤的寫法
List<Integer> codes = new ArrayList<Integer>();
codes
codes
codes
codes
正確的寫法
int[] codes = {
錯誤的寫法
// horribly slow and a memory waster if l has a few thousand elements (try it yourself!)
List<Mergeable> l =
for (int i=
Mergeable one = l
Iterator<Mergeable> j = erator(i+
while (j
Mergeable other = l
if (one
rge(other);
other
}
}
}
正確的寫法
// quite fast and no memory allocation
Mergeable[] l =
for (int i=
Mergeable one = l[i];
for (int j=i+
Mergeable other = l[j];
if (one
rge(other);
l[j] = null;
}
}
}
實際上Sun也意識到這一點
用數組來描述一個結構
錯誤用法
/**
* @returns [
*/
Object[] getDetails(int id) {
這裡用數組+文檔的方式來描述一個方法的返回值
正確的寫法
Details getDetails(int id) {
private class Details {
public Location location;
public Customer customer;
public Incident incident;
}
對方法過度限制
錯誤用法
public void notify(Person p) {
sendMail(p
}
class PhoneBook {
String lookup(String employeeId) {
Employee emp =
return emp
}
}
第一個例子是對方法參數做了過多的限制
正確的寫法
public void notify(Person p) {
sendMail(p);
}
class EmployeeDirectory {
Employee lookup(String employeeId) {
Employee emp =
return emp;
}
}
對POJO的setter方法畫蛇添足
錯誤的寫法
private String name;
public void setName(String name) {
this
}
public void String getName() {
return this
}
有時候我們很討厭字符串首尾出現空格
正確的做法
person
日歷對象(Calendar)誤用
錯誤的寫法
Calendar cal = new GregorianCalender(TimeZone
cal
cal
date = cal
這裡主要是對date
正確的寫法
date = new Date(date
TimeZone的誤用
錯誤的寫法
Calendar cal = new GregorianCalendar();
cal
cal
cal
cal
Date startOfDay = cal
這裡有兩個錯誤
正確的寫法
Calendar cal = new GregorianCalendar(user
cal
cal
cal
cal
cal
Date startOfDay = cal
時區(Time Zone)調整的誤用
錯誤的寫法
public static Date convertTz(Date date
Calendar cal = Calendar
cal
cal
cal
return cal
}
這個方法實際上沒有改變時間
Calendar
錯誤的寫法
Calendar c = Calendar
c
Calendar
正確的寫法
Calendar c = new GregorianCalendar(timeZone);
c
Date
錯誤的寫法
account
Date lastmod = account
lastmod
在更新密碼之後
正確的做法
account
account
SimpleDateFormat非線程安全誤用
錯誤的寫法
public class Constants {
public static final SimpleDateFormat date = new SimpleDateFormat(
}
SimpleDateFormat不是線程安全的
使用全局參數配置常量類/接口
public interface Constants {
String version =
String dateFormat =
String configFile =
int maxNameLength =
String someQuery =
}
很多應用都會定義這樣一個全局常量類或接口
比較好的做法是將這些常量定義在組件內部
忽略造型溢出(cast overflow)
錯誤的寫法
public int getFileSize(File f) {
long l = f
return (int) l;
}
這個方法的本意是不支持傳遞超過
正確的寫法
public int getFileSize(File f) {
long l = f
if (l > Integer
return (int) l;
}
另一個溢出bug是cast的對象不對
long a = System
long b = a +
System
System
對float和double使用==操作
錯誤的寫法
for (float f =
System
}
上面的浮點數遞減只會無限接近
正確的寫法
for (float f =
System
}
用浮點數來保存money
錯誤的寫法
float total =
for (OrderLine line : lines) {
total += line
}
double a =
System
BigDecimal d = new BigDecimal(
這個也是一個老生常談的錯誤
因此絕不要用浮點類型來保存money數據
BigDecimal就滿足了上面所說的需求
正確的寫法
BigDecimal total = BigDecimal
for (OrderLine line : lines) {
BigDecimal price = new BigDecimal(line
BigDecimal count = new BigDecimal(unt);
total = total
}
total = total
BigDecimal a = (new BigDecimal(
a = a
System
BigDecimal a = new BigDecimal(
不使用finally塊釋放資源
錯誤的寫法
public void save(File f) throws IOException {
OutputStream out = new BufferedOutputStream(new FileOutputStream(f));
out
out
}
public void load(File f) throws IOException {
InputStream in = new BufferedInputStream(new FileInputStream(f));
in
in
}
上面的代碼打開一個文件輸出流
如果BufferedOutputStream
下面的代碼有一個小小的瑕疵: 如果分配file stream成功
// code for your cookbook
public void save() throws IOException {
File f =
OutputStream out = new BufferedOutputStream(new FileOutputStream(f));
try {
out
out
} finally {
out
}
}
public void load(File f) throws IOException {
InputStream in = new BufferedInputStream(new FileInputStream(f));
try {
in
} finally {
try { in
}
}
數據庫訪問也涉及到類似的情況
Car getCar(DataSource ds
Car car = null;
Connection c = null;
PreparedStatement s = null;
ResultSet rs = null;
try {
c = ds
s = c
s
rs = s
if (rs
car = new Car();
car
lor = rs
}
} finally {
if (rs != null) try { rs
if (s != null) try { s
if (c != null) try { c
}
return car;
}
finalize方法誤用
錯誤的寫法
public class FileBackedCache {
private File backingStore;
protected void finalize() throws IOException {
if (backingStore != null) {
backingStore
backingStore = null;
}
}
}
這個問題Effective Java這本書有詳細的說明
正確的做法是定義一個close方法
public class FileBackedCache {
private File backingStore;
public void close() throws IOException {
if (backingStore != null) {
backingStore
backingStore = null;
}
}
}
在JDK
try (Writer w = new FileWriter(f)) { // implements Closable
w
// w goes out of scope here: w
} catch (IOException e) {
throw new RuntimeException(e
}
Thread
錯誤的寫法
try {
Thread
} catch (InterruptedException e) {
// ok
}
or
while (true) {
if (Thread
}
這裡主要是interrupted靜態方法除了返回當前線程的中斷狀態
正確的寫法
try {
Thread
} catch (InterruptedException e) {
Thread
}
or
while (true) {
if (Thread
}
在靜態變量初始化時創建線程
錯誤的寫法
class Cache {
private static final Timer evictor = new Timer();
}
Timer構造器內部會new一個thread
正確的做法
class Cache {
private static Timer evictor;
public static setupEvictor() {
evictor = new Timer();
}
}
已取消的定時器任務依然持有狀態
錯誤的寫法
final MyClass callback = this;
TimerTask task = new TimerTask() {
public void run() {
callback
}
};
timer
try {
doSomething();
} finally {
task
}
上面的task內部包含一個對外部類實例的應用
正確的寫法
TimerTask task = new Job(this);
timer
try {
doSomething();
} finally {
task
}
static class Job extends TimerTask {
private MyClass callback;
public Job(MyClass callback) {
this
}
public boolean cancel() {
callback = null;
return super
}
public void run() {
if (callback == null) return;
callback
}
}
From:http://tw.wingwit.com/Article/program/Java/hx/201311/26536.html