Java代码常见错误写法
程序员文章站
2022-03-02 12:15:24
...
对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));
未完待续。。。。
上一篇: LayUI笔记
下一篇: jsp EL表达式比较时间