对float和double使用==比较

//错误的写法:
for (float f = 10f; f!=0; f-=0.1) {   
    System.out.println(f);   
}
/**上面的浮点数递减只会无限接近0而不会等于0, 这样会导致上面的for进入死循环. 通常绝不要对float和double使用==操作. 而采用大于和小于操作. 如果java编译器能针对这种情况给出警告. 或者在java语言规范中不支持浮点数类型的==操作就最好了。**/

//正确的写法:
for (float f = 10f; f>0; f-=0.1) {   
    System.out.println(f);   
}

用浮点数来保存金额

//错误的写法:
float total = 0.0f;   
for (OrderLine line : lines) {   
    total += line.price * line.count;   
}   
double a = 1.14 * 75; // 85.5 将表示为 85.4999...   
System.out.println(Math.round(a)); // 输出值为85   
BigDecimal d = new BigDecimal(1.14); //造成精度丢失

/**这个也是一个老生常谈的错误. 比如计算100笔订单, 每笔0.3元, 最终的计算结果是29.9999971. 如果将float类型改为double类型, 得到的结果将是30.000001192092896. 出现这种情况的原因是, 人类和计算的计数方式不同. 人类采用的是十进制, 而计算机是二进制.二进制对于计算机来说非常好使, 但是对于涉及到精确计算的场景就会带来误差. 比如银行金融中的应用。
因此绝不要用浮点类型来保存money数据. 采用浮点数得到的计算结果是不精确的. 即使与int类型做乘法运算也会产生一个不精确的结果.那是因为在用二进制存储一个浮点数时已经出现了精度丢失. 最好的做法就是用一个string或者固定点数来表示. 为了精确, 这种表示方式需要指定相应的精度值.
BigDecimal就满足了上面所说的需求. 如果在计算的过程中精度的丢失超出了给定的范围, 将抛出runtime exception.**/

//正确的写法:
BigDecimal total = BigDecimal.ZERO;   
for (OrderLine line : lines) {   
    BigDecimal price = new BigDecimal(line.price);   
    BigDecimal count = new BigDecimal(line.count);   
    total = total.add(price.multiply(count)); // BigDecimal is immutable!   
}   
total = total.setScale(2, RoundingMode.HALF_UP);   
BigDecimal a = (new BigDecimal("1.14")).multiply(new BigDecimal(75)); // 85.5 exact   
a = a.setScale(0, RoundingMode.HALF_UP); // 86   
System.out.println(a); // correct output: 86   
BigDecimal a = new BigDecimal("1.14");

字符串连接的错误写法

//错误的写法:
String s = "";  
for (Person p : persons) {  
    s += ", " + p.getName();  
}  
s = s.substring(2); //remove first comma

//正确的写法:
StringBuilder sb = new StringBuilder(persons.size() * 16); // well estimated buffer
for (Person p : persons) {
    if (sb.length() > 0) sb.append(", ");
    sb.append(p.getName);
}

StringBuffer 的错误使用

//错误的写法:
StringBuffer sb = new StringBuffer();  
sb.append("Name: ");  
sb.append(name + '\n');  
sb.append("!");  
...  
String s = sb.toString();
/**问题在第三行,append char比String性能要好,另外就是初始化StringBuffer没有指定size,导致中间append时可能重新调整内部数组大小。如果是JDK1.5最好用StringBuilder取代StringBuffer,除非有线程安全的要求。还有一种方式就是可以直接连接字符串。缺点就是无法初始化时指定长度。**/

//正确的写法:
StringBuilder sb = new StringBuilder(100);  
sb.append("Name: ");  
sb.append(name);  
sb.append("\n!");  
String s = sb.toString();
//或者这样写:
String s = "Name: " + name + "\n!";

字符串的比较

//错误的写法:
if (name.compareTo("John") == 0) ...  
if (name == "John") ...  
if (name.equals("John")) ...  
if ("".equals(name)) ...

//正确的写法:
if ("John".equals(name)) ...  
if (name.length() == 0) ...  
if (name.isEmpty()) ...

 数字转换成字符串

//错误的写法:
"" + set.size()  
new Integer(set.size()).toString()

//正确的写法:
String.valueOf(set.size())

利用不可变对象(Immutable)

//错误的写法:
zero = new Integer(0);  
return Boolean.valueOf("true");

//正确的写法:
zero = Integer.valueOf(0);  
return Boolean.TRUE;

请使用XML解析器

//错误的写法:
int start = xml.indexOf("<name>") + "<name>".length();  
int end = xml.indexOf("</name>");  
String name = xml.substring(start, end);

//正确的写法:
SAXBuilder builder = new SAXBuilder(false);  
Document doc = doc = builder.build(new StringReader(xml));  
String name = doc.getRootElement().getChild("name").getText();

请使用JDom组装XML

//错误的写法:
String name = ...  
String attribute = ...  
String xml = "<root>" 
            +"<name att=\""+ attribute +"\">"+ name +"</name>" 
            +"</root>";
//正确的写法:
Element root = new Element("root");  
root.setAttribute("att", attribute);  
root.setText(name);  
Document doc = new Documet();  
doc.setRootElement(root);  
XmlOutputter out = new XmlOutputter(Format.getPrettyFormat());  
String xml = out.outputString(root);

 未指定字符编码

//错误的写法:
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.getBytes();
//这样的代码主要不具有跨平台可移植性。因为不同的平台可能使用的是不同的默认字符编码。

//正确的写法:
Reader r = new InputStreamReader(new FileInputStream(file), "ISO-8859-1");  
Writer w = new OutputStreamWriter(new FileOutputStream(file), "ISO-8859-1");  
Reader r = new InputStreamReader(inputStream, "UTF-8");  
Writer w = new OutputStreamWriter(outputStream, "UTF-8");  
String s = new String(byteArray, "ASCII");  
byte[] a = string.getBytes("ASCII");

未对数据流进行缓存

//错误的写法:
InputStream in = new FileInputStream(file);   
int b;   
while ((b = in.read()) != -1) {   
...   
}
/**上面的代码是一个byte一个byte的读取,导致频繁的本地JNI文件系统访问,非常低效,因为调用本地方法是非常耗时的。最好用BufferedInputStream包装一下。曾经做过一个测试,从/dev/zero下读取1MB,大概花了1s,而用BufferedInputStream包装之后只需要60ms,性能提高了94%! 这个也适用于output stream操作以及socket操作。**/

//正确的写法:
InputStream in = new BufferedInputStream(new FileInputStream(file));

 不指定超时时间

//错误的代码:
Socket socket = ...   
socket.connect(remote);   
InputStream in = socket.getInputStream();   
int i = in.read();
/**这种情况在工作中已经碰到不止一次了。个人经验一般超时不要超过20s。这里有一个问题,connect可以指定超时时间,但是read无法指定超时时间。但是可以设置阻塞(block)时间。**/

//正确的写法:
Socket socket = ...   
socket.connect(remote, 20000); // fail after 20s   
InputStream in = socket.getInputStream();   
socket.setSoTimeout(15000);   
int i = in.read();
/**另外,文件的读取(FileInputStream, FileChannel, FileDescriptor, File)没法指定超时时间, 而且IO操作均涉及到本地方法调用, 这个更操作了JVM的控制范围,在分布式文件系统中,对IO的操作内部实际上是网络调用。一般情况下操作60s的操作都可以认为已经超时了。为了解决这些问题,一般采用缓存和异步/消息队列处理。**/

捕获所有的异常

//错误的写法:
Query q = ...   
Person p;   
try {   
    p = (Person) q.getSingleResult();   
} catch(Exception e) {   
    p = null;   
}
/**这是EJB3的一个查询操作,可能出现异常的原因是:结果不唯一;没有结果;数据库无法访问,而捕获所有的异常,设置为null将掩盖各种异常情况。**/

//正确的写法:
Query q = ...   
Person p;   
try {   
    p = (Person) q.getSingleResult();   
} catch(NoResultException e) {   
    p = null;   
}

重复包装RuntimeException

//错误的写法:
try {   
    doStuff();   
} catch(Exception e) {   
    throw new RuntimeException(e);   
}

//正确的写法:
try {   
    doStuff();   
} catch(RuntimeException e) {   
    throw e;   
} catch(Exception e) {   
    throw new RuntimeException(e.getMessage(), e);   
}   
try {   
    doStuff();   
} catch(IOException e) {   
    throw new RuntimeException(e.getMessage(), e);   
} catch(NamingException e) {   
    throw new RuntimeException(e.getMessage(), e);   
}

异常处理不彻底

//错误的写法:
try {   
    is = new FileInputStream(inFile);   
    os = new FileOutputStream(outFile);   
} finally {   
    try {   
        is.close();   
        os.close();   
    } catch(IOException e) {   
        /* we can't do anything */   
    }   
}

//is可能close失败, 导致os没有close
//正确的写法:
try {   
    is = new FileInputStream(inFile);   
    os = new FileOutputStream(outFile);   
} finally {   
    try { if (is != null) is.close(); } catch(IOException e) {/* we can't do anything */}   
    try { if (os != null) os.close(); } catch(IOException e) {/* we can't do anything */}   
}

不必要的初始化

//错误的写法:
public class B {   
    private int count = 0;   
    private String name = null;   
    private boolean important = false;   
}
//这里的变量会在初始化时使用默认值:0, null, false, 因此上面的写法有些多此一举。


//正确的写法:
public class B {   
    private int count;   
    private String name;   
    private boolean important;   
}

最好用静态final定义Log变量

 这样做的好处有三:

  • 可以保证线程安全
  • 静态或非静态代码都可用
  • 不会影响对象序列化
private static final Log log = LogFactory.getLog(MyClass.class);

 

 选择错误的类加载器

//错误的代码:
Class clazz = Class.forName(name);   
Class clazz = getClass().getClassLoader().loadClass(name);
/**这里本意是希望用当前类来加载希望的对象, 但是这里的getClass()可能抛出异常, 特别在一些受管理的环境中, 比如应用服务器, web容器, Java WebStart环境中, 最好的做法是使用当前应用上下文的类加载器来加载。**/

//正确的写法:
ClassLoader cl = Thread.currentThread().getContextClassLoader();   
if (cl == null) cl = MyClass.class.getClassLoader(); // fallback   
Class clazz = cl.loadClass(name);

HashMap size陷阱

//错误的写法:
Map map = new HashMap(collection.size());  
for (Object o : collection) {  
  map.put(o.key, o.value);  
}
/**这里可以参考guava的Maps.newHashMapWithExpectedSize的实现. 用户的本意是希望给HashMap设置初始值, 避免扩容(resize)的开销. 但是没有考虑当添加的元素数量达到HashMap容量的75%时将出现resize。**/

//正确的写法:
Map map = new HashMap(1 + (int) (collection.size() / 0.75));

 未完待续。。。。