最近公司來了一批實習生,小黑哥負責帶一個。這位小師弟說實話,基本功很紮實,作事也很是靠譜,深得小黑哥真傳。java
不過最近給其 Review 代碼的時候,小黑哥發現小師弟有些代碼邏輯有些繁瑣,有些代碼小黑哥看來能夠用一些開源工具類實現,不須要本身重複實現。mysql
不過這也是正常的,小黑哥剛入行的時候寫的代碼也是這樣,這幾年慢慢接觸了一些開源工具類,逐漸積累。如今寫代碼纔會直接用工具類替換本身實現的這些繁瑣的邏輯。正則表達式
因而小黑哥給小師弟分享了幾個本身經常使用的開源工具類,小師弟學完直呼:『666』。sql
這裏小黑哥拋磚引玉,分享幾個經常使用的工具類,但願幫助到剛入行的同窗們。其餘編程老司機若是還有其餘好用的工具類,歡迎評論區分享。apache
下文主要分享這幾個方向的經常使用工具類:編程
Java 中 String 應該是平常用的最多一個類吧,日常咱們不少代碼須要圍繞 String ,作一些處理。數組
JDK 提供 String API 雖然比較多,可是功能比較基礎,一般咱們須要結合 String 多個方法才能完成一個業務功能。安全
下面介紹一下 Apache 提供的一個工具類 StringUtils.mybatis
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;
複製代碼
判斷字符串是否爲空,使用頻率很是高,這裏你們可使用 IDEA Prefix 的功能,輸入直接生成判空語句。
這個一般用於字符串須要固定長度的場景,好比須要固定長度字符串做爲流水號,若流水號長度不足,,左邊補 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。
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 以後,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);
複製代碼
其餘方法感興趣同窗能夠再自行研究下,另外 Guava 中也有提供對於集合的操做加強類 Lists/Maps
,這個能夠看下小黑哥以前寫的:老司機小黑哥帶你玩轉 Guava 集合類。
JDK 有提供一系列的類能夠讀取文件等,不太小黑哥以爲那些類有些晦澀難懂,實現一個小功能可能還要寫好多代碼,並且還不必定能寫對。
小黑哥推薦一下 Apache 提供的 commons-io
庫,加強 I/O 操做,簡化操做難度。pom 信息:
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
複製代碼
文件操做工具類提供一系列方法,可讓咱們快速讀取寫入文件。
快速實現文件/文件夾拷貝操做 ,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)
複製代碼
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
複製代碼
今天小黑哥拋磚引玉,介紹了字符串、日期、數組/集合、I/O、計時等工具類,簡化平常業務代碼。你們看完能夠嘗試一下,不得不說,這些工具類真香!
歡迎關注個人公衆號:程序通事,得到平常乾貨推送。若是您對個人專題內容感興趣,也能夠關注個人博客:studyidea.cn