代码中的“恶臭”,比如“私欲”、“灰尘”,每天都在增加,每天不清除,就会越来越累。如果努力去除这些“臭味”,不仅可以提高自己的编码水平,还可以让代码“一尘不染”。在此,一直从事Java研发的阿里地图高级技术工程师王超整理了一些日常工作中的“臭味”和清理方法,供大家参考。让代码更高效当你需要Map的主键和值时,你应该迭代entrySet()当你在循环中只需要Map的主键时,迭代keySet()是正确的。但是,当需要主键和值时,迭代entrySet()效率更高,比先迭代keySet()再获取值要好。反例:Mapma??p=...;for(Stringkey:map.keySet()){Stringvalue=map.get(key);...}正例:Mapma??p=...;for(Map.Entryentry:map.entrySet()){Stringkey=entry.getKey();Stringvalue=entry.getValue();...}应该使用Collection.isEmpty()Empty使用Collection.size()来检测是否为空没有逻辑问题,但是使用Collection.isEmpty()可以使代码更具可读性并且可以获得更好的性能。任何Collection.isEmpty()实现的时间复杂度为O(1),但某些Collection.size()实现的时间复杂度可能为O(n)。反例:if(collection.size()==0){...}正例:if(collection.isEmpty()){...}如果需要检测null,可以使用CollectionUtils.isEmpty(collection)和CollectionUtils。isNotEmpty(集合)。不要将集合对象传递给自己此外,由于某些方法要求参数在执行期间保持不变,因此将集合传递给自身可能会导致不稳定的行为。反例:Listlist=newArrayList<>();list.add("Hello");list.add("World");if(list.containsAll(list)){//无意义,总是返回true...}list.removeAll(list);//性能不好,直接用clear()初始化集合,尽量指定大小。java集合类用起来很方便,但是看源码,集合也是有大小限制的。每次展开的时间复杂度很可能是O(n),所以尽量指定一个可预测的集合大小来减少集合展开的次数。反例:int[]arr=newint[]{1,2,3};Listlist=newArrayList<>();for(inti:arr){list.add(i);}正例:int[]arr=newint[]{1,2,3};Listlist=newArrayList<>(arr.length);for(inti:arr){list.add(i);}字符串连接使用StringBuilder一般Java会在编译时优化字符串拼接,但是在java编译时无法优化循环中的字符串拼接,需要使用StringBuilder来代替。反例:Strings="";for(inti=0;i<10;i++){s+=i;}正例:Stringa="a";Stringb="b";Stringc="c";Strings=a+b+c;//没问题,java编译器会优化StringBuildersb=newStringBuilder();for(inti=0;i<10;i++){sb.append(i);//循环中,java编译器不能为了优化,需要手动使用StringBuilder}List的随机访问。大家都知道数组和链表的区别:数组的随机访问效率更高。调用方法获取到List后,如果想随机访问里面的数据,不知道数组内部实现是链表还是数组怎么办?可以判断是否实现了*RandomAccess*接口。正例://调用别人的服务获取listListlist=otherService.getList();if(listinstanceofRandomAccess){//内部数组实现,随机访问System.out.println(list.get(list.size()-1));}else{//内部可能是链表实现,随机访问效率低}经常调用Collection.contains方法,请使用Set在java集合类库中,contains的时间复杂度一般List的方法是O(n),如果代码中需要频繁调用contains方法查找数据,可以先将list转成HashSet,将时间复杂度从O(n)降低到O(1)。反例:ArrayListlist=otherService.getList();for(inti=0;i<=Integer.MAX_VALUE;i++){//时间复杂度O(n)list.contains(i);}正例:ArrayListlist=otherService.getList();Setset=newHashSet(list);for(inti=0;i<=Integer.MAX_VALUE;i++){//时间复杂度O(1)set。contains(i);}使代码更优雅。在长整数常量值后添加一个大写的L。当使用长整型常量值时,需要在其后加L。它必须是大写的L,而不是小写的l。小写的l很容易跟在数字1后面,造成误解。反例:longvalue=1l;longmax=Math.max(1L,5);正例:longvalue=1L;longmax=Math.max(1L,5L);Don'tusemagicvalues当你写一段代码的时候,使用magicvalues可能看起来很清楚,但是在调试的时候它们就显得不那么清楚了。这就是为什么需要将魔法值定义为可读常量。但是,-1、0和1不被视为魔法值。反例:for(inti=0;i<100;i++){...}if(a==100){...}正例:privatestaticfinalintMAX_COUNT=100;for(inti=0;imap=newHashMap(){{put("a",1);put("b",2);}};privatestaticListlist=newArrayList(){{add("a");add("b");}};正例:privatestaticMapmap=newHashMap<>();static{map.put("a",1);map.put("b",2);};privatestaticListlist=newArrayList<>();static{list.add("a");list.add("b");};建议使用try-with-resources语句Java7引入了try-with-resources语句,可以保证关闭相关资源,比原来的try-catch-finally语句更好,让程序代码更安全、更简洁。反例:privatevoidhandle(StringfileName){BufferedReaderreader=null;try{Stringline;reader=newBufferedReader(newFileReader(fileName));while((line=reader.readLine())!=null){...}}catch(Exceptione){...}finally{if(reader!=null){try{reader.close();}catch(IOExceptione){...}}}}正例:privatevoidhandle(StringfileName){try(BufferedReaderreader=newBufferedReader(newFileReader(fileName))){Stringline;while((line=reader.readLine())!=null){...}}catch(Exceptione){...}}删除未使用的私有方法和字段delete未使用的私有方法和字段使代码更简洁,更易于维护。如果需要再次使用,可以从历史提交中取回。反例:publicclassDoubleDemo1{privateintunusedField=100;privatevoidunusedMethod(){...}publicintsum(inta,intb){returna+b;}}正例:publicclassDoubleDemo1{publicintsum(inta,intb){returna+b;}}删除未使用的删除不用的局部变量的局部变量,使代码更简洁,更容易维护。反例:publicintsum(inta,intb){intc=100;returna+b;}正例:publicintsum(inta,intb){returna+b;}删除未使用的方法参数未使用的方法参数具有误导性,删除未使用的方法参数方法使用的参数使代码更简洁,更易于维护。但是,由于覆盖方法是基于父类或接口的方法定义,所以即使有未使用的方法参数,也不能删除。反例:publicintsum(inta,intb,intc){returna+b;}正例:publicintsum(inta,intb){returna+b;}删除表达式中多余的括号对应表达式中的多余括号,有人认为它有助于代码阅读,有些人认为这是完全没有必要的。对于熟悉Java语法的人来说,表达式中多出的括号会使代码更加繁琐。反例:return(x);return(x+2);intx=(y*3)+1;intm=(n*4+2);正例:returnx;returnx+2;intx=y*3+1;intm=n*4+2;工具类应该屏蔽构造函数。工具类是静态字段和函数的集合,不应该被实例化。然而,Java为每个没有显式定义构造函数的类添加了一个隐式公共构造函数。因此,为了避免java“小白”的错误使用,应该显式定义一个private构造函数来屏蔽这个隐含的public构造函数。反例:publicclassMathUtils{publicstaticfinaldoublePI=3.1415926D;publicstaticintsum(inta,intb){returna+b;}}正例:publicclassMathUtils{publicstaticfinaldoublePI=3.1415926D;privateMathUtils(){}publicstaticintsum(inta,intb){}删除冗余异常捕获而throw用catch语句捕捉到异常后,什么也不做,让异常再次抛出。这和没有捕获到异常是一样的。您可以删除这段代码或添加其他处理。反例:privatestaticStringreadFile(StringfileName)throwsIOException{try(BufferedReaderreader=newBufferedReader(newFileReader(fileName))){Stringline;StringBuilderbuilder=newStringBuilder();while((line=reader.readLine())!=null){builder.append(line);}returnbuilder.toString();}catch(Exceptione){throwe;}}正例:privatestaticStringreadFile(StringfileName)throwsIOException{try(BufferedReaderreader=newBufferedReader(newFileReader(fileName))){Stringline;StringBuilderbuilder=newStringBuilder();while((line=reader.readLine())!=null){builder.append(line);}returnbuilder.toString();}}公共静态常量应该通过类访问,尽管通过实例访问公共静态常量类是允许的,但是很容易让它误认为类的每个实例都有一个公共静态常量。因此,应该通过类直接访问公共静态常量。反例:publicclassUser{publicstaticfinalStringCONST_NAME="name";...}Useruser=newUser();StringnameKey=user.CONST_NAME;正例:publicclassUser{publicstaticfinalStringCONST_NAME="name";...}StringnameKey=User.CONST_NAME;不要使用NullPointerException对空值和空指针异常的判断应该通过代码来避免(比如检查它是否不为空),而不是通过捕获异常来避免。反例:publicStringgetUserName(Useruser){try{returnuser.getName();}catch(NullPointerExceptione){returnnull;}}正例:publicStringgetUserName(Useruser){if(Objects.isNull(user)){returnnull;}returnuser.getName();}使用String.valueOf(value)而不是""+value将其他对象或类型转换为字符串时,使用String.valueOf(value)比""+value更高效。反例:inti=1;Strings=""+i;正例:inti=1;Strings=String.valueOf(i);为过期代码添加@Deprecated注解当一段代码已经过时,但为了兼容又不能直接删除,不想以后有人再次使用时,可以添加@Deprecated注解来标记。在文档注释中添加@deprecated以解释和提供替代方案。正例:/***save**@deprecated该方法效率低,请使用{@linknewSave()}方法替换*/@Deprecatedpublicvoidsave(){//dosomething}保持代码远离bug,禁止使用构造函数BigDecimal(double)BigDecimal(double)存在精度丢失的风险,在精确计算或取值比较场景下可能会导致业务逻辑异常。反例:BigDecimalvalue=newBigDecimal(0.1D);//0.100000000000000005551115...正例:BigDecimalvalue=BigDecimal.valueOf(0.1D);//0.1返回空数组和空集合而不是null返回null,这需要由调用者检查null,否则将抛出空指针异常。返回一个空数组或集合,有效防止调用者因为调用者没有检测到null而抛出空指针异常,也可以删除调用者检测到null的语句,让代码更简洁。反例:publicstaticResult[]getResults(){returnnull;}publicstaticListgetResultList(){returnull;}publicstaticMapgetResultMap(){returnnull;}publicstaticvoidmain(String[]args){Result[]results=getResults();if(results!=null){for(Resultresult:results){...}}ListresultList=getResultList();if(resultList!=null){for(Resultresult:resultList){...}}MapresultMap=getResultMap();if(resultMap!=null){for(Map.EntryresultEntry:resultMap){...}}}正例:publicstaticResult[]getResults(){returnnewResult[0];}publicstaticListgetResultList(){returnCollections.emptyList();}publicstaticMapgetResultMap(){returnCollections.emptyMap();}publicstaticvoidmain(String[]args){结果[]results=getResults();for(Resultresult:results){...}ListresultList=getResultList();for(Resultresult:resultList){...}MapresultMap=getResultMap();for(地图.EntryresultEntry:resultMap){...}}优先使用常量或定值来调用equals方法。对象的equals方法很容易抛出空指针异常。您应该使用常量或确定的值来调用equals方法。当然,最好的做法是使用java.util.Objects.equals()方法。反例:publicvoidisFinished(OrderStatusstatus){returnstatus.equals(OrderStatus.FINISHED);//可能抛出空指针异常}正例:publicvoidisFinished(OrderStatusstatus){returnOrderStatus.FINISHED.equals(status);}publicvoidisFinished(OrderStatusstatus){返回对象。(status,OrderStatus.FINISHED);}枚举的属性字段必须是私有且不可变的。枚举通常用作常量。如果枚举中有公共属性字段或设置字段方法,那么这些枚举常量的属性就很容易被使用。修订。理想情况下,枚举中的属性字段是私有的,在私有构造函数中赋值,没有对应的Setter方法,最好带final修饰符。反例:publicenumUserStatus{DISABLED(0,"Disabled"),ENABLED(1,"Enable");publicintvalue;privateStringdescription;privateUserStatus(intvalue,Stringdescription){this.value=value;this.description=description;}publicStringgetDescription(){returndescription;}publicvoidsetDescription(Stringdescription){this.description=description;}}正例:publicenumUserStatus{DISABLED(0,"Disabled"),ENABLED(1,"Enable");privatefinalintvalue;privatefinalStringdescription;privateUserStatus(intvalue,Stringdescription){this.value=value;this.description=description;}publicintgetValue(){returnvalue;}publicStringgetDescription(){returndescription;}}注意String.split(Stringregex)stringString的split方法,传入的分隔符字符串是正则表达式!一些关键字(如.[]()\|等)需要进行转义。反例:"a.ab.abc".split(".");//结果为[]"a|ab|abc".split("|");//结果为["a",“|”,"a","b","|","a","b","c"]正例:"a.ab.abc".split("\\.");//结果为["a","ab","abc"]"a|ab|abc".split("\\|");//结果为["a","ab","abc"]的总结这篇文章,可以说是Java开发经验的总结,分享给大家参考。希望能帮助大家避坑,让代码更加高效优雅。