[案例1】

 public boolean equalNode(JudgeNode a, JudgeNode b) {
return a.getId() == b.getId();
}

【点评】

应在JudgeNode类里定义equals()方法(估计刚从面向过程语言"转行"过来...)。

【案例2】

 public String[] getMsg() {
List<String> msgList = new ArrayList<String>(2);
msgList.add(linkStatus.get(AC)); msgList.add(getLinkMsg());
String[] result = new String[msgList.size()];
msgList.toArray(result);
return result;
}

【点评】

可简化为return new String[]{linkStatus.get(AC), getLinkMsg()};

【案例3】

 private boolean bInteger(Object[][] values, int i) { //开发环境真实代码
boolean b = false;
try {
b = values[0][i] instanceof Integer;
} catch(Exception e) {
logger.error("values[0][i] instanceof Integer error:"+e);
b = false;
}
return b;
}

【点评】

1. instanceof为运算符,不会抛出空指针等异常;从上下文得知,values[0][i]也不会出现越界等异常。

因此,通过instanceof判断类型时无须捕获异常。进一步,bInteger()方法毫无意义,完全可以在调用处直接用instanceof判断。

2. 使用slf4j日志组件时,logger.error(与log.warn)接受Throwable参数,以打印异常名和详细的堆栈信息(可能内部调用e.printStackTrace())。

但书写打印语句时,需要注意格式。例如:

 logger.error("Best print: ", e);
logger.error("Good print: {}", e); //a.
logger.error("Bad print: " + e); //b. 或 + e.toString()
logger.error("Bad print: " + e.getMessage()); //c. 或: {}", e.getMessage())

a句仍可打印异常名和堆栈信息,但多输出一对花括号"{}";b句仅打印异常名;c句打印异常消息字符串。以空指针异常(Runtime异常)为例,b句打印"java.lang.NullPointerException",c句打印"null"(过于简陋)。

【案例4】

 public DBBatch(int count) throws SQLException {
con = ConnnectionManager.getConnection(DBConsts.DB_SOURCE_NAME);
preparedStatement = con.prepareStatement(DBConsts.SQL);
result = preparedStatement.executeQuery();
this.count = count;
}
public void close() { //close result\statement\connection
}

【点评】

应避免在构造函数里申请资源。若构造函数DBBatch(int count)里preparedStatement.executeQuery()发生异常,上面的preparedStatement、con将无法关闭。此时,外部获取不到DBBatch对象引用,也就无法调用DBBatch.close()方法,导致资源泄露。

【案例5】

 private static String listToString(List<String> columnList) {
StringBuilder colName = new StringBuilder(1000); //
for(String s : columnList) {
colName.append(s + ",");
}
colName.deleteCharAt(colName.length() - 1); //
return colName.toString();
}

【点评】

1. StringBuffer的初始容量为16个字节。若待处理的数据远大于16个字节,最好在创建StringBuffer时给出适当的初始容量,以减少不必要的扩容操作(容量不足时会通过Arrays.copyOf创建2倍容量的新数组来装载老数据)。但此处new StringBuilder(1000)初始化指定的长度显然过大,会造成空间浪费。

StringBuilder不同创建方式的初始容量如下:

1) StringBuilder()                  // 默认分配16个字节的空间
2) StringBuilder(int size)   // 默认分配size个字节的空间
3) StringBuilder(String str)  // 默认分配16个字节 + str.length()个字节空间
4) StringBuilder(String str)  // 默认分配16个字节 + str.length()个字节空间
5) StringBuilder(CharSequence charSeq) // 默认分配16个字节 + charSeq.length()个字节空间

    优选方式3,不建议方式4、5(进行字符串拼接时会面临再次扩容的问题)。

相关资料还可参考《浅谈JAVA中HashMap、ArrayList、StringBuilder等的扩容机制》。

2. columnList为空集合时,例如List<String> columnList = Collections.emptyList(),会抛出下标越界异常(StringIndexOutOfBoundsException)。

3. 可用String.join(",", columnList)代替。若期望"[a, b, c, d]"的输出格式,直接用columnList.toString()即可。

【案例6】

 private static boolean checkName1(String name) { //循环10000000次,耗时350ms
List<String> nameList = new ArrayList<String>();
nameList.add("LiLei"); nameList.add("HaMeimei"); nameList.add("HeDan");
nameList.add("Jame"); nameList.add("Lucy"); nameList.add("Beth");
if(nameList.contains(name)) {
return true;
}
return false;
} private static boolean checkName2(String name) { //循环10000000次,耗时1729ms
Set<String> nameSet = new HashSet<String>() {
{ add("LiLei"); add("HaMeimei"); add("HeDan");
add("Jame"); add("Lucy"); add("Beth");
}};
return nameSet.contains(name);
} private static final List<String> NAME_LIST = new ArrayList<String>() {
{ add("LiLei"); add("HaMeimei"); add("HeDan");
add("Jame"); add("Lucy"); add("Beth");
}};
private static boolean checkName3(String name) { //循环10000000次,耗时110ms
return NAME_LIST.contains(name);
} private static final Set<String> NAME_SET = new HashSet<String>() {
{ add("LiLei"); add("HaMeimei"); add("HeDan");
add("Jame"); add("Lucy"); add("Beth");
}};
private static boolean checkName4(String name) { //循环10000000次,耗时52ms
return NAME_SET.contains(name);
} private static boolean checkName5(String name) { //循环10000000次,耗时46ms
return "LiLei".equals(name) || "HaMeimei".equals(name) || "HeDan".equals(name) ||
"Jame".equals(name) || "Lucy".equals(name) || "Beth".equals(name);
}

【点评】

1. 不要使用"if(expression) return true; else return false;"的写法,应改为"return expression;"。

2. List的contains()方法内部调用indexOf()遍历查找整个数组,效率较低。若涉及大规模数据操作,应选用Map等容器。

3. 本案例采用《Java字符串连接的多种实现方法及效率对比》中的计时方法,统计checkName1~5()的执行效率。结果一目了然~

【案例7】

 Map<String, String> apMap = constructApMap();
for(String apId: apMap.keySet()) {
String apType = apMap.get(apId);
... ...

【点评】

建议使用apMap.entrySet遍历Map,而不是keySet+get(实际上遍历两次)。虽然Map get方法"很快",但也不能浪费!

相关资料参见《keySet 与entrySet 遍历HashMap性能差别》。

entrySet简化语法(Java 8函数式编程):

 Map<String, Object> map = new HashMap<>();
map.forEach((key, value)-> {
... ...
});

【案例8】

 private double[] devanningArray(List<Double> origin) {
double[] target = new double[origin.size()];
for(int i = 0; i < origin.size(); ++i) {
target[i] = origin.get(i);
... ...

【点评】

ArrayList随机访问,时间复杂度O(1);LinkedList随机访问,时间复杂度O(n)。

建议:1) 优先for-each,即for(e:list){};2) 若必须随机访问,建议从接口契约上约束,如fun(ArrayList array){}。

附Java容器类的特征概述:

ArrayList封装数组,适用于“读多写少”的场景;
LinkedList双向链表,适用于“写多”的场景,随机访问效率低;
Vector、Stack鸡肋
HashMap、HashTable关联容器,HashTable同步
TreeMap红黑树实现,有序:按逻辑大小排序
LinkedHashMap有序:按插入顺序排序
HashSet、TreeSet、LinkedHashSet封装对应的Map
ConcurrentHashMap
ConcurrentLinkedQueue、ConcurrentLinkedDueue无锁队列,慎用
ArrayBlockingQueue、LinkedBlockingQueue 阻塞队列
CopyOnWriteArrayList、CopyOnWriteArraySet 慎用
工具、封装类:
Arrays.asList、Collections.unmodifiableList\Set\Map\Collection
Collections.synchronizedList\Set\Map\Collection

Java编程常见缺陷汇总(一)的更多相关文章

  1. 50道经典的JAVA编程题(汇总)

    这是一次不可思议的编程历程.从2013年的最后一天开始做这份题,中间连续好几天的考试,包括java考试(今天考试的JAVA编程题),直到今天完成了.挺有成就感的...废话不多说了,来电实质性的吧. 全 ...

  2. Java编程:常见问题汇总

    每天在写Java程序,其实里面有一些细节大家可能没怎么注意,这不,有人总结了一个我们编程中常见的问题.虽然一般没有什么大问题,但是最好别这样做. AD: 每天在写Java程序,其实里面有一些细节大家可 ...

  3. java中常见异常汇总(根据自己遇到的异常不定时更新)

    1.java.lang.ArrayIndexOutOfBoundsException:N(数组索引越界异常.如果访问数组元素时指定的索引值小于0,或者大于等于数组的长度,编译程序不会出现任何错误,但运 ...

  4. NDK(22)JNI编程如何避免常见缺陷

    转自 : http://www.ibm.com/developerworks/cn/java/j-jni/index.html 避免常见缺陷 假设您编写了一些新 JNI 代码,或者继承了别处的某些 J ...

  5. Java Socket常见异常处理 和 网络编程需要注意的问题

    在java网络编程Socket通信中,通常会遇到以下异常情况: 第1个异常是 java.net.BindException:Address already in use: JVM_Bind. 该异常发 ...

  6. 大数据揭示的10个常见JAVA编程错误

    初学者最常犯的编程错误是什么呢?有可能他们总是混淆等值(==)与赋值(=),或者 & 和 &&:也有可能是他们在循环中使用错误的分隔符(for (int i = 0, i &l ...

  7. Java编程的逻辑 (90) - 正则表达式 (下 - 剖析常见表达式)

    本系列文章经补充和完善,已修订整理成书<Java编程的逻辑>,由机械工业出版社华章分社出版,于2018年1月上市热销,读者好评如潮!各大网店和书店有售,欢迎购买,京东自营链接:http:/ ...

  8. Java编程的逻辑 (64) - 常见文件类型处理: 属性文件/CSV/EXCEL/HTML/压缩文件

    ​本系列文章经补充和完善,已修订整理成书<Java编程的逻辑>,由机械工业出版社华章分社出版,于2018年1月上市热销,读者好评如潮!各大网店和书店有售,欢迎购买,京东自营链接:http: ...

  9. Java学习笔记(六)——google java编程风格指南(下)

    [前面的话] 年后开始正式上班,计划着想做很多事情,但是总会有这样那样的打扰,不知道是自己要求太高还是自我的奋斗意识不够?接下来好好加油.好好学学技术,好好学习英语,好好学习做点自己喜欢的事情,趁着自 ...

随机推荐

  1. 通过pip安装python web

    提示 No module named 'utils' 我就pip install utils 提示 No module named 'db' 然后我就 pip install db 报错 No mod ...

  2. 常见的git清单

    我们每天使用 Git ,但是很多命令记不住. 一般来说,日常使用只要记住下图6个命令,就可以了.但是熟练使用,恐怕要记住60-100个命令. 这篇文章是从别人博客上copy重新整理出来的,作为笔记用, ...

  3. python3.5环境配置

    前言: python3应该是python的趋势所在,当然目前争议也比较大,这篇随笔的主要目的是记录在linux6.4下搭建python3环境的过程 以及碰到的问题和解决过程. 另外,如果本机安装了py ...

  4. poj3273 Monthly Expense(二分搜索)

    https://vjudge.net/problem/POJ-3273 认真审题,代码仔细!!ans的初值应该是1 #include<iostream> #include<cstdi ...

  5. CSS中margin边界叠加问题及解决方案

    你对CSS的margin边界叠加的概念是否了解,这里和大家分享一下,当一个元素出现在另一个元素上面时,第一个元素的底边界与第二个元素的顶边界发生叠加. CSS的margin边界叠加深度剖析 边界叠加简 ...

  6. Linux进程管理学习资料

    下面是一些Linux进程管理相关的资料. 博客 Process Creation(一) Process Creation(二) 进程切换分析(1):基本框架 进程切换分析(2):TLB处理 When ...

  7. Javascript数组(一)排序

    一.简介首先,我们来看一下JS中sort()和reverse()这两个函数的函数吧reverse();这个函数是用来进行倒序,这个没有什么可说的,所谓倒序就是大的在前面,小的在后面. 比如: var ...

  8. golang 特殊知识点

    golang 代码不需要分号; 但是又会自己在底层增加;号 ,所以 golang的{左花括号必须在代码的最后一行,而不能在新的一行; golang 代码组织里需要注意 vendor 和 interna ...

  9. 用GO开发企业级分布式云存储系统

    一.基础架构 二.开发工具

  10. NLP 第10章 基于深度学习的NLP 算法