java常用工具庫使用
字符串相關工具類
Java 中 String 應該是日常用的最多一個類吧,平常我們很多代碼需要圍繞 String ,做一些處理。
JDK 提供 String API 雖然比較多,但是功能比較基礎,通常我們需要結合 String 多個方法才能完成一個業務功能。 下面介紹一下 Apache 提供的一個工具類 StringUtils. Maven Pom 信息如下:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.10</version>
</dependency>
commons-lang 有兩個版本,一個是 commons-lang3 ,一個是 commons-lang 。 commons-lang 是老版本,已經很久沒有維護了。 commons-lang3 是一直在維護的版本,推薦直接使用這個版本。 注意:如果你系統已經有 commons-lang,注意如果直接替換成 commons-lang3,將會編譯錯誤。commons-lang3 中相關類與 commons-lang 一樣,但是包名不一樣。
判斷字符串是否為空
判斷字符串是否為空,想必每個人應該都寫過吧:
if (null == str || str.isEmpty()) { }
雖然這段代碼非常簡單,但是說實話,以前還是在這里犯過空指針的異常的。
使用 StringUtils ,上面代碼可以替換下面這樣:
if (StringUtils.isEmpty(str)) { }
StringUtils 內部還有一個方法 isBlank,也是用來判斷字符串是否為空,兩個方法比較相近,比較搞混,主要區別如下:
// 如果字符串都是空格的話, StringUtils.isBlank(" ") = true; StringUtils.isEmpty(" ") = false;
字符串固定長度
這個通常用於字符串需要固定長度的場景,比如需要固定長度字符串作為流水號,若流水號長度不足,,左邊補 0 。 這里當然可以使用 String#format 方法,不過阿粉覺得比較麻煩,這里可以這樣使用:
// 字符串固定長度 8位,若不足,往左補 0 StringUtils.leftPad("test", 8, "0");
另外還有一個 StringUtils#rightPad,這個方法與上面方法正好相反。
字符串關鍵字替換
StringUtils 提供一些列的方法,可以替換某些關鍵字:
// 默認替換所有關鍵字 StringUtils.replace("aba", "a", "z") = "zbz"; // 替換關鍵字,僅替換一次 StringUtils.replaceOnce("aba", "a", "z") = "zba"; // 使用正則表達式替換 StringUtils.replacePattern("ABCabc123", "[^A-Z0-9]+", "") = "ABC123"; ....
字符串拼接
字符串拼接是個常見的需求,簡單辦法使用 StringBuilder 循環遍歷拼接:
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。
這里我們可以直接使用以下方法獲取拼接之后字符串:
StringUtils.join(["a", "b", "c"], ",") = "a,b,c"
StringUtils 只能傳入數組拼接字符串,不過我比較喜歡集合拼接,所以再推薦下 Guava 的 Joiner。
實例代碼如下:
String[] array = new String[]{"test", "1234", "5678"}; List<String> 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 也有拆分字符串的方法。
StringUtils.split("a..b.c", '.') = ["a", "b", "c"] StringUtils.splitByWholeSeparatorPreserveAllTokens("a..b.c", ".")= ["a","", "b", "c"]
ps:注意以上兩個方法區別。
StringUtils 拆分之后得到是一個數組,我們可以使用 Guava 的
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。
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 類?阿粉發現我們現有工程,多個模塊有提供這個類,每個實現大同小異。
使用方法非常簡單:
// 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 還提供時間計算的相關功能。
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 類型,如果需要使用新的時間類型,我們需要進行相關轉化。兩者之間進行轉化, 稍微復雜一點,我們需要顯示指定當前時區。
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 進行字符串格式化。
// 按照 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 獲取當前時間年份,月份特別簡單:
LocalDateTime now = LocalDateTime.now(); // 年 int year = now.getYear(); // 月 int month = now.getMonthValue(); // 日 int day = now.getDayOfMonth();
最后我們還可以使用 LocalDateTime 進行日期加減,獲取下一天的時間:
LocalDateTime now = LocalDateTime.now(); // 當前時間加一天 LocalDateTime plusDays = now.plusDays(1l); // 當前時間減一個小時 LocalDateTime minusHours = now.minusHours(1l); // 還有很多其他方法
總之 JDK8 提供的時間類非常好用,還沒用過小伙伴,可以嘗試下。
集合/數組相關
集合與數組我們日常也需要經常使用,也需要對其進行判空:
if (null == list || list.isEmpty()) { }
ps: 數組、Map 集合與其類似
上面代碼如字符串判空一樣寫起來都非常簡單,但是也比較容易寫出會拋出空指針異常的代碼。這里我們可以使用 commons-collections 提供工具類。
pom 信息:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.4</vesion>
</dependency>
ps: 還有一個低版本的 ,artifactId 為 commons-collections
我們可以使用 CollectionUtils/MapUtils進行判空判斷。
// List/Set 集合判空 if(CollectionUtils.isEmpty(list)){ } // Map 等集合進行判空 if (MapUtils.isEmpty(map)) { }
至於數組判空判斷需要使用 commons-lang 下的 ArrayUtils進行判斷:
// 數組判空 if (ArrayUtils.isEmpty(array)) { }
除此之外還有一些列的對於集合增強方法,比如快速將數組加入到現有集合中:
List<String> listA = new ArrayList<>(); listA.add("1"); listA.add("2"); listA.add("3"); String[] arrays = new String[]{"a", "b", "c"}; CollectionUtils.addAll(listA, arrays);
I/O 相關
JDK 有提供一系列的類可以讀取文件等,不過阿粉覺得那些類有些晦澀難懂,實現一個小功能可能還要寫好多代碼,而且還不一定能寫對。 阿粉推薦一下 Apache 提供的 commons-io 庫,增強 I/O 操作,簡化操作難度。pom 信息:
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
FileUtils-文件操作工具類
文件操作工具類提供一系列方法,可以讓我們快速讀取寫入文件。 快速實現文件/文件夾拷貝操作 ,FileUtils.copyDirectory/FileUtils.copyFile
// 拷貝文件 File fileA = new File("E:\\test\\test.txt"); File fileB = new File("E:\\test1\\test.txt"); FileUtils.copyFile(fileA,fileB);
使用 FileUtils.listFiles 獲取指定文件夾上所有文件
// 按照指定文件后綴如java,txt等去查找指定文件夾的文件 File directory = new File("E:\\test"); FileUtils.listFiles(directory, new String[]{"txt"}, false);
使用 FileUtils.readLines 讀取該文件所有行。
// 讀取指定文件所有行 不需要使用 while 循環讀取流了 List<String> lines = FileUtils.readLines(fileA)
有讀就存在寫,可以使用 FileUtils.writeLines,直接將集合中數據,一行行寫入文本。
// 可以一行行寫入文本 List<String> lines = new ArrayList<>(); ..... FileUtils.writeLines(lines)
IOUtils-I/O 操作相關工具類
FileUtils 主要針對相關文件操作,IOUtils 更加針對底層 I/O,可以快速讀取 InputStream。實際上 FileUtils 底層操作依賴就是 IOUtils。 IOUtils可以適用於一個比較試用的場景,比如支付場景下,HTTP 異步通知場景。如果我們使用 JDK 原生方法寫:
從 Servlet 獲取異步通知內容
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,一個方法就可以簡單搞定:
// 將輸入流信息全部輸出到字節數組中 byte[] b = IOUtils.toByteArray(request.getInputStream()); // 將輸入流信息轉化為字符串 String resMsg = IOUtils.toString(request.getInputStream());
ps: InputStream 不能被重復讀取
計時
編程中有時需要統計代碼的的執行耗時,當然執行代碼非常簡單,結束時間與開始時間相減即可。
long start = System.currentTimeMillis(); //獲取開始時間 //其他代碼 //... long end = System.currentTimeMillis(); //獲取結束時間 System.out.println("程序運行時間: " + (end - start) + "ms");
雖然代碼很簡單,但是非常不靈活,默認情況我們只能獲取 ms 單位,如果需要轉換為秒,分鍾,就需要另外再計算。
這里我們介紹 Guava Stopwatch 計時工具類,借助他統計程序執行時間,使用方式非常靈活。
commons-lang3 與 Spring-core 也有這個工具類,使用方式大同小異,大家根據情況選擇。
// 創建之后立刻計時,若想主動開始計時 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));
輸出結果為:
2
4
6