1. 不要迷信静态代码扫描工具检测的结果
例如 SonarLint 部分检测项不许确,须要甄别。
其中一条,‘String 方法单个字符使用''比""效率高’,该条目有问题,用''和""效率差距不大,随便使用哪一个,参考:
https://stackoverflow.com/questions/33646781/java-performance-string-indexofchar-vs-string-indexofsingle-string
2. 考虑使用阿里 p3c 插件
该插件和 SonarLint 不冲突,可同时使用。
3. switch 必须有 default
[阿里手册] 在一个 switch 块内,都必须包含一个 default 语句而且放在最后,即便空代码。
缘由:1. 捕获意想不到的值 2. 处理默认状况 3. 告诉阅读代码的人你已经考虑了那种状况
4. 不容许魔法值
[阿里手册] 不容许任何魔法值 ( magic number,即未经预先定义的常量 ) 直接出如今代码中。
缘由:1. 数值的意义难以理解 2. 数值须要变更时,可能要改不仅一个地方
5. 工具类要有私有构造器
工具类是一些静态成员的集合,不但愿被初始化,实例化对它没有任何意义。能够在私有构造器内部添加 throw new AssertionError(), 防止其被内部调用。html
public class ClassExtraUtil { private ClassExtraUtil (){ } //..... }
参考:Effective Java 第二版 第四条 经过私有构造器强化不可实例化的能力
7. 不要使用同步的类:Vector、Hashtable、Stack、StringBuffer
说明:建议使用->不推荐使用java
ArrayList | LinkedList -> Vector
Deque -> Stack
HashMap | ConcurrentHashMap -> Hashtable
StringBuilder -> StringBuffer
8. 不要用构造器初始化 Integer
说明:
根据 Integer.valueOf()的 java Doc:
Returns an Integer instance representing the specified int value. If a new Integer instance is not
required, this method should generally be used in preference to the constructor Integer(int), as
this method is likely to yield significantly better space and time performance by caching
frequently requested values. This method will always cache values in the range -128 to 127,
inclusive, and may cache other values outside of this range. 应该使用 Integer i = 1 或 Integer i = Integer.valueOf(1)的形式构造整形包装类型,因为整型的缓存机制,这样能够提升时间性能且节省内存空间。Integer i = 1 是语法糖,反编译后是 Integer i = Integer.valueOf(1),因此用 Integer i = 1 这种形式。
9. 全部的包装类对象之间值的比较所有使用 equals
判断 Integer 和 int 用 ==,Integer 会自动拆箱。判断 Integer 和 Integer 用 equals。
[阿里手册] 对于Integer var=?在-128至127之间的赋值,Integer对象是在IntegerCache.cache产生,会复用已有对象,这个区间内的 Integer 值能够直接使用==进行判断,可是这个区间以外的全部数据,都会在堆上产生,并不会复用已有对象,这是一个大坑,推荐使用 equals方法进行判断。
10. 日志取代 System.out.println 和 printStackTrace
11. 遵照广泛接受的命名惯例
[阿里手册] 常量命名所有大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字长。
[Effective Java] 把标准的命名惯例看成一种内在的机制来看待,而且学者用它们做为第二特性。
Effective Java 第二版 第 56 条 遵照广泛接受的命名惯例
12. BigDecimal.valueOf(double val) 取 代 new BigDecimal(double val)
说明:
new BigDecimal(double x)会致使精度丢失。用 BigDecimal(String val)也能够。
BigDecimal.valueOf()底层实现:git
public static BigDecimal valueOf(double val) { return new BigDecimal(Double.toString(val)); }
13. 不要忽略没有反作用的函数返回值程序员
没有反作用的函数返回值被忽略,这种状况要么方法调用是没意义的,应该删除,要么源码的行为和预期不符。github
if (content.length() > 1024) { content.substring(0,1024); }
14. 不要使用 catch 块中的“instanceof”测试异常类型sql
15. 集合操做前要判 null
说明:此处 payApplyEntities 传入方法中,可能会被设为 null,要防止 NPE。
16. 避免没必要要的初始化数据库
List<Object> lists;//不用 List<Object> lists = new ArrayList<>(); lists= xxxMapper.selectList(params);
17. 了解和使用类库编程
if (Collectoins.isEmpty(result)) { //不使用 result == null || result.iEmpty() throw new XxxException("xxxx"); }
参考:Effective Java 第二版 第 47 条 了解和使用类库数组
18. 提炼分解过长的方法
[阿里手册]
单个方法的总行数不超过 80 行。 说明:包括方法签名、结束右大括号、方法内代码、注释、
空行、回车及任何不可见字符的总行数不超过 80 行。
[重构]
咱们要遵照这样一条原则:每当感受须要以注释说明点什么的时候,咱们就把须要说明的东
西写进一个独立的方法中,并以其用途命名。
[代码整洁之道]
函数的第一规则是短小,第二条规则还要更短小。函数应该作一件事。作好这件事。只作这一件事。
19. 移除不用的导入
20. 考虑“public static final”取代"public static" 说明:
由于被声明为"public static"的成员变量能够被任何对象修改,应该用 final 使其不可变。
21. 可合并的 if
说明:合并可折叠的 if 可提升代码可读性
22. 坚持使用 Override 注解
[阿里手册]
全部的覆写方法,必须加@Override 注解。
反例:getObject()与 get0bject()的问题。一个是字母的 O,一个是数字的 0,加@Override 可
以准确判断是否覆盖成功。另外,若是在抽象类中对方法签名进行修改,其实现类会立刻编
译报错。
参考:Effective Java 第二版 第 36 条 坚持使用 Override 注解
23. Map 的 key 是枚举时用 EnumMap
说明:根据 Java Doc:Implementation note: All basic operations execute in constant time. They are likely (though not guaranteed) to be faster than their HashMap counterparts. EnumMap 可能比 HashMap 快。
24. 不要声明局部变量而后返回或抛出缓存
return userService.selectList<params>; //List<UserEntity> lists = userService.selectList<params>; //return lists;
25. “<>”取代”<...>”
Map<String,Object> maps = new HashMap<>(); //Map<String,Object> maps = new HashMap<String,Objec>();
26. Boolean 值不要冗余
return string.length()<1024; //return string.length()<1024 ? true:false;
27. 考 虑 用 @GetMapping, @PostMapping 等 取 代 @RequestMapping
//@RequestMapping(value ="/getUser",method=RequestMethod.GET) @GetMapping("/getuser")
@ResponseBody public Response() getUserList(){ }
28. 不要在同一行声明多个变量
说明:在同一行声明多个变量不方便注释。
参考:https://www.oracle.com/technetwork/java/javase/documentation/codeconventions-141270.html#2991
29. 避免用对象引用访问静态成员或静态方法
Calendar date = Calendar.getInstance(); date.set(Calendar.DATE,date.getActualMaxinum(Calendar.DATE)); //date.set(Calendar.DATE,date.getActualMaxinum(date.DATE));
[阿里手册]
避免经过一个类的对象引用访问此类的静态变量或静态方法,无谓增长编译器解析成本,直
接用类名来访问便可。
30. 按正确顺序声明修饰符
说明:
Java 语言规范推荐按如下顺序声明修饰符:
1. Annotations 2. public 3. protected 4. private 5. abstract 6. static
7. final 8. transient 9. volatile 10. synchronized 11. native 12. strictfp
31. 须要同时遍历 Map 的 Key 和 Value 时用 entrySet()
//修改前 for (Object key : header.keySet()) { method.setHeader(String.valueOf(key),String.valueOf(header.get(key))) } //修改后 for (Map.Entry<String,Object> entry : header.entrySet()) { method.setHeader(entry.getKey(),String.valueOf(entry.getValue())) }
32. 不要忽略异常
参考:Effective Java 第二版 第 65 条 不要忽略异常
33. 不要将只包含一条语句的 lambda 嵌套在一个块中
34. 不要在循环中使用“+”链接字符串
[阿里手册] 循环体内,字符串的联接方式,使用 StringBuilder 的 append 方法进行扩展。
说明:反编译出的字节码文件显示每次循环都会 new 出一个 StringBuilder 对象,而后进行
append 操做,最后经过 toString 方法返回 String 对象,形成内存资源浪费。
36. 不抛出原生异常
说明:不抛出原生异常,包括 Error, RuntimeException, Throwable, and Exception,抛出单独的异常类型可让调用代码时控制如何处理每一个异常。此处应该抛出咱们自定义的异常
BusinessException。
37. 谨慎注释掉代码
说明:
[代码整洁之道]
直接把代码注释掉是讨厌的作法。别这么干!
其余人不敢注释掉的代码。他们会想,代码依然放在那儿,必定有其缘由,并且这段代码很
重要,不能删除。注释掉的代码堆积在一块儿,就像破酒瓶底的渣滓通常。
咱们已经拥有优良的源代码控制系统如此之久,这些系统能够为咱们记住不要的代码。咱们
无需用注释来标记,删掉便可,它们丢不了。
[阿里手册]
谨慎注释掉代码。在上方详细说明,而不是简单地注释掉。若是无用,则删除。代码被注释
掉有两种可能性:
(1) 后续会恢复此段代码逻辑。
(2) 永久不用。前者若是没有备注信息,难以知晓注释动机。后者建议直接删掉 ( 代码
仓库保存了历史代码 ) 。
38. long 或者 Long 初始赋值时必须用大写的 L
[阿里手册] long 或者 Long 初始赋值时,必须使用大写的 L,不能是小写的 l,小写容易跟
数字 1 混淆,形成误解。
39. 在 if/else/for/while/do 语句中必须使用大括号
[阿里手册] 在 if/else/for/while/do 语句中必须使用大括号,即便只有一行代码,避免使用下
面的形式:if (condition) statements;
40. 使用常量或肯定有值的对象来调用 equals
[阿里手册] Object 的 equals 方法容易抛空指针异常,应使用常量或肯定有值的对象来调用
equals。
41. 不要使用行尾注释
[阿里手册] 方法内部单行注释,在被注释语句上方另起一行,使用//注释。方法内部多行注
释使用/* */注释。注意与代码对齐。
42. 简化 stream API 调用链
说明:
stream API 调用链能够被简化,这样能够在遍历集合时避免建立重复的临时对象。
如下调用链能够被替代:
collection.stream().forEach() → collection.forEach()
collection.stream().collect(toList/toSet/toCollection()) → new CollectionType<>(collection)
collection.stream().toArray() → collection.toArray()
Arrays.asList().stream() → Arrays.stream() or Stream.of()
IntStream.range(0, array.length).mapToObj(idx -> array[idx]) → Arrays.stream(array)
IntStream.range(0, list.size()).mapToObj(idx -> list.get(idx)) → list.stream()
Collections.singleton().stream() → Stream.of()
Collections.emptyList().stream() → Stream.empty()
stream.filter().findFirst().isPresent() → stream.anyMatch()
stream.collect(counting()) → stream.count()
stream.collect(maxBy()) → stream.max()
stream.collect(mapping()) → stream.map().collect()
stream.collect(reducing()) → stream.reduce()
stream.collect(summingInt()) → stream.mapToInt().sum()
stream.mapToObj(x -> x) → stream.boxed()
stream.map(x -> {...; return x;}) → stream.peek(x -> ...)
!stream.anyMatch() → stream.noneMatch()
!stream.anyMatch(x -> !(...)) → stream.allMatch()
stream.map().anyMatch(Boolean::booleanValue) -> stream.anyMatch()
IntStream.range(expr1, expr2).mapToObj(x -> array[x]) -> Arrays.stream(array, expr1, expr2)
Collection.nCopies(count, ...) -> Stream.generate().limit(count)
stream.sorted(comparator).findFirst() -> Stream.min(comparator)
43. 类、类属性、类方法的注释必须使用 javadoc 规范
[阿里手册] 类、类属性、类方法的注释必须使用 javadoc 规范,使用/**内容*/格式,不得使
用//xxx 方式和/*xxx*/方式。 在 IDE 编辑窗口中,javadoc 方式会提示相关注释,生成 javadoc
能够正确输出相应注释;在 IDE 中,工程调用方法时,不进入方法便可悬浮提示方法、参数、
返回值的意义,提升阅读效率。
44. 集合初始化时指定集合初始值大小
说明:HashMap 使用 new HashMap(int initialCapacity)构造方法进行初始化,若是暂时没法确
定集合大小,那么指定默认值(16)便可。
45. sql.xml 配置参数使用#{}
[阿里手册] sql. xml 配置参数使用:#{},# param # 不要使用${} 此种方式容易出现 SQL 注
入。
46. SQL 模糊查询参数绑定考虑使用 bind 标签
模糊查询通常有三种方式:
1. Java 代码里拼接匹配符: 代码和 SQL 耦合度高;查看 xml 不能直接看出查询条件,下降开
发效率;有可能在 service 和 facade 层屡次加%_
2. SQL 里用 concat 拼接匹配符:增长数据库运算
3. 使用<bind>:Java 代码作链接,推荐使用
47. 使用 Optional 取代 null
48. 考虑使用不可变集合
说明:
不可变对象有不少优势,包括:
1.当对象被不可信的库调用时,不可变形式是安全的;
2.不可变对象被多个线程调用时,不存在竞态条件问题
3.不可变集合不须要考虑变化,所以能够节省时间和空间。全部不可变的集合都比它们的可
变形式有更好的内存利用率(分析和测试细节);
4.不可变对象由于有固定不变,能够做为常量来安全使用。
建立对象的不可变拷贝是一项很好的防护性编程技巧。Guava 为全部 JDK 标准集合类型和
Guava 新集合类型都提供了简单易用的不可变版本。
JDK 也提供了 Collections.unmodifiableXXX 方法把集合包装为不可变形式,但咱们认为不够
好:
1.笨重并且累赘:不能温馨地用在全部想作防护性拷贝的场景;
2.不安全:要保证没人经过原集合的引用进行修改,返回的集合才是事实上不可变的;
3.低效:包装过的集合仍然保有可变集合的开销,好比并发修改的检查、散列表的额外空间,
等等。
若是你没有修改某个集合的需求,或者但愿某个集合保持不变时,把它防护性地拷贝到
不可变集合是个很好的实践。
关于防护式编程,《代码大全》里“防护式编程”这一章有详细的介绍。防护式编程的
概念来自于防护式驾驶,在防护式驾驶中要创建这样一种思惟,那就是你永远不能肯定另外一
位司机要作什么。这样才能确保其余人作出危险动做时你也不会受到伤害。对于不可变集合,
看成为参数传递到其余方法时,不用担忧该集合会被改变,在当前方法能够放心继续使用该
集合。防护式编程的核心思想是认可程序都会有问题,都须要被修改。
参考:
https://github.com/google/guava/wiki/ImmutableCollectionsExplained
《代码大全》
49. 避免采用取反逻辑运算符
[阿里手册] 取反逻辑不利于快速理解,而且取反逻辑写法必然存在对应的正向逻辑写法。
50. 全部枚举类型字段必需要有注释
[阿里手册] 全部的枚举类型字段必需要有注释,说明每一个数据项的用途
51. 遇到多个构造器参数时要考虑用构建器
说明:
[Effective Java] 若是类的构造器或者静态工厂中具备多个参数,设计这种类时,Builder 模式
就是种不错的选择,特别是当大多数参数都是可选的时候。与使用传统的重叠构造器模式相
比,使用 Builder 模式的客户端代码将更易于阅读和编写,构建器也比 JavaBeans 更加安全。
参考:
Effective Java 第二版 第 2 条 遇到多个构造器参数时要考虑用构建器
52. try-with-resources 优于 try-finally
说明:
在处理必须关闭的资源时,使用 try-with-resources 语句替代 try-finally 语句。 生成的代码更
简洁,更清晰,而且生成的异常更有用。try-with-resources 语句在编写必须关闭资源的代码
时会更容易,也不会出错,而使用 try-finally 可能会出错。
53. lambda 表达式优于匿名类
说明:
从 Java8 开始,lambda 表达式是表示小函数对象的最佳方式。除非必须建立非函数式接
口类型的实例,不然不要使用匿名类做为函数对象。另外,lambda 表达式使表示小函数对
象变得如此容易,可使用函数式编程。
lambda 表达式没有名称和文档; 若是计算不是显而易见的,或者超过几行,则不要将其
放入 lambda 表达式中。一行代码对于 lambda 说是理想的,三行代码是合理的最大值。如
果违反这一规定,可能会严重损害程序的可读性。
参考:Effective Java 3rd Item 42: Prefer lambdas to anonymous classes
54. 方法引用优先于 Lambda 表达式
说明:方法引用一般为 lambda 提供一个更简洁的选择。 若是方法引用看起来更简短更清晰,请
使用它们;不然,仍是坚持 lambda
参考:Effective Java 3rdItem 43: Prefer method references to lambdas
55. 优先使用标准的函数式接口
如今 Java 已经有了 lambda 表达式,所以必须考虑 lambda 表达式来设计你的 API。在输
入 上 接 受 函 数 式 接 口 类 型 并 在 输 出 中 返 回 它 们 。 一 般 来 说 , 最 好 使 用
java.util.function.Function 中提供的标准接口,但请注意,在相对罕见的状况下须要编写本身
的函数式接口。
在 java.util.Function 中有 43 个接口。不能期望所有记住它们,可是若是记住了六个基本
接口,就能够在须要它们时派生出其他的接口。六种基本函数式接口概述以下:
接口 方法 示例
UnaryOperator T apply(T t) String::toLowerCase
BinaryOperator T apply(T t1, T t2) BigInteger::add
Predicate boolean test(T t) Collection::isEmpty
Function<T,R> R apply(T t) Arrays::asList
Supplier T get() Instant::now
Consumer void accept(T t) System.out::println
参考:Effective Java 3rdItem 44: Favor the use of standard functional interfaces
56. 谨慎地使用 streams
Stream API 具备足够的通用性,实际上任何计算均可以使用 Stream 执行,但仅仅是可
以,并不意味着应该这样作。若是使用得当,流可使程序更短更清晰;若是使用不当,过
度使用流会使程序难以阅读且难以维护。过分使用流使程序难于阅读和维护。
有些任务最好使用流来完成,有些任务最好使用迭代来完成。将这两种方法结合起来,
能够最好地完成许多任务。对于选择使用哪一种方法进行任务,没有硬性规定,可是有一些有
用的启发式方法。在许多状况下,使用哪一种方法将是清楚的;在某些状况下,则不会很清楚。
若是不肯定一个任务是经过流仍是迭代更好地完成,那么尝试这两种方法,看看哪种效果
更好。
参考:Effective Java 3rdItem 45: Use streams judiciously
57. 优先考虑流中无反作用的函数
管道流编程的本质是无反作用的函数对象。这适用于传递给流和相关对象的全部许多函
数对象。终结操做 forEach 仅应用于报告流执行的计算结果,而不是用于执行计算。 为了
正确使用流,必须了解收集器。最重要的收集器工厂是 toList,toSet,toMap,groupingBy
和 join。
参考:Effective Java 3rdItem 46: Prefer side-effect-free functions in streams
58. 优先使用集合而不是流做为返回类型
在编写返回元素序列的方法时,请记住,某些用户可能但愿将它们做为流处理,而其余
用户可能但愿以迭代的方式来处理。尽可能适应这两类人。若是返回集合是可行的,请执行此
操做。Collection 接口是 Iterable 的子类型,而且具备 stream 方法,所以它提供迭代和流访
问。所以,Collection 或适当的子类型一般是公共序列返回方法的最佳返回类型。若是返回
的元素是基本类型或有严格的性能要求,则使用数组,数组还使用 Arrays.asList 和 Stream.of
方法提供简单的迭代和流访问。若是返回集合是不可行的,则返回流或可迭代的,不管哪一个
看起来更天然。
参考:Effective Java 3rdItem 47: Prefer Collection to Stream as a return type
59. 谨慎使用并行流
一般,并行性带来的性能优点在 ArrayList、HashMap、HashSet 和 ConcurrentHashMap
实例、数组、int 类型范围和 long 类型的范围的流上最好。这些数据结构的共同之处在于,
它们均可以精确而廉价地分割成任意大小的子程序,这使得在并行线程之间划分工做变得很
容易。
并行化一个流不只会致使糟糕的性能,包括活性失败,还会致使不正确的结果和不可预
知的行为(安全故障)。使用映射器(mappers),过滤器(filters)和其余程序员提供的不符
合其规范的功能对象的管道并行化可能会致使安全故障。
总之,甚至不要尝试并行化管道流,除非你有充分的理由相信它将保持计算的正确性并
提升其速度。不恰当地并行化流的代价多是程序失败或性能灾难。若是您认为并行性是合
理的,那么请确保您的代码在并行运行时保持正确,并在实际状况下进行仔细的性能度量。
若是您的代码是正确的,而且这些实验证明了您对性能提升的怀疑,那么只有这样才能在生
产环境代码中并行化流。
参考:Effective Java 3rdItem 48: Use caution when making streams parallel
60. 谨慎地返回 optionals若是你发如今写一个不会老是返回一个值的方法,而且你认为重要的是,使用该方法的人每次调用该方法时都要考虑到这种可能性,那么你就应该返回一个 Optional。可是,你应该意识到返回 Optional 会影响性能,Optional 是一个须要被分配空间和初始化的对象,而且从 Optional 中读取值须要额外的间接操做,因此对于性能敏感的方法返回 Optional 是不合适的,最好是返回 null 或者抛出异常。最后,除了做为返回值之外,几乎不该该以任何其余方式使用 Optional。参考:Effective Java 3rdItem 55: Return optionals judiciously