1. 程式人生 > >求求你了,不要再自己實現這些邏輯了,開源工具類不香嗎?

求求你了,不要再自己實現這些邏輯了,開源工具類不香嗎?

最近公司來了一批實習生,小黑哥負責帶一個。這位小師弟說實話,基本功很紮實,做事也非常靠譜,深得小黑哥真傳。 ![](https://i02piccdn.sogoucdn.com/f6aaf20d44494b85) 不過最近給其 Review 程式碼的時候,小黑哥發現小師弟有些程式碼邏輯有些繁瑣,有些程式碼小黑哥看來可以用一些開源工具類實現,不需要自己重複實現。 不過這也是正常的,小黑哥剛入行的時候寫的程式碼也是這樣,這幾年慢慢接觸了一些開源工具類,逐漸積累。現在寫程式碼才會直接用工具類替換自己實現的這些繁瑣的邏輯。 於是小黑哥給小師弟分享了幾個自己常用的開源工具類,小師弟學完直呼:『666』。 ![](https://img2020.cnblogs.com/other/1419561/202005/1419561-20200512095750131-833516218.jpg) 這裡小黑哥拋磚引玉,分享幾個常用的工具類,希望幫助到剛入行的同學們。其他**程式設計老司機**如果還有其他好用的工具類,歡迎評論區分享。 下文主要分享這幾個方向的常用工具類: ![](https://img2020.cnblogs.com/other/1419561/202005/1419561-20200512095750431-1947058752.png) ## 字串相關工具類 Java 中 String 應該是日常用的最多一個類吧,平常我們很多程式碼需要圍繞 String ,做一些處理。 JDK 提供 String API 雖然比較多,但是功能比較基礎,通常我們需要結合 String 多個方法才能完成一個業務功能。 下面介紹一下 Apache 提供的一個工具類 StringUtils. Maven Pom 資訊如下: ```xml org.apache.commons commons-lang3 3.10 ``` > commons-lang 有兩個版本,一個是 commons-lang3 ,一個是 commons-lang 。 > > commons-lang 是老版本,已經很久沒有維護了。 > > commons-lang3 是一直在維護的版本,推薦直接使用這個版本。 > > **注意:如果你係統已經有 commons-lang,注意如果直接替換成 commons-lang3,將會編譯錯誤。commons-lang3 中相關類與 commons-lang 一樣,但是包名不一樣。** ### 判斷字串是否為空 判斷字串是否為空,想必每個人應該都寫過吧: ```java if (null == str || str.isEmpty()) { } ``` 雖然這段程式碼非常簡單,但是說實話,小黑哥以前還是在這裡犯過空指標的異常的。 ![img](https://img2020.cnblogs.com/other/1419561/202005/1419561-20200512095750611-1093002754.gif) 使用 StringUtils ,上面程式碼可以替換下面這樣: ```java if (StringUtils.isEmpty(str)) { } ``` StringUtils 內部還有一個方法 `isBlank`,也是用來判斷字串是否為空,兩個方法比較相近,比較搞混,主要區別如下: ```java // 如果字串都是空格的話, StringUtils.isBlank(" ") = true; StringUtils.isEmpty(" ") = false; ``` 判斷字串是否為空,使用頻率非常高,這裡大家可以使用 IDEA Prefix 的功能,輸入直接生成判空語句。 ![](https://img2020.cnblogs.com/other/1419561/202005/1419561-20200512095751561-1141481004.png) ### 字串固定長度 這個通常用於字串需要固定長度的場景,比如需要固定長度字串作為流水號,若流水號長度不足,,左邊補 0 。 這裡當然可以使用 `String#format` 方法,不過小黑哥覺得比較麻煩,這裡可以這樣使用: ```java // 字串固定長度 8位,若不足,忘左補 0 StringUtils.leftPad("test", 8, "0"); ``` 另外還有一個 `StringUtils#rightPad`,這個方法與上面方法正好相反。 ### 字串關鍵字替換 `StringUtils` 提供一些列的方法,可以替換某些關鍵字: ```java // 預設替換所有關鍵字 StringUtils.replace("aba", "a", "z") = "zbz"; // 替換關鍵字,僅替換一次 StringUtils.replaceOnce("aba", "a", "z") = "zba"; // 使用正則表示式替換 StringUtils.replacePattern("ABCabc123", "[^A-Z0-9]+", "") = "ABC123"; .... ``` ### 字串拼接 字串拼接是個常見的需求,簡單辦法使用 `StringBuilder` 迴圈遍歷拼接: ```java String[] array = new String[]{"test", "1234", "5678"}; StringBuilder stringBuilder = new StringBuilder(); for (String s : array) { stringBuilder.append(s).append(";"); } // 防止最終拼接字串為空 if (stringBuilder.length() >
0) { stringBuilder.deleteCharAt(stringBuilder.length() - 1); } System.out.println(stringBuilder.toString()); ``` 上面業務程式碼不太難,但是需要注意一下上面這段程式碼非常容易出錯,容易丟擲 `StringIndexOutOfBoundsException`。 這裡我們可以直接使用以下方法獲取拼接之後字串: ```java StringUtils.join(["a", "b", "c"], ",") = "a,b,c" ``` `StringUtils` 只能傳入陣列拼接字串,不過我比較喜歡集合拼接,所以再推薦下 Guava 的 `Joiner`。 例項程式碼如下: ```java String[] array = new String[]{"test", "1234", "5678"}; List list=new ArrayList<>(); list.add("test"); list.add("1234"); list.add("5678"); StringUtils.join(array, ","); // 逗號分隔符,跳過 null Joiner joiner=Joiner.on(",").skipNulls(); joiner.join(array); joiner.join(list); ``` ### 字串拆分 有字串拼接,就會有拆分字串的需求,同樣的 `StringUtils` 也有拆分字串的方法。 ```java StringUtils.split("a..b.c", '.') = ["a", "b", "c"] StringUtils.splitByWholeSeparatorPreserveAllTokens("a..b.c", ".")= ["a","", "b", "c"] ``` > ps:注意以上兩個方法區別。 `StringUtils` 拆分之後得到是一個數組,我們可以使用 Guava 的 ```java Splitter splitter = Splitter.on(","); // 返回是一個 List 集合,結果:[ab, , b, c] splitter.splitToList("ab,,b,c"); // 忽略空字串,輸出結果 [ab, b, c] splitter.omitEmptyStrings().splitToList("ab,,b,c") ``` `StringUtils` 內部還有其他常用的方法,小夥伴可以自行檢視其 API。 ## 日期相關工具類 ### DateUtils/DateFormatUtils JDK8 之前,Java 只提供一個 `Date` 類,平常我們需要將 `Date` 按照一定格式轉化成字串,我們需要使用 `SimpleDateFormat`。 ```java SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // Date 轉 字串 simpleDateFormat.format(new Date()); // 字串 轉 Date simpleDateFormat.parse("2020-05-07 22:00:00"); ``` 程式碼雖然簡單,但是這裡需要注意 `SimpleDateFormat`,不是執行緒安全的,多執行緒環境一定要注意使用安全。 這裡小黑哥推薦 **commons-lang3** 下的時間工具類`DateUtils/DateFormatUtils`,解決 Date 與字串轉化問題。 > ps:吐槽一下,你們工程中有沒有多個叫 `DateUtils` 類?小黑哥發現我們現有工程,多個模組有提供這個類,每個實現大同小異。 使用方法非常簡單: ```java // Date 轉化為字串 DateFormatUtils.format(new Date(),"yyyy-MM-dd HH:mm:ss"); // 字串 轉 Date DateUtils.parseDate("2020-05-07 22:00:00","yyyy-MM-dd HH:mm:ss"); ``` 除了格式轉化之外,`DateUtils` 還提供時間計算的相關功能。 ```java Date now = new Date(); // Date 加 1 天 Date addDays = DateUtils.addDays(now, 1); // Date 加 33 分鐘 Date addMinutes = DateUtils.addMinutes(now, 33); // Date 減去 233 秒 Date addSeconds = DateUtils.addSeconds(now, -233); // 判斷是否 Wie 同一天 boolean sameDay = DateUtils.isSameDay(addDays, addMinutes); // 過濾時分秒,若 now 為 2020-05-07 22:13:00 呼叫 truncate 方法以後 // 返回時間為 2020-05-07 00:00:00 Date truncate = DateUtils.truncate(now, Calendar.DATE); ``` ### JDK8 時間類 JDK8 之後,Java 將日期與時間分為 `LocalDate`,`LocalTime`,功能定義更加清晰,當然其也提供一個 `LocalDateTime`,包含日期與時間。這些類相對於 Date 類優點在於,這些類與 `String` 類一樣都是不變型別,不但執行緒安全,而且不能修改。 > ps:仔細對比 mysql 時間日期型別 `DATE`,`TIME`,`DATETIME`,有沒有感覺差不多 現在 mybatis 等 ORM 框架已經支援 `LocalDate` 與 JDBC 時間型別轉化,所以大家可以直接將時間欄位實際型別定義為 JDK8 時間型別,然後再進行相關轉化。 如果依然使用的是 Date 型別,如果需要使用新的時間型別,我們需要進行相關轉化。兩者之間進行轉化, 稍微複雜一點,我們需要顯示指定當前時區。 ```java Date now = new Date(); // Date-----> LocalDateTime 這裡指定使用當前系統預設時區 LocalDateTime localDateTime = now.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(); // LocalDateTime------> Date 這裡指定使用當前系統預設時區 Date date = Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant()); ``` 接下來我們使用 `LocalDateTime` 進行字串格式化。 ```java // 按照 yyyy-MM-dd HH:mm:ss 轉化時間 LocalDateTime dateTime = LocalDateTime.parse("2020-05-07 22:34:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); // 將 LocalDateTime 格式化字串 String format = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(dateTime); ``` 另外我們使用 `LocalDateTime` 獲取當前時間年份,月份特別簡單: ```java LocalDateTime now = LocalDateTime.now(); // 年 int year = now.getYear(); // 月 int month = now.getMonthValue(); // 日 int day = now.getDayOfMonth(); ``` 最後我們還可以使用 `LocalDateTime` 進行日期加減,獲取下一天的時間: ```java LocalDateTime now = LocalDateTime.now(); // 當前時間加一天 LocalDateTime plusDays = now.plusDays(1l); // 當前時間減一個小時 LocalDateTime minusHours = now.minusHours(1l); // 還有很多其他方法 ``` 總之 JDK8 提供的時間類非常好用,還沒用過小夥伴,可以嘗試下。 ## 集合/陣列相關 集合與陣列我們日常也需要經常使用,也需要對其進行判空: ```java if (null == list || list.isEmpty()) { } ``` > ps: 陣列、Map 集合與其類似 上面程式碼如字串判空一樣寫起來都非常簡單,但是也比較容易寫出會丟擲空指標異常的程式碼。這裡我們可以使用 **commons-collections** 提供工具類。 pom 資訊: ```xml org.apache.commons
commons-collections4 4.4
``` > ps: 還有一個低版本的 ,artifactId 為 commons-collections 我們可以使用 `CollectionUtils/MapUtils`進行判空判斷。 ```java // List/Set 集合判空 if(CollectionUtils.isEmpty(list)){ } // Map 等集合進行判空 if (MapUtils.isEmpty(map)) { } ``` 至於陣列判空判斷需要使用 `commons-lang` 下的 `ArrayUtils`進行判斷: ```java // 陣列判空 if (ArrayUtils.isEmpty(array)) { } ``` 除此之外還有一些列的對於集合增強方法,比如快速將陣列加入到現有集合中: ```java List listA = new ArrayList<>(); listA.add("1"); listA.add("2"); listA.add("3"); String[] arrays = new String[]{"a", "b", "c"}; CollectionUtils.addAll(listA, arrays); ``` 其他方法感興趣同學可以再自行研究下,另外 Guava 中也有提供對於集合的操作增強類 `Lists/Maps`,這個可以看下小黑哥之前寫的:[老司機小黑哥帶你玩轉 Guava 集合類](https://mp.weixin.qq.com/s/ZWGMplVYklt_jmPMsSrSEQ)。 ## I/O 相關 JDK 有提供一系列的類可以讀取檔案等,不過小黑哥覺得那些類有些晦澀難懂,實現一個小功能可能還要寫好多程式碼,而且還不一定能寫對。 小黑哥推薦一下 Apache 提供的 `commons-io` 庫,增強 I/O 操作,簡化操作難度。pom 資訊: ```java commons-io
commons-io 2.6
``` ### FileUtils-檔案操作工具類 檔案操作工具類提供一系列方法,可以讓我們快速讀取寫入檔案。 快速實現檔案/資料夾拷貝操作 ,`FileUtils.copyDirectory/FileUtils.copyFile` ```java // 拷貝檔案 File fileA = new File("E:\\test\\test.txt"); File fileB = new File("E:\\test1\\test.txt"); FileUtils.copyFile(fileA,fileB); ``` 使用 `FileUtils.listFiles` 獲取指定資料夾上所有檔案 ```java // 按照指定檔案字尾如java,txt等去查詢指定資料夾的檔案 File directory = new File("E:\\test"); FileUtils.listFiles(directory, new String[]{"txt"}, false); ``` 使用 `FileUtils.readLines` 讀取該檔案所有行。 ```java // 讀取指定檔案所有行 不需要使用 while 迴圈讀取流了 List lines = FileUtils.readLines(fileA) ``` 有讀就存在寫,可以使用 `FileUtils.writeLines`,直接將集合中資料,一行行寫入文字。 ```java // 可以一行行寫入文字 List lines = new ArrayList<>(); ..... FileUtils.writeLines(lines) ``` ### IOUtils-I/O 操作相關工具類 `FileUtils` 主要針對相關檔案操作,`IOUtils` 更加針對底層 I/O,可以快速讀取 `InputStream`。實際上 `FileUtils` 底層操作依賴就是 `IOUtils`。 `IOUtils`可以適用於一個比較試用的場景,比如支付場景下,HTTP 非同步通知場景。如果我們使用 JDK 原生方法寫: > 從 Servlet 獲取非同步通知內容 ```java byte[] b = null; ByteArrayOutputStream baos = null; String respMsg = null; try { byte[] buffer = new byte[1024]; baos = new ByteArrayOutputStream(); // 獲取輸入流 InputStream in = request.getInputStream(); for (int len = 0; (len = in.read(buffer)) > 0; ) { baos.write(buffer, 0, len); } b = baos.toByteArray(); baos.close(); // 位元組陣列轉化成字串 String reqMessage = new String(b, "utf-8"); } catch (IOException e) { } finally { if (baos != null) { try { baos.close(); } catch (IOException e) { } } } ``` 上面程式碼說起來還是挺複雜的。不過我們使用 `IOUtils`,一個方法就可以簡單搞定: ```java // 將輸入流資訊全部輸出到位元組陣列中 byte[] b = IOUtils.toByteArray(request.getInputStream()); // 將輸入流資訊轉化為字串 String resMsg = IOUtils.toString(request.getInputStream()); ``` > ps: InputStream 不能被重複讀取 ## 計時 程式設計中有時需要統計程式碼的的執行耗時,當然執行程式碼非常簡單,結束時間與開始時間相減即可。 ```java long start = System.currentTimeMillis(); //獲取開始時間 //其他程式碼 //... long end = System.currentTimeMillis(); //獲取結束時間 System.out.println("程式執行時間: " + (end - start) + "ms"); ``` 雖然程式碼很簡單,但是非常不靈活,預設情況我們只能獲取 ms 單位,如果需要轉換為秒,分鐘,就需要另外再計算。 這裡我們介紹 Guava `Stopwatch` 計時工具類,藉助他統計程式執行時間,使用方式非常靈活。 > commons-lang3 與 Spring-core 也有這個工具類,使用方式大同小異,大家根據情況選擇。 ```java // 建立之後立刻計時,若想主動開始計時 Stopwatch stopwatch = Stopwatch.createStarted(); // 建立計時器,但是需要主動呼叫 start 方法開始計時 // Stopwatch stopwatch = Stopwatch.createUnstarted(); // stopWatch.start(); // 模擬其他程式碼耗時 TimeUnit.SECONDS.sleep(2l); // 當前已經消耗的時間 System.out.println(stopwatch.elapsed(TimeUnit.SECONDS));; TimeUnit.SECONDS.sleep(2l); // 停止計時 未開始的計時器呼叫 stop 將會拋錯 IllegalStateException stopwatch.stop(); // 再次統計總耗時 System.out.println(stopwatch.elapsed(TimeUnit.SECONDS));; // 重新開始,將會在原來時間基礎計算,若想重新從 0開始計算,需要呼叫 stopwatch.reset() stopwatch.start(); TimeUnit.SECONDS.sleep(2l); System.out.println(stopwatch.elapsed(TimeUnit.SECONDS)); ``` 輸出結果為: ```log 2 4 6 ``` ## 總結 今天小黑哥拋磚引玉,介紹了字串、日期、陣列/集合、I/O、計時等工具類,簡化日常業務程式碼。大家看完可以嘗試一下,不得不說,這些工具類真香! ![](https://img2020.cnblogs.com/other/1419561/202005/1419561-20200512095751961-55871503.png) > 歡迎關注我的公眾號:程式通事,獲得日常乾貨推送。如果您對我的專題內容感興趣,也可以關注我的部落格:[studyidea.cn](https://studyidea.cn)