java代码之美(11)---java代码的优化
java代码的优化
随着自己做开发时间的增长,越来越理解雷布斯说的: 敲代码要像写诗一样美。也能理解有一次面试官问我你对代码有洁癖吗?
一段好的代码会让人看就像诗一样,也像一个干净房间会让人看去很舒服。
一段好的项目代码我觉得可以用这三个维度去分析。1)性能
2)可扩展性
3)可读性
有关代码的规范早在很久就有阿里巴巴的java开发手册,里面有非常多的规范。太多了,自己也没完全记住,抽空也会时不时再去翻翻。
接下来就写一些有关性能和可读性一些习惯,不全以后想到什么会再补充进来。
一、性能考虑
1、必须注意: 不对数据库层做任何操作
如果业务的确需要,那也最好注解说明原因。
2、尽量减少对变量的重复计算。
在不做编译优化的情况下,在循环中,循环条件会被反复计算,如果不使用复杂表达式,而使循环条件值不变的话,程序将会运行的更快。
for (int i = 0; i < list.size(); i++)
{...}
//建议修改成:
for (int i = 0, length = list.size(); i < length; i++)
{...}
这样list.size()只会调用一次,减少性能消耗。
3、尽量采用懒加载的策略,即在需要的时候才创建。
这个习惯需要培养,在写逻辑的时候,尤其是创建对象的时候是否需要考虑懒加载。
例如:
A a = new A();
if (i == 1)
{
list.add(a);
}
//建议替换为:
if (i == 1)
{
A a = new A();
list.add(a);
}
4、字符串累加。
1)循环外: 字符串拼接可以直接使用String的+操作,没有必要通过StringBuilder进行append.
2)循环内: 好的做法是在循环外声明StringBuilder对象,在循环内进行手动append。不论循环多少层都只有一个 StringBuilder对象。
反编译出的字节码文件显示每次循环都会 new 出一个 StringBuilder 对象,然后进行 append 操作,最后通过 toString 方法返回 String 对象,造成内存资源浪费。
StringBuffer sb = new StringBuffer();
sb.append("a");
sb.append("b");
sb.append("c");
sb.append("d");
//不在循环体内其实可以直接用加号,优化后一行代码:
String sb="a"+"b"+"c"+"d";
有关JDK不同版本对String拼接的优化可以参考:jdk不同版本对String拼接的优化分析
5、尽量避免使用split。
split由于支持正则表达式,所以效率比较低。
替代
String str1="a,b,c,d,,f,g";
//可以考虑使用apache的StringUtils.split(string,char)
List<String> list = Arrays.asList(StringUtils.split(str1, ","));
//可以考虑guava工具
List<String> list1=Splitter.on(",").splitToList(str1);
6、确定Stringbuffer的容量
Stringbuffer的构造器会创建一个默认大小(通常是16
)的字符数组。在使用中,如果超出这个大小,就会重新分配内存,创建一个更大的数组,并将原先的数组复制过来,再丢弃旧的数组。在大多数情况下,你可以在创建Stringbuffer的时候指定大小,这样就避免了在容量不够的时候自动增长,以提高性能。
例子:
Stringbuffer buffer = new Stringbuffer(); // violation
buffer.append ("hello");
//更正好:为stringbuffer提供寝大小。一般循环体内使用都可以知道大小
Stringbuffer buffer = new Stringbuffer(max);
buffer.append ("hello");
7、使用工具类 Arrays.asList()把数组转换成集合时,不能使用其修改集合相关的方 法。
它的 add/remove/clear 方法会抛出 UnsupportedOperationException 异常。
说明
:asList 的返回对象是一个 Arrays 内部类,并没有实现集合的修改方法。Arrays.asList 体现的是适配器模式,只是转换接口,后台的数据仍是数组。
String[] str = new String[] { "a", "b" };
List list = Arrays.asList(str);
//第一种情况:list.add("c"); 运行时异常。
//第二种情况:str[0]= "gujin"; 那么list.get(0)也会随之修改。
8、查找数组元素,可以用Arrays.asList(T[] array).contains(T obj)
二、可读性考虑
1、推荐尽量少用 else, if-else 的方式
可以考虑:
if(condition){
...
return obj; }
// 接着写 else 的业务逻辑代码;
说明
:如果非得使用if()…else if()…else…方式表达逻辑,【强制】请勿超过3层,超过请使用状态设计模式。正例
:逻辑上超过 3 层的 if-else 代码可以使用卫语句,或者状态模式来实现。
接下来抽空会写一篇超过三层if-else更好的解决方案博客。
2、在 if/else/for/while/do 语句中必须使用大括号,即使只有一行代码。
避免使用: if (condition) statements;
3、使用条件操作符替代”if (cond) return; else return;” 结构。
//条件操作符更加的简捷
if (isdone) {
return 0;
} else {
return 10;
}
//更正
return (isdone ? 0 : 10);
4、Object 的 equals 方法容易抛空指针异常,应使用常量或确定有值的对象来调用 equals。
正例
: “test”.equals(object);反例
: object.equals(“test”);说明
:推荐使用java.util.Objects (JDK7引入的工具类)
5、不允许出现任何魔法值(即未经定义的常量)直接出现在代码中。
反例
String key="Id#taobao_"+tradeId;cache.put(key, value);
6、取反操作符(!)降低程序的可读性,所以不要总是使用。
boolean method (boolean a, boolean b) {
if (!a)
return !a;
else
return !b;
}
7、注释掉的代码尽量要配合说明,而不是简单的注释掉。
代码被注释掉有两种可能性:1)后续会恢复此段代码逻辑
。2)永久不用
。前者如果没 有备注信息,难以知晓注释动机。后者建议直接删掉(代码仓库保存了历史代码)。
8、特殊注释标记,请注明标记人与标记时间。
1) 待办事宜(TODO)
🙁 标记人,标记时间,[预计处理时间]) 表示需要实现,但目前还未实现的功能。
2) 错误不能工作(FIXME)
:(标记人,标记时间,[预计处理时间])在注释中用 FIXME 标记某代码是错误的,而且不能工作,需要及时纠正的情况。
只要自己变优秀了,其他的事情才会跟着好起来(少将1)