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