簡介
Hutool是一個小而全的Java工具類庫,通過靜態方法封裝,降低相關API的學習成本,提高工作效率,使Java擁有函數式語言般的優雅,讓Java語言也可以“甜甜的”。
Hutool中的工具方法來自於每個用戶的精雕細琢,它涵蓋了Java開發底層代碼中的方方面面,它既是大型項目開發中解決小問題的利器,也是小型項目中的效率擔當;
Hutool是項目中“util”包友好的替代,它節省了開發人員對項目中公用類和公用工具方法的封裝時間,使開發專注於業務,同時可以最大限度的避免封裝不完善帶來的bug。
這是官方對它的介紹,簡單點說,它通過一些封裝,將原來略顯複雜的API進一步優化,使得你在使用的時候能夠更加方便快捷,當然語法也會比原來更加簡單易懂。
包含組件
一個Java基礎工具類,對文件、流、加密解密、轉碼、正則、線程、XML等JDK方法進行封裝,組成各種Util工具類,同時提供以下組件:
模塊 | 介紹 |
---|---|
hutool-aop | JDK動態代理封裝,提供非IOC下的切面支持 |
hutool-bloomFilter | 布隆過濾,提供一些Hash算法的布隆過濾 |
hutool-cache | 簡單緩存實現 |
hutool-core | 核心,包括Bean操作、日期、各種Util等 |
hutool-cron | 定時任務模塊,提供類Crontab表達式的定時任務 |
hutool-crypto | 加密解密模塊,提供對稱、非對稱和摘要算法封裝 |
hutool-db | JDBC封裝後的數據操作,基於ActiveRecord思想 |
hutool-dfa | 基於DFA模型的多關鍵字查找 |
hutool-extra | 擴展模塊,對第三方封裝(模板引擎、郵件、Servlet、二維碼、Emoji、FTP、分詞等) |
hutool-http | 基於HttpUrlConnection的Http客戶端封裝 |
hutool-log | 自動識別日誌實現的日誌門面 |
hutool-script | 腳本執行封裝,例如Javascript |
hutool-setting | 功能更強大的Setting配置文件和Properties封裝 |
hutool-system | 系統參數調用封裝(JVM信息等) |
hutool-json | JSON實現 |
hutool-captcha | 圖片驗證碼實現 |
hutool-poi | 針對POI中Excel的封裝 |
hutool-socket | 基於Java的NIO和AIO的Socket封裝 |
可以根據需求對每個模塊單獨引入,也可以通過引入hutool-all
方式引入所有模塊。
安裝
1、Maven項目
在項目的pom.xml的dependencies中加入以下內容:
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.0.7</version>
</dependency>
2、非Maven項目
下載地址: https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.0.7/
下載hutool-all-X.X.X.jar即可。
下面對某幾個組件的使用進行一個入門。
類型轉換
1、常用類型轉換
在傳統的類型轉換過程中,我們需要使用到包裝類的valueof()方法,例如:
String s = "1234";
int num = Integer.valueOf(s);
double d = Double.valueOf(s);
float f = Float.valueOf(s);
但很顯然實際情況並沒有這麼簡單,在企業級的開發項目中,從前端傳遞過來的參數各式各樣,類型繁多,我們如何知曉參數類型並作對應的轉換呢?一般會先將所有參數轉成String類型,如Web中的HttpServletRequest的getParamer()方法得到的數據類型就永遠是String。轉成String之後再將參數轉成對應的數據類型,此時還需要考慮轉換異常的問題,所以通常還需要在轉換代碼外面使用try-catch。
爲了使轉換過程變得輕鬆愉快,HuTool爲我們提供了類型轉換工具——Convert。
來看看使用Convert該如何進行轉換:
String s = "1234";
int num = Convert.toInt(s);
double d = Convert.toDouble(s);
float f = Convert.toFloat(s);
首先在代碼風格上,Convert作了一個統一,統一使用Convert類作爲工具類進行類型轉換,而無需使用每個類型對應的包裝類。
當然了,Convert類的作用可遠不止如此,比如:
String[] s = { "1", "2", "3", "4", "5" };
Integer[] intArray = Convert.toIntArray(s);
Double[] doubleArray = Convert.toDoubleArray(s);
Float[] floatArray = Convert.toFloatArray(s);
Character[] charArray = Convert.toCharArray(s);
將數組轉換爲任意類型的數組。
對於集合,Convert同樣支持轉換:
Object[] objs= {"hello","world","java",1};
List<?> list = Convert.toList(objs);
還有日期類型:
String s = "2019-12-07";
Date date = Convert.toDate(s);
關於日期的處理,HuTool爲我們提供了專門的工具類,這個我們放到後面說。
2、其它類型轉換
通過Convert的convert()方法也能夠實現上述的所有操作,不信我們可以試一試:
String s = "1234";
int num = Convert.convert(Integer.class, s);
double d = Convert.convert(Double.class, s);
float f = Convert.convert(Float.class, s);
關於其它類型大家可以自己試一試,總之,通過convert()方法可以將任意類型轉換爲指定類型,但這種方法終歸是有侷限的,試問一下,我們如何將一個數組轉換成List類型呢?
我們可以通過一個重載方法convert( TypeReference reference, Object value ),該方法需要一個TypeReference對象參數,我們就可以創建TypeReference對象並通過嵌套泛型來指定需要轉換的類型,比如:
Object[] objs = { "hello", "world", "java"};
List<String> list = Convert.convert(new TypeReference<List<String>>() {}, objs);
Convert還提供了全角與半角符號之間的轉換,比如:
//將全角符轉爲半角符
String s = "1,2,3,4,5";
String dbc = Convert.toDBC(s);
//將半角符轉爲全角符
String sbc = Convert.toSBC(dbc);
可以看看運行結果,更加直觀:
3、編碼轉換
在一些場景下,比如表單提交,會將參數進行一個簡單的加密,此時通常會使用16進制轉換,當然了,我們在準備16進制轉換的時候也不會自己去寫,都是去百度找一個現成的。不過,有了HuTool就不需要了,它爲我們提供了方法用於完成16進制的轉換。
String s = "你好世界";
//轉換爲16進制字符串
String hex = Convert.toHex(s, CharsetUtil.CHARSET_UTF_8);
//轉換爲普通字符串
String str = Convert.hexToStr(hex, CharsetUtil.CHARSET_UTF_8);
運行結果:
e4bda0e5a5bde4b896e7958c
你好世界
注意編碼對象要相同,這裏都使用UTF-8編碼,所以順逆的轉換過程都是成功的,如果編碼不同,在轉爲普通字符串的時候就會出現亂碼。
還有Unicode編碼和字符串的轉換:
String s = "你好世界";
//轉換爲Unicode編碼
String unicode = Convert.strToUnicode(s);
//轉換爲普通字符串
String str = Convert.unicodeToStr(unicode);
運行結果:
\u4f60\u597d\u4e16\u754c
你好世界
Convert類還提供了convertCharset ()用於將字符串轉換爲指定編碼的字符串,比如在處理表單數據時通常要處理亂碼問題,如下:
String s = "你好世界";
//將字符串先轉成亂碼
String str = Convert.convertCharset(s, CharsetUtil.UTF_8, CharsetUtil.ISO_8859_1);
//處理亂碼
String result = Convert.convertCharset(str, CharsetUtil.ISO_8859_1, CharsetUtil.UTF_8);
運行結果:
????????????
你好世界
還有金額大小寫轉換的功能:
double money = 2019.127;
String s = Convert.digitToChinese(money);
運行結果:
貳仟零壹拾玖元壹角叄分
4、自定義類型轉換
Convert類的功能是不是非常強大呢?我們繼續來看,對於數據類型轉換,肯定是做不到包含所有數據類型的,因爲Java面向對象的特性,但是HuTool提供了自定義類型轉換。
ConverterRegistry converterRegistry = ConverterRegistry.getInstance();
converterRegistry.putCustom(Person.class, CustomConverter.class);
String[] str = { "20", "張三" };
Person person = converterRegistry.convert(Person.class, str);
System.out.println(person);
運行結果:
Person [age=20, name=張三]
該轉換器將一個數組類型轉換爲了Person對象,它是如何實現的呢?(分三步)
- 自定義類實現Converter接口,並重寫convert()方法
- 註冊自定義的轉換器
- 實現轉換
先定義一個Person類:
public class Person {
private int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person [age=" + age + ", name=" + name + "]";
}
}
然後自定義類實現Converter接口,並重寫方法:
public class CustomConverter implements Converter<Person> {
@Override
public Person convert(Object value, Person person) throws IllegalArgumentException {
person = new Person();
String[] str = Convert.toStrArray(value);
person.setAge(Convert.toInt(str[0]));
person.setName(str[1]);
return person;
}
}
該方法將傳遞過來的數據封裝成Person對象並返回,實現類型轉換。
接着註冊自定義的轉換器:
ConverterRegistry converterRegistry = ConverterRegistry.getInstance();
converterRegistry.putCustom(Person.class, CustomConverter.class);
現在就可以使用我們的轉換器了,也就是剛剛的代碼:
String[] str = { "20", "張三" };
Person person = converterRegistry.convert(Person.class, str);
System.out.println(person);
需要轉換的數據是什麼樣式的,完全由你自定義的轉換器決定,非常靈活,可根據自己的需求隨意定製。
日期時間處理
對於日期時間的處理,Java提供了Date類和Calendar類,但就是因爲有了更多的選擇,使得日期時間轉換的操作變得混亂和複雜,爲此,HuTool提供了DateUtil工具。
1、Date、long、Calendar的相互轉換
使用DateUtil可以實現Date、long和Calendar之間的相互轉換,如下:
// Calendar轉爲Date
Date date = DateUtil.date(Calendar.getInstance());
// long轉爲Date
Date date2 = DateUtil.date(System.currentTimeMillis());
// Date轉爲Calendar
Calendar calendar = DateUtil.calendar(date);
// long轉爲Calendar
Calendar calendar2 = DateUtil.calendar(System.currentTimeMillis());
2、日期字符串轉換爲Date
String s = "2019-12-07";
DateTime dateTime = DateUtil.parse(s);
System.out.println(dateTime);
運行結果:
2019-12-07 00:00:00
該方法能夠將日期字符串轉換爲Date類型,它能夠自動識別以下格式的字符串:
- yyyy-MM-dd HH:mm:ss
- yyyy-MM-dd
- HH:mm:ss
- yyyy-MM-dd HH:mm
- yyyy-MM-dd HH:mm:ss.SSS
3、格式化日期
格式化日期很簡單,和Java的API沒什麼區別。
String s = "2019-12-07";
DateTime date = DateUtil.parse(s);
String dateStr = DateUtil.format(date, "yyyy/MM/dd");
System.out.println(dateStr);
String dateStr2 = DateUtil.formatDate(date);
System.out.println(dateStr2);
String dateStr3 = DateUtil.formatDateTime(date);
System.out.println(dateStr3);
String dateStr4 = DateUtil.formatTime(date);
System.out.println(dateStr4);
通過format()方法可以將日期字符串轉換爲指定的格式,不過,DateUtil提供了其它的一些方法來作爲常用的日期格式轉換,看運行結果即可:
2019/12/07
2019-12-07
2019-12-07 00:00:00
00:00:00
4、獲取年、月、日
對於年、月、日的獲取,DateUtil也提供了非常簡便的獲取方式:
// 獲取當前日期時間
DateTime date = DateUtil.date();
System.out.println(date);
// 獲取年
int year = DateUtil.year(date);
System.out.println(year);
// 獲取月 從0開始
int month = DateUtil.month(date);
System.out.println(month);
運行結果:
2019-12-07 21:45:42
2019
11
5、日期時間偏移量
對於日期時間的偏移,DateUtil同樣能夠很方便地實現,如下:
String s = "2019-12-06 21:46:00";
DateTime date = DateUtil.parse(s);
// 日期往後偏移一天
DateTime dateTime = DateUtil.offset(date, DateField.DAY_OF_MONTH, 1);
System.out.println(dateTime);
// 日期往後偏移兩天
DateTime dateTime2 = DateUtil.offsetDay(dateTime, 2);
System.out.println(dateTime2);
// 日期往後偏移一個小時
DateTime dateTime3 = DateUtil.offsetHour(date, 1);
System.out.println(dateTime3);
運行結果:
2019-12-07 21:46:00
2019-12-09 21:46:00
2019-12-06 22:46:00
關於日期時間的偏移,通過offset()方法即可實現,該方法的第二個參數可傳入偏移的單位,不過DateUtil還提供了一些比較常用的偏移方法,比如偏移天數、偏移小時。
對於與當前十分接近的日期時間,DateUtil也提供了一些較爲常用的方法,比如昨天、明天、上週、下週、上個月、下個月等:
DateTime yesrerday = DateUtil.yesterday();
System.out.println(yesrerday);
DateTime tomorrow = DateUtil.tomorrow();
System.out.println(tomorrow);
DateTime lastMonth = DateUtil.lastMonth();
System.out.println(lastMonth);
DateTime nextMonth = DateUtil.nextMonth();
System.out.println(nextMonth);
DateTime lastWeek = DateUtil.lastWeek();
System.out.println(lastWeek);
DateTime nextWeek = DateUtil.nextWeek();
System.out.println(nextWeek);
運行結果:
2019-12-06 22:02:29
2019-12-08 22:02:29
2019-11-07 22:02:29
2020-01-07 22:02:29
2019-11-30 22:02:29
2019-12-14 22:02:29
6、計算日期時間差
String s1 = "2019-12-06 22:15:00";
DateTime date1 = DateUtil.parse(s1);
String s2 = "2019-12-08 22:15:00";
DateTime date2 = DateUtil.parse(s2);
// 計算相差的天數
long day = DateUtil.between(date1, date2, DateUnit.DAY);
System.out.println(day);
// 計算相差的小時數
long hour = DateUtil.between(date1, date2, DateUnit.HOUR);
System.out.println(hour);
運行結果:
2
48
對於兩個日期時間的差值,通過between()方法能夠很輕鬆地得到,該方法的第三個參數是需要計算的差值的單位。
7、計時器
DateUtil類還封裝了計時器功能,用過傳統的Timer計時器的同學就會知道,Timer計時器略顯複雜,而DateUtil的封裝則恰到好處。
TimeInterval timer = DateUtil.timer();
// 延遲2秒
Thread.sleep(2000);
// 花費的時間,單位:毫秒
long interval = timer.interval();
System.out.println(interval);
// 花費的時間,單位:分
long intervalMinute = timer.intervalMinute();
System.out.println(intervalMinute);
運行結果:
2000
0
還有很多其它的方法,篇幅有限,就不一一例舉了。
8、其它
考慮到一些比較常見的場景,例如計算一個人的年齡,判斷給定年份是否爲閏年,DateUtil也給出了相應的解決辦法。
int age = DateUtil.ageOfNow("1999-08-08");
System.out.println(age);
boolean leapYear = DateUtil.isLeapYear(2019);
System.out.println(leapYear);
運行結果:
20
false
IO操作
IO操作是Java中比較重要的操作之一,爲此,Java提供了InputStream、OutputStream、Reader、Writer等接口,而實現類又非常的多,往往選擇一多,我們就不知道該如何選擇,而HuTool爲我們封裝了一系列的工具類。
FileUtil(文件操作工具類)
既然是IO流,那就離不開文件操作,HuTool爲我們提供了FileUtil工具類用來解決大部分的文件操作問題。
對於文件操作的方法,有Linux基礎的同學肯定非常熟悉,開源項目的作者努力將方法名與Linux保持一致,比如創建文件的方法不是createFile()而是touch()。
File[] files = FileUtil.ls("E:");
for (File file : files) {
System.out.println(file);
}
運行結果:
E:\$RECYCLE.BIN
E:\androidSdk
E:\androidstudio
E:\b2631f36a0808f7d3cab19543d645e63
E:\flutter
E:\Java
E:\MailMasterData
E:\QLDownload
E:\QQPCmg
E:\QQPCmgr
E:\qycache
E:\System Volume Information
E:\test.txt
E:\Tool
E:\迅雷下載
ls()方法會列出給定路徑下的所有目錄和文件。
FileUtil.touch("E:/test/hello.txt");
touch()方法用於創建文件,如果父目錄不存在也自動創建,比如這裏的hello.txt文件,倘若E盤下沒有test目錄,則會先創建test文件夾,再在test目錄下創建hello.txt文件。
其它方法也如上所示使用,就不一一演示了:
mkdir
創建目錄,會遞歸創建每層目錄del
刪除文件或目錄(遞歸刪除,不判斷是否爲空),這個方法相當於Linux的delete命令copy
拷貝文件或目錄
注意:對於del()方法,它會直接刪除目錄而不判斷其是否爲空,所以請謹慎使用。
IOUtil(IO工具類)
第二個便是IOUtil,該工具類主要針對輸入輸出流的一個封裝。
1、文件複製
針對文件複製操作,IOUtil能夠很輕鬆地完成。
BufferedInputStream inputStream = FileUtil.getInputStream("C:/Users/Administrator/Desktop/eclipseworkspace/HuToolDemo/src/com/wwj/test/TestDemo.java");
BufferedOutputStream outputStream = FileUtil.getOutputStream("E:/test.txt");
long copySize = IoUtil.copy(inputStream, outputStream, IoUtil.DEFAULT_BUFFER_SIZE);
這裏就複製了一下當前的java文件並將其保存至E盤下的test.txt文件中。
2、讀流寫流
讀流寫流也是IO操作中使用頻率非常高的操作,它跟傳統的方式沒有太大區別,只不過對調用者進行了統一。
BufferedInputStream inputStream = FileUtil.getInputStream("C:\\Users\\Administrator\\Desktop\\eclipseworkspace\\HuToolDemo\\src\\com\\wwj\\test\\TestDemo.Java");
BufferedOutputStream outputStream = FileUtil.getOutputStream("E:\\test.txt");
int len = -1;
byte[] buffer = new byte[1024];
while ((len = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, len);
}
IoUtil.close(inputStream);
IoUtil.close(outputStream);
下面列出關於讀寫流操作相關的方法:
readBytes
返回byte數組(讀取圖片等)readHex
讀取16進制字符串readObj
讀取序列化對象(反序列化)readLines
按行讀取
對於寫流操作,IoUtil提供了兩個write()的重載方法,當然也可以直接使用輸出流的write()方法,而事實上,IoUtil的write()方法也是這麼做的。
我們再來看看IoUtil如何讀寫圖片,對於圖片的讀寫操作,它提供了readBytes()方法,使用該方法讀寫圖片簡直不要太簡單:
BufferedInputStream inputStream = FileUtil.getInputStream("C:\\Users\\Administrator\\Desktop\\eclipseworkspace\\HuToolDemo\\默認圖表.png");
BufferedOutputStream outputStream = FileUtil.getOutputStream("E:\\test.png");
byte[] bytes = IoUtil.readBytes(inputStream);
outputStream.write(bytes);
IoUtil.close(inputStream);
IoUtil.close(outputStream);
這樣即可完成圖片的讀寫。
IoUtil還提供了一些其它方法用於簡化編程,比如:toStream()方法用於將某些對象轉換爲流對象;writeObjects()方法用於將可序列化對象序列化後寫入到流中。
3、釋放流資源
IO操作中的一個好習慣就是用完哪個流就關掉哪個流,而關閉操作會面臨兩個問題:
- 被關閉的對象爲空
- 對象關閉失敗
而IoUtil提供的close()方法則很好地解決了這些問題,我們只需將要關閉的流傳入close()方法即可。
FileTypeUtil(文件類型判斷工具類)
FileTypeUtil是一個判斷文件類型的工具類,它並不是通過文件的擴展名來確定文件類型,而是通過讀取文件的首部幾個二進制位來判斷。
File file = FileUtil.file("C:\\Users\\Administrator\\Desktop\\eclipseworkspace\\HuToolDemo\\默認圖表.png");
String type = FileTypeUtil.getType(file);
Console.log(type);
運行結果:
png
FileReader(文件讀取)
雖然FileUtil已經提供了關於文件讀寫的API,但是根據職責分離原則,HuTool還是爲我們提供了FileReader類專門讀取文件。
在JDK中同樣提供了FileReader類,但並不好用,HuTool正是對它進行的一個升級。
FileReader fileReader = new FileReader("test.properties");
String result = fileReader.readString();
System.out.println(result);
運行結果:
username=root
password=123456
該類還提供了一些常用的方法幫助文件讀取:
readBytes
readString
readLines
文件寫入(FileWriter)
有文件讀取,肯定就會有文件寫入,使用方法和FileReader是一樣的,這裏就不做代碼演示了。
寫入方式分爲追加和覆蓋兩種模式,追加的話可以用append()方法,覆蓋可以用write()方法;當然你也可以直接使用write()方法,並將寫入模式作爲第二個參數傳入。
資源訪問
在Java開發中,資源訪問是比較常見的操作,例如在操作數據庫、整合框架的時候,需要頻繁地訪問配置文件,通常我們會將配置文件放在類路徑下,方便訪問:
InputStream in = TestDemo.class.getResource("test.properties").openStream();
對於資源訪問這種頻繁而且麻煩的操作,HuTool對其進行了封裝。
ClassPathResource resource = new ClassPathResource("test.properties");
Properties properties = new Properties();
properties.load(resource.getStream());
Console.log(properties);
運行結果:
{password=123456, username=root}
當然,對於資源訪問的封裝遠不止如此,這個放到後面說。
最後
以上內容只是HuTool項目的冰山一角,我將在下篇相關文章中繼續介紹該項目下封裝的一些工具類。