J2EE代码优化
-
与log4j有关的性能问题
- Logger变量添加static修饰符
- 使用transient防止Logger对象序列化
- 使用log.isDebugEnable()来减少准备形参的开销
-
与字符串有关的性能问题
-
不要使用String中提供的split,replace,replaceAll等方法
-
替代方案:使用org.apache.commons.lang.StringUtils中提供的split,replaceChars,性能是JDK实现的3-4倍
split示例:
public static void main(String[] args) { String test = "123,123,123,123,123,123,123,123,123"; for (int i = 0; i < 100000; i++) { test = test.concat("123,123,123,123,123,123,123,123,123"); } long start = System.currentTimeMillis(); // String[] split = test.split(","); // 44 String[] split = StringUtils.split(test, ','); // 37 long stop = System.currentTimeMillis(); System.out.println(stop - start); }
replace,replaceAll,replaceChars示例
public static void main(String[] args) { String test = "123,123,123,123,123,123,123,123,123"; for (int i = 0; i < 100000; i++) { test = test.concat("123,123,123,123,123,123,123,123,123"); } long start = System.currentTimeMillis(); // System.out.println(test.replace("123", "456")); // 99 // System.out.println(test.replaceAll("123", "456")); // 101 StringUtils.replace(test, "123", "456"); // 44 StringUtils.replaceChars(test, "123", "456"); // 77 long stop = System.currentTimeMillis(); System.out.println(stop - start); }
-
用StringBuilder替代StringBuffer。StringBuilder效率快
在append()方法中不要使用+号做字符串连接操作
错误示例:
StringBuilder sb = new StringBuilder(); sb.append("111"+"222"); // 还是在做字符串 + 操作!
-
在构造StringBuilder时,要能预估容量就更好
原因:防止StringBuilder因预先分配的容量不够而做干净扩充!
-
-
与时间有关的性能问题
-
避免重复构造SimpleDateFormat对象,SimpleDateFormat对象的创建开销很大
-
避免利用SimpleDateFormat对象,因为SimpleDateFormat对象是不可重用的
-
建议使用apache工具包提供的:org.apache.commons.lang.time.DateFormatUtils,DateFormatUtils.format(new Date(),“yyyy-MM-dd HH:mm:ss”);
性能可提高3倍!
SimpleDateFormat示例:
public static void main(String[] args) { long start = System.currentTimeMillis(); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String format = simpleDateFormat.format(new Date()); System.out.println(format); long end = System.currentTimeMillis(); System.out.println(end - start); // 27 }
DateFormatUtils示例:
public static void main(String[] args) { long start = System.currentTimeMillis(); String format = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss"); System.out.println(format); long end = System.currentTimeMillis(); System.out.println(end - start); }
-
-
与for循环有关的性能问题
-
避免条件判断时调用函数
错误示例:
public static void main(String[] args) { List<String> lists = new ArrayList<>(); for (int i = 0; i < 10000; i++) { lists.add("aa"); } long start = System.currentTimeMillis(); for (int i = 0; i < lists.size(); i++) { System.out.println(lists.get(i)); } long end = System.currentTimeMillis(); System.out.println(end - start); // 46 }
正确示例:
public static void main(String[] args) { List<String> lists = new ArrayList<>(); for (int i = 0; i < 10000; i++) { lists.add("aa"); } long start = System.currentTimeMillis(); for (int i = 0,size = lists.size(); i < size; i++) { System.out.println(lists.get(i)); } long end = System.currentTimeMillis(); System.out.println(end - start); // 44 }
-
try…catch要放在循环外面
// 错误的方式: for(…) { try { … } catch (Exception e) { … } } // 正确的方式: try { for(…) { … } } catch (Exception e) { … }
-
避免在循环中反复调用同一个结果集的同一个对象
// 错误方式for(int i = 0;i < datas.size();i++) { datas.getData(i).getString("字段名"); datas.getData(i).getString("字段名"); datas.getData(i).getString("字段名"); // datas.getData(i) 重复获取对象}// 正确方式for(int i = 0;i < datas.size();i++) { 类型 变量名 = datas.getData(i); 变量名.getString("字段名"); 变量名.getString("字段名"); 变量名.getString("字段名");}
-
-
与集合类有关的性能问题
-
能用ArrayList就不要用Vector
-
能用HashMap就不要用Hashtable
-
能用HashMap就不要用ConcurrentHashMap
前三条原因:
能不加锁就不加锁
-
用 ConcurrentHashMap 替代 Collections.synchronizedMap(new HashMap());
原因:控制锁的粒度尽可能小,前者是多个细粒度的局部锁,后者是一把全局锁
-
在使用容器时能预设容量,尽量预设
比如:
new ArrayList(30);new HashMap(30);// 注意,够用就好,不要设置得过大!
原因:防止基于数据实现的ArrayList二次扩充,和HashMap的重hash
-
-
与开关有关的性能问题
-
不论是框架开关,还是业务开关,到java端后最 好保存为boolean型
// 错误方式:if ("true".equals(validate)) { …}// 正确方式:if (validate) { …}
前者开销是后者的几十倍,字符串越长,开销越大!
-
-
与工具方法有关的性能问题
-
将小工具函数标识为final
// 如:public static final boolean isBlank (String str) { if (null == str || "".equals(str.trim())) { return true; } return false;}
原因:JDK在运行一定次数后JIT(即时编译器)有可能将此final类型的函数内联
-
-
与SQL动态绑定有关的性能问题
能用PreparedStatement就不要使用Statement
-
与反射调用有关的性能问题
尽可能缓存Method对象,获取Method对象是反射调用最消耗性能的地方
-
缓存使用的性能问题
-
缓存的作用有两个:
- 将你要的数据搬到离你更近的地方
- 缓存计算
避免从缓存获取数据后,再对象数据进行排序、过滤等操作。
正确的做法是,先排序好,过滤完再放进缓存,以后直接拿来用即可。
-
-
并发锁带来的性能问题
- 最好是不用锁,比如用CAS机制解决并发问题
- 在读多写少的并发场景,用读写锁,不要有排它锁,如果synchronize
-
杂项
- 避免频繁使用instanceof做类型判断,建议拆成多个对象,用多态调用
- 能不用正则表达式就不用,正则表达式只是更为灵活,但在特定场景下性能不一定最优
- 避免重复对象的反复构造,可以复用就复用,尤其是大对象的构造,是非常消耗性能的
- 两数组对对拷,记得用System.arraycopy(),不要自己写for循环,性能差距非常大