Java编码技巧之高效代码50例

导读
世界上只有两种物质:高效率和低效率;世界上只有两种人:高效率的人和低效率的人。——萧伯纳
同理,世界上只有两种代码:高效代码和低效代码;世界上只有两种人:编写高效代码的人和编写低效代码的人。如何编写高效代码,是每个研发团队都面临的一个重大问题。所以,作者根据实际经验,查阅了大量资料,总结了java高效代码50例,让每一个java程序员都能编写出高效代码。
1.常量&变量
1.1.直接赋值常量值,禁止声明新对象
直接赋值常量值,只是创建了一个对象引用,而这个对象引用指向常量值。
反例:
longi=newlong(1l); strings=newstring(abc);
正例:
longi=1l; strings=abc;
1.2.当成员变量值无需改变时,尽量定义为静态常量
在类的每个对象实例中,每个成员变量都有一份副本,而成员静态常量只有一份实例。
反例:
publicclasshttpconnection{ privatefinallongtimeout=5l; ... }
正例:
publicclasshttpconnection{ privatestaticfinallongtimeout=5l; ... }
1.3.尽量使用基本数据类型,避免自动装箱和拆箱
java 中的基本数据类型double、float、long、int、short、char、boolean,分别对应包装类double、float、long、integer、short、character、boolean。 jvm支持基本类型与对应包装类的自动转换,被称为自动装箱和拆箱。装箱和拆箱都是需要cpu和内存资源的,所以应尽量避免使用自动装箱和拆箱。
反例:
integersum=0; int[]values=...; for(intvalue:values){ sum+=value;//相当于result=integer.valueof(result.intvalue()+value); }
正例:
intsum=0; int[]values=...; for(intvalue:values){ sum+=value; }
1.4.如果变量的初值会被覆盖,就没有必要给变量赋初值
反例:
listuserlist=newarraylist(); if(isall){ userlist=userdao.queryall(); }else{ userlist=userdao.queryactive(); }
正例:
listuserlist; if(isall){ userlist=userdao.queryall(); }else{ userlist=userdao.queryactive(); }
1.5.尽量使用函数内的基本类型临时变量
在函数内,基本类型的参数和临时变量都保存在栈(stack)中,访问速度较快;对象类型的参数和临时变量的引用都保存在栈(stack)中,内容都保存在堆(heap)中,访问速度较慢。在类中,任何类型的成员变量都保存在堆(heap)中,访问速度较慢。
反例:
publicfinalclassaccumulator{ privatedoubleresult=0.0d; publicvoidaddall(@nonnulldouble[]values){ for(doublevalue:values){ result+=value; } } ... }
正例:
publicfinalclassaccumulator{ privatedoubleresult=0.0d; publicvoidaddall(@nonnulldouble[]values){ doublesum=0.0d; for(doublevalue:values){ sum+=value; } result+=sum; } ... }
1.6.尽量不要在循环体外定义变量
在老版jdk中,建议“尽量不要在循环体内定义变量”,但是在新版的jdk中已经做了优化。通过对编译后的字节码分析,变量定义在循环体外和循环体内没有本质的区别,运行效率基本上是一样的。反而,根据“ 局部变量作用域最小化 ”原则,变量定义在循环体内更科学更便于维护,避免了延长大对象生命周期导致延缓回收问题 。
反例:
uservouservo; listuserdolist=...; listuservolist=newarraylist(userdolist.size()); for(userdouserdo:userdolist){ uservo=newuservo(); uservo.setid(userdo.getid()); ... uservolist.add(uservo); }
正例:
listuserdolist=...; listuservolist=newarraylist(userdolist.size()); for(userdouserdo:userdolist){ uservouservo=newuservo(); uservo.setid(userdo.getid()); ... uservolist.add(uservo); }
1.7.不可变的静态常量,尽量使用非线程安全类
不可变的静态常量,虽然需要支持多线程访问,也可以使用非线程安全类。
反例:
publicstaticfinalmapclass_map; static{ mapclassmap=newconcurrenthashmap(16); classmap.put(varchar,java.lang.string.class); ... class_map=collections.unmodifiablemap(classmap); }
正例:
publicstaticfinalmapclass_map; static{ mapclassmap=newhashmap(16); classmap.put(varchar,java.lang.string.class); ... class_map=collections.unmodifiablemap(classmap); }
1.8.不可变的成员变量,尽量使用非线程安全类
不可变的成员变量,虽然需要支持多线程访问,也可以使用非线程安全类。
反例:
@service publicclassstrategyfactoryimplementsinitializingbean{ @autowired privateliststrategylist; privatemapstrategymap; @override publicvoidafterpropertiesset(){ if(collectionutils.isnotempty(strategylist)){ intsize=(int)math.ceil(strategylist.size()*4.0/3); mapmap=newconcurrenthashmap(size); for(strategystrategy:strategylist){ map.put(strategy.gettype(),strategy); } strategymap=collections.unmodifiablemap(map); } } ... }
正例:
@service publicclassstrategyfactoryimplementsinitializingbean{ @autowired privateliststrategylist; privatemapstrategymap; @override publicvoidafterpropertiesset(){ if(collectionutils.isnotempty(strategylist)){ intsize=(int)math.ceil(strategylist.size()*4.0/3); mapmap=newhashmap(size); for(strategystrategy:strategylist){ map.put(strategy.gettype(),strategy); } strategymap=collections.unmodifiablemap(map); } } ... }
2.对象&类
2.1.禁止使用json转化对象
json提供把对象转化为json字符串、把json字符串转为对象的功能,于是被某些人用来转化对象。这种对象转化方式,虽然在功能上没有问题,但是在性能上却存在问题。
反例:
listuserdolist=...; listuservolist=json.parsearray(json.tojsonstring(userdolist),uservo.class);
正例:
listuserdolist=...; listuservolist=newarraylist(userdolist.size()); for(userdouserdo:userdolist){ uservouservo=newuservo(); uservo.setid(userdo.getid()); ... uservolist.add(uservo); }
2.2.尽量不使用反射赋值对象
用反射赋值对象,主要优点是节省了代码量,主要缺点却是性能有所下降。
反例:
listuserdolist=...; listuservolist=newarraylist(userdolist.size()); for(userdouserdo:userdolist){ uservouservo=newuservo(); beanutils.copyproperties(userdo,uservo); uservolist.add(uservo); }
正例:
listuserdolist=...; listuservolist=newarraylist(userdolist.size()); for(userdouserdo:userdolist){ uservouservo=newuservo(); uservo.setid(userdo.getid()); ... uservolist.add(uservo); }
2.3.采用lambda表达式替换内部匿名类
对于大多数刚接触jdk8的同学来说,都会认为lambda表达式就是匿名内部类的语法糖。实际上, lambda表达式在大多数虚拟机中采用invokedynamic指令实现,相对于匿名内部类在效率上会更高一些。
反例:
listuserlist=...; collections.sort(userlist,newcomparator(){ @override publicintcompare(useruser1,useruser2){ longuserid1=user1.getid(); longuserid2=user2.getid(); ... returnuserid1.compareto(userid2); } });
正例:
listuserlist=...; collections.sort(userlist,(user1,user2)->{ longuserid1=user1.getid(); longuserid2=user2.getid(); ... returnuserid1.compareto(userid2); });
2.4.尽量避免定义不必要的子类
多一个类就需要多一份类加载,所以尽量避免定义不必要的子类。
反例:
publicstaticfinalmapclass_map= collections.unmodifiablemap(newhashmap(16){ privatestaticfinallongserialversionuid=1l; { put(varchar,java.lang.string.class); } });
正例:
publicstaticfinalmapclass_map; static{ mapclassmap=newhashmap(16); classmap.put(varchar,java.lang.string.class); ... class_map=collections.unmodifiablemap(classmap); }
2.5.尽量指定类的final修饰符
为类指定final修饰符,可以让该类不可以被继承。如果指定了一个类为final,则该类所有的方法都是final的,java编译器会寻找机会内联所有的final方法。内联对于提升java运行效率作用重大,具体可参见java运行期优化,能够使性能平均提高50%。
反例:
publicclassdatehelper{ ... }
正例:
publicfinalclassdatehelper{ ... }
注意:使用spring的aop特性时,需要对bean进行动态代理,如果bean类添加了final修饰,会导致异常。
3.方法
3.1.把跟类成员变量无关的方法声明成静态方法
静态方法的好处就是不用生成类的实例就可以直接调用。静态方法不再属于某个对象,而是属于它所在的类。只需要通过其类名就可以访问,不需要再消耗资源去反复创建对象。即便在类内部的私有方法,如果没有使用到类成员变量,也应该声明为静态方法。
反例:
publicintgetmonth(datedate){ calendarcalendar=calendar.getinstance(); calendar.settime(date); returncalendar.get(calendar.month)+1; }
正例:
publicstaticintgetmonth(datedate){ calendarcalendar=calendar.getinstance(); calendar.settime(date); returncalendar.get(calendar.month)+1; }
3.2.尽量使用基本数据类型作为方法参数类型,避免不必要的装箱、拆箱和空指针判断
反例:
publicstaticdoublesum(doublevalue1,doublevalue2){ doubledouble1=objects.isnull(value1)?0.0d:value1; doubledouble2=objects.isnull(value2)?0.0d:value2; returndouble1+double2; } doubleresult=sum(1.0d,2.0d);
正例:
publicstaticdoublesum(doublevalue1,doublevalue2){ returnvalue1+value2; } doubleresult=sum(1.0d,2.0d);
3.3.尽量使用基本数据类型作为方法返回值类型,避免不必要的装箱、拆箱和空指针判断
在jdk类库的方法中,很多方法返回值都采用了基本数据类型,首先是为了避免不必要的装箱和拆箱,其次是为了避免返回值的空指针判断。比如:collection.isempty()和map.size()。
反例:
publicstaticbooleanisvalid(userdouser){ if(objects.isnull(user)){ returnfalse; } returnboolean.true.equals(user.getisvalid()); } //调用代码 userdouser=...; booleanisvalid=isvalid(user); if(objects.nonnull(isvalid)&&isvalid.booleanvalue()){ ... }
正例:
publicstaticbooleanisvalid(userdouser){ if(objects.isnull(user)){ returnfalse; } returnboolean.true.equals(user.getisvalid()); } //调用代码 userdouser=...; if(isvalid(user)){ ... }
3.4.协议方法参数值非空,避免不必要的空指针判断
协议编程,可以@nonnull和@nullable标注参数,是否遵循全凭调用者自觉。
反例:
publicstaticbooleanisvalid(userdouser){ if(objects.isnull(user)){ returnfalse; } returnboolean.true.equals(user.getisvalid()); }
正例:
publicstaticbooleanisvalid(@nonnulluserdouser){ returnboolean.true.equals(user.getisvalid()); }
3.5.协议方法返回值非空,避免不必要的空指针判断
协议编程,可以@nonnull和@nullable标注参数,是否遵循全凭实现者自觉。
反例:
//定义接口 publicinterfaceorderservice{ publiclistqueryuserorder(longuserid); } //调用代码 listorderlist=orderservice.queryuserorder(userid); if(collectionutils.isnotempty(orderlist)){ for(ordervoorder:orderlist){ ... } }
正例:
//定义接口 publicinterfaceorderservice{ @nonnull publiclistqueryuserorder(longuserid); } //调用代码 listorderlist=orderservice.queryuserorder(userid); for(ordervoorder:orderlist){ ... }
3.6.被调用方法已支持判空处理,调用方法无需再进行判空处理
反例:
userdouser=null; if(stringutils.isnotblank(value)){ user=json.parseobject(value,userdo.class); }
正例:
userdouser=json.parseobject(value,userdo.class);
3.7.尽量避免不必要的函数封装
方法调用会引起入栈和出栈,导致消耗更多的cpu和内存,应当尽量避免不必要的函数封装。当然,为了使代码更简洁、更清晰、更易维护,增加一定的方法调用所带来的性能损耗是值得的。
反例:
//函数封装 publicstaticbooleanisvip(booleanisvip){ returnboolean.true.equals(isvip); } //使用代码 booleanisvip=isvip(user.getvip());
正例:
booleanisvip=boolean.true.equals(user.getvip());
3.8.尽量指定方法的final修饰符
方法指定final修饰符,可以让方法不可以被重写,java编译器会寻找机会内联所有的final方法。内联对于提升java运行效率作用重大,具体可参见java运行期优化,能够使性能平均提高50%。
注意:所有的private方法会隐式地被指定final修饰符,所以无须再为其指定final修饰符。
反例:
publicclassrectangle{ ... publicdoublearea(){ ... } }
正例:
publicclassrectangle{ ... publicfinaldoublearea(){ ... } }
注意:使用spring的aop特性时,需要对bean进行动态代理,如果方法添加了final修饰,将不会被代理。
4.表达式
4.1.尽量减少方法的重复调用
反例:
listuserlist=...; for(inti=0;i 正例:
listuserlist=...; intuserlength=userlist.size(); for(inti=0;i 4.2.尽量避免不必要的方法调用
反例:
listuserlist=userdao.queryactive(); if(isall){ userlist=userdao.queryall(); }
正例:
listuserlist; if(isall){ userlist=userdao.queryall(); }else{ userlist=userdao.queryactive(); }
4.3.尽量使用移位来代替正整数乘除
用移位操作可以极大地提高性能。对于乘除2^n(n为正整数)的正整数计算,可以用移位操作来代替。
反例:
intnum1=a*4; intnum2=a/4;
正例:
intnum1=a2;
4.4.提取公共表达式,避免重复计算
提取公共表达式,只计算一次值,然后重复利用值。
反例:
doubledistance=math.sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));
正例:
doubledx=x2-x1; doubledy=y2-y1; doubledistance=math.sqrt(dx*dx+dy*dy); 或 doubledistance=math.sqrt(math.pow(x2-x1,2)+math.pow(y2-y1,2));
4.5.尽量不在条件表达式中用!取反
使用!取反会多一次计算,如果没有必要则优化掉。
反例:
if(!(a>=10)){ ...//条件处理1 }else{ ...//条件处理2 }
正例:
if(a<10){ ...//条件处理1 }else{ ...//条件处理2 }
4.6.对于多常量选择分支,尽量使用switch语句而不是if-else语句
if-else语句,每个if条件语句都要加装计算,直到if条件语句为true为止。switch语句进行了跳转优化,java中采用tableswitch或lookupswitch指令实现,对于多常量选择分支处理效率更高。经过试验证明:在每个分支出现概率相同的情况下,低于5个分支时if-else语句效率更高,高于5个分支时switch语句效率更高。
反例:
if(i==1){ ...;//分支1 }elseif(i==2){ ...;//分支2 }elseif(i==...){ ...;//分支n }else{ ...;//分支n+1 }
正例:
switch(i){ case1: ...//分支1 break; case2: ...//分支2 break; case...: ...//分支n break; default: ...//分支n+1 break; }
备注:如果业务复杂,可以采用map实现策略模式。
5.字符串
5.1.尽量不要使用正则表达式匹配
正则表达式匹配效率较低,尽量使用字符串匹配操作。
反例:
stringsource=a::1,b::2,c::3,d::4; stringtarget=source.replaceall(::,=); stringp[]targets=source.spit(::);
正例:
stringsource=a::1,b::2,c::3,d::4; stringtarget=source.replace(::,=); stringp[]targets=stringutils.split(source,::);
5.2.尽量使用字符替换字符串
字符串的长度不确定,而字符的长度固定为1,查找和匹配的效率自然提高了。
反例:
stringsource=a:1,b:2,c:3,d:4; intindex=source.indexof(:); stringtarget=source.replace(:,=);
正例:
stringsource=a:1,b:2,c:3,d:4; intindex=source.indexof(':'); stringtarget=source.replace(':','=');
5.3.尽量使用stringbuilder进行字符串拼接
string是final类,内容不可修改,所以每次字符串拼接都会生成一个新对象。stringbuilder在初始化时申请了一块内存,以后的字符串拼接都在这块内存中执行,不会申请新内存和生成新对象。
反例:
strings=; for(inti=0;i<10;i++){ if(i!=0){ s+=','; } s+=i; }
正例:
stringbuildersb=newstringbuilder(128); for(inti=0;i<10;i++){ if(i!=0){ sb.append(','); } sb.append(i); }
5.4.不要使用+转化字符串
使用+进行字符串转化,使用方便但是效率低,建议使用string.valueof.
反例:
inti=12345; strings=+i;
正例:
inti=12345; strings=string.valueof(i);
6.数组
6.1.不要使用循环拷贝数组,尽量使用system.arraycopy拷贝数组
推荐使用system.arraycopy拷贝数组,也可以使用arrays.copyof拷贝数组。
反例:
int[]sources=newint[]{1,2,3,4,5}; int[]targets=newint[sources.length]; for(inti=0;i 正例:
int[]sources=newint[]{1,2,3,4,5}; int[]targets=newint[sources.length]; system.arraycopy(sources,0,targets,0,targets.length);
6.2.集合转化为类型t数组时,尽量传入空数组t[0]
将集合转换为数组有2种形式:toarray(new t[n])和toarray(new t[0])。在旧的java版本中,建议使用toarray(new t[n]),因为创建数组时所需的反射调用非常慢。在openjdk6后,反射调用是内在的,使得性能得以提高,toarray(new t[0])比toarray(new t[n])效率更高。此外,toarray(new t[n])比toarray(new t[0])多获取一次列表大小,如果计算列表大小耗时过长,也会导致toarray(new t[n])效率降低。
反例:
listintegerlist=arrays.aslist(1,2,3,4,5,...); integer[]integers=integerlist.toarray(newinteger[integerlist.size()]);
正例:
listintegerlist=arrays.aslist(1,2,3,4,5,...); integer[]integers=integerlist.toarray(newinteger[0]);//勿用newinteger[]{}
建议:集合应该提供一个toarray(class clazz)方法,避免无用的空数组初始化(new t[0])。
6.3.集合转化为object数组时,尽量使用toarray()方法
转化object数组时,没有必要使用toarray[new object[0]],可以直接使用toarray()。避免了类型的判断,也避免了空数组的申请,所以效率会更高。
反例:
listobjectlist=arrays.aslist(1,2,3,4,5,...); object[]objects=objectlist.toarray(newobject[0]);
正例:
listobjectlist=arrays.aslist(1,2,3,4,5,...); object[]objects=objectlist.toarray();
7.集合
7.1.初始化集合时,尽量指定集合大小
java集合初始化时都会指定一个默认大小,当默认大小不再满足数据需求时就会扩容,每次扩容的时间复杂度有可能是o(n)。所以,尽量指定预知的集合大小,就能避免或减少集合的扩容次数。
反例:
listuserdolist=...; setuserset=newhashset(); mapusermap=newhashmap(); listuserlist=newarraylist(); for(userdouserdo:userdolist){ userset.add(userdo.getid()); usermap.put(userdo.getid(),userdo); userlist.add(transuser(userdo)); }
正例:
listuserdolist=...; intusersize=userdolist.size(); setuserset=newhashset(usersize); mapusermap=newhashmap((int)math.ceil(usersize*4.0/3)); listuserlist=newarraylist(usersize); for(userdouserdo:userdolist){ userset.add(userdo.getid()); usermap.put(userdo.getid(),userdo); userlist.add(transuser(userdo)); }
7.2.不要使用循环拷贝集合,尽量使用jdk提供的方法拷贝集合
jdk提供的方法可以一步指定集合的容量,避免多次扩容浪费时间和空间。同时,这些方法的底层也是调用system.arraycopy方法实现,进行数据的批量拷贝效率更高。
反例:
listuser1list=...; listuser2list=...; listuserlist=newarraylist(user1list.size()+user2list.size()); for(userdouser1:user1list){ userlist.add(user1); } for(userdouser2:user2list){ userlist.add(user2); }
正例:
listuser1list=...; listuser2list=...; listuserlist=newarraylist(user1list.size()+user2list.size()); userlist.addall(user1list); userlist.addall(user2list);
7.3.尽量使用arrays.aslist转化数组为列表
原理与不要使用循环拷贝集合,尽量使用jdk提供的方法拷贝集合类似。
反例:
listtypelist=newarraylist(8); typelist.add(short); typelist.add(integer); typelist.add(long); string[]names=...; listnamelist=...; for(stringname:names){ namelist.add(name); }
正例:
listtypelist=arrays.aslist(short,integer,long); string[]names=...; listnamelist=...; namelist.addall(arrays.aslist(names));
7.4.直接迭代需要使用的集合
直接迭代需要使用的集合,无需通过其它操作获取数据。
反例:
mapusermap=...; for(longuserid:usermap.keyset()){ userdouser=usermap.get(userid); ... }
正例:
mapusermap=...; for(map.entryuserentry:usermap.entryset()){ longuserid=userentry.getkey(); userdouser=userentry.getvalue(); ... }
7.5.不要使用size方法检测空,必须使用isempty方法检测空
使用size方法来检测空逻辑上没有问题,但使用isempty方法使得代码更易读,并且可以获得更好的性能。任何isempty方法实现的时间复杂度都是o(1),但是某些size方法实现的时间复杂度有可能是o(n)。
反例:
listuserlist=...; if(userlist.size()==0){ ... } mapusermap=...; if(usermap.size()==0){ ... }
正例:
listuserlist=...; if(userlist.isempty()){ ... } mapusermap=...; if(usermap.isempty()){ ... }
7.6.非随机访问的list,尽量使用迭代代替随机访问
对于列表,可分为随机访问和非随机访问两类,可以用是否实现randomaccess接口判断。随机访问列表,直接通过get获取数据不影响效率。而非随机访问列表,通过get获取数据效率极低。
反例:
linkedlistuserdolist=...; intsize=userdolist.size(); for(inti=0;i 正例:
linkedlistuserdolist=...; for(userdouserdo:userdolist){ ... }
其实,不管列表支不支持随机访问,都应该使用迭代进行遍历。
7.7.尽量使用hashset判断值存在
在java集合类库中,list的contains方法普遍时间复杂度是o(n),而hashset的时间复杂度为o(1)。如果需要频繁调用contains方法查找数据,可以先将list转换成hashset。
反例:
listadminidlist=...; listuserdolist=...; listuservolist=newarraylist(userdolist.size()); for(userdouserdo:userdolist){ if(adminidlist.contains(userdo.getid())){ uservolist.add(transuser(userdo)); } }
正例:
setadminidset=...; listuserdolist=...; listuservolist=newarraylist(userdolist.size()); for(userdouserdo:userdolist){ if(adminidset.contains(userdo.getid())){ uservolist.add(transuser(userdo)); } }
7.8.避免先判断存在再进行获取
如果需要先判断存在再进行获取,可以直接获取并判断空,从而避免了二次查找操作。
反例:
publicstaticuservotransuser(userdouser,maprolemap){ uservouservo=newuservo(); uservo.setid(user.getid()); ... if(rolemap.contains(user.getroleid())){ roledorole=rolemap.get(user.getroleid()); uservo.setrole(transrole(role)); } }
正例:
publicstaticuservotransuser(userdouser,maprolemap){ uservouservo=newuservo(); uservo.setid(user.getid()); ... roledorole=rolemap.get(user.getroleid()); if(objects.nonnull(role)){ uservo.setrole(transrole(role)); } }
8.异常
8.1.直接捕获对应的异常
直接捕获对应的异常,避免用instanceof判断,效率更高代码更简洁。
反例:
try{ savedata(); }catch(exceptione){ if(einstanceofioexception){ log.error(保存数据io异常,e); }else{ log.error(保存数据其它异常,e); } }
正例:
try{ savedata(); }catch(ioexceptione){ log.error(保存数据io异常,e); }catch(exceptione){ log.error(保存数据其它异常,e); }
8.2.尽量避免在循环中捕获异常
当循环体抛出异常后,无需循环继续执行时,没有必要在循环体中捕获异常。因为,过多的捕获异常会降低程序执行效率。
反例:
publicdoublesum(listvaluelist){ doublesum=0.0d; for(stringvalue:valuelist){ try{ sum+=double.parsedouble(value); }catch(numberformatexceptione){ returnnull; } } returnsum; }
正例:
publicdoublesum(listvaluelist){ doublesum=0.0d; try{ for(stringvalue:valuelist){ sum+=double.parsedouble(value); } }catch(numberformatexceptione){ returnnull; } returnsum; }
8.3.禁止使用异常控制业务流程
相对于条件表达式,异常的处理效率更低。
反例:
publicstaticbooleanisvalid(userdouser){ try{ returnboolean.true.equals(user.getisvalid()); }catch(nullpointerexceptione){ returnfalse; } }
正例:
publicstaticbooleanisvalid(userdouser){ if(objects.isnull(user)){ returnfalse; } returnboolean.true.equals(user.getisvalid()); }
9.缓冲区
9.1.初始化时尽量指定缓冲区大小
初始化时,指定缓冲区的预期容量大小,避免多次扩容浪费时间和空间。
反例:
stringbufferbuffer=newstringbuffer(); stringbuilderbuilder=newstringbuilder();
正例:
stringbufferbuffer=newstringbuffer(1024); stringbuilderbuilder=newstringbuilder(1024);
9.2.尽量重复使用同一缓冲区
针对缓冲区,java虚拟机需要花时间生成对象,还要花时间进行垃圾回收处理。所以,尽量重复利用缓冲区。
反例:
stringbuilderbuilder1=newstringbuilder(128); builder1.append(updatet_usersetname=').append(username).append('whereid=).append(userid); statement.executeupdate(builder1.tostring()); stringbuilderbuilder2=newstringbuilder(128); builder2.append(selectid,namefromt_userwhereid=).append(userid); resultsetresultset=statement.executequery(builder2.tostring()); ...
正例:
stringbuilderbuilder=newstringbuilder(128); builder.append(updatet_usersetname=').append(username).append('whereid=).append(userid); statement.executeupdate(builder.tostring()); builder.setlength(0); builder.append(selectid,namefromt_userwhereid=).append(userid); resultsetresultset=statement.executequery(builder.tostring()); ...
其中,使用setlength方法让缓冲区重新从0开始。
9.3.尽量设计使用同一缓冲区
为了提高程序运行效率,在设计上尽量使用同一缓冲区。
反例:
//转化xml(userdo) publicstaticstringtoxml(userdouser){ stringbuilderbuilder=newstringbuilder(128); builder.append(); builder.append(toxml(user.getid())); builder.append(toxml(user.getname())); builder.append(toxml(user.getrole())); builder.append(); returnbuilder.tostring(); } //转化xml(long) publicstaticstringtoxml(longvalue){ stringbuilderbuilder=newstringbuilder(128); builder.append(); builder.append(value); builder.append(); returnbuilder.tostring(); } ... //使用代码 userdouser=...; stringxml=toxml(user);
正例:
//转化xml(userdo) publicstaticvoidtoxml(stringbuilderbuilder,userdouser){ builder.append(); toxml(builder,user.getid()); toxml(builder,user.getname()); toxml(builder,user.getrole()); builder.append(); } //转化xml(long) publicstaticvoidtoxml(stringbuilderbuilder,longvalue){ builder.append(); builder.append(value); builder.append(); } ... //使用代码 userdouser=...; stringbuilderbuilder=newstringbuilder(1024); toxml(builder,user); stringxml=builder.tostring();
去掉每个转化方法中的缓冲区申请,申请一个缓冲区给每个转化方法使用。从时间上来说,节约了大量缓冲区的申请释放时间;从空间上来说,节约了大量缓冲区的临时存储空间。
9.4.尽量使用缓冲流减少io操作
使用缓冲流bufferedreader、bufferedwriter、bufferedinputstream、bufferedoutputstream等,可以大幅减少io次数并提升io速度。
反例:
try(fileinputstreaminput=newfileinputstream(a); fileoutputstreamoutput=newfileoutputstream(b)){ intsize=0; byte[]temp=newbyte[1024]; while((size=input.read(temp))!=-1){ output.write(temp,0,size); } }catch(ioexceptione){ log.error(复制文件异常,e); }
正例:
try(bufferedinputstreaminput=newbufferedinputstream(newfileinputstream(a)); bufferedoutputstreamoutput=newbufferedoutputstream(newfileoutputstream(b))){ intsize=0; byte[]temp=newbyte[1024]; while((size=input.read(temp))!=-1){ output.write(temp,0,size); } }catch(ioexceptione){ log.error(复制文件异常,e); }
其中,可以根据实际情况手动指定缓冲流的大小,把缓冲流的缓冲作用发挥到最大。
10.线程
10.1.在单线程中,尽量使用非线程安全类
使用非线程安全类,避免了不必要的同步开销。
反例:
stringbufferbuffer=newstringbuffer(128); buffer.append(select*from).append(t_user).append(whereid=?);
正例:
stringbuilderbuffer=newstringbuilder(128); buffer.append(select*from).append(t_user).append(whereid=?);
10.2.在多线程中,尽量使用线程安全类
使用线程安全类,比自己实现的同步代码更简洁更高效。
反例:
privatevolatileintcounter=0; publicvoidaccess(longuserid){ synchronized(this){ counter++; } ... }
正例:
privatefinalatomicintegercounter=newatomicinteger(0); publicvoidaccess(longuserid){ counter.incrementandget(); ... }
10.3.尽量减少同步代码块范围
在一个方法中,可能只有一小部分的逻辑是需要同步控制的,如果同步控制了整个方法会影响执行效率。所以,尽量减少同步代码块的范围,只对需要进行同步的代码进行同步。
反例:
privatevolatileintcounter=0; publicsynchronizedvoidaccess(longuserid){ counter++; ...//非同步操作 }
正例:
privatevolatileintcounter=0; publicvoidaccess(longuserid){ synchronized(this){ counter++; } ...//非同步操作 }
10.4.尽量合并为同一同步代码块
同步代码块是有性能开销的,如果确定可以合并为同一同步代码块,就应该尽量合并为同一同步代码块。
反例:
//处理单一订单 publicsynchronizedhandleorder(orderdoorder){ ... } //处理所有订单 publicvoidhandleorder(listorderlist){ for(orderdoorder:orderlist){ handleorder(order); } }
正例:
//处理单一订单 publichandleorder(orderdoorder){ ... } //处理所有订单 publicsynchronizedvoidhandleorder(listorderlist){ for(orderdoorder:orderlist){ handleorder(order); } }
10.5.尽量使用线程池减少线程开销
多线程中两个必要的开销:线程的创建和上下文切换。采用线程池,可以尽量地避免这些开销。
反例:
publicvoidexecutetask(runnablerunnable){ newthread(runnable).start(); }
正例:
privatestaticfinalexecutorserviceexecutor_service=executors.newfixedthreadpool(10); publicvoidexecutetask(runnablerunnable){ executorservice.execute(runnable); }

怎样确定变、配电所的巡视检查路线呢?
以色列推出一种绕过障碍物以清除面部表情的面部识别方案
vivo X60 Pro深度评测一览
KVM设备系统助力能源石化生产运营中心提高管控效能
峰岹科技电机驱动IC
Java编码技巧之高效代码50例
BP3189A 晶丰明源高精度的两绕组、低 PF 原边反馈恒流驱动器
贸泽电子开售新款Sensirion SHT4xA车用相对湿度/温度传感器
Intel芯片组的简介
跨链中可以赚取矿工费吗
打造极致的城市体验:让“造城”如“造车”
短路,断线,振动报警器,Short-circuit alarm
荣耀9什么时候上市最新消息:华为荣耀96月27日发布,配置、价格、外观统统对标小米6!就看你选谁?
单bit信号同步器和DMUX同步器的工作原理
小米6什么时候上市:小米6又延期?小米6陶瓷机身+双摄像头设计你还等吗?
边缘计算中的能耗难题,有解吗?
圆形连接器的基本结构_圆形连接器的优点和缺点
快讯:世界互联网大会将11月23日举行 多家芯片厂商获准向华为供货
人工智能能不能加速芯片的发展
e络盟加大投入产品线,进一步扩展测试与测量产品线