还在重复造轮子?duck不必,hutool一键搞定👍

「这是我参与11月更文挑战的第2天,活动详情查看:2021最后一次更文挑战」。PS:已经更文多少天,N就写几。一定要写对文案,否则文章不计入在内。

点赞再看,养成习惯。👏👏

一、前言

Hello 大家好,我是l拉不拉米,今天给大家分享一个国产开源的顶级项目,Java开发的利器👉【Hutool】。

本文已收录到 Github-java3c ,里面有我的系列文章,欢迎大家Star。

二、Hutool简介

Hutool是一个小而全的Java工具类库,通过静态方法封装,降低相关API的学习成本,提高工作效率,使Java拥有函数式语言般的优雅,让Java语言也可以“甜甜的”。

Hutool中的工具方法来自每个用户的精雕细琢,它涵盖了Java开发底层代码中的方方面面,它既是大型项目开发中解决小问题的利器,也是小型项目中的效率担当;

  • Web开发
  • 与其它框架无耦合
  • 高度可替换

Hutool由上百个工具类组成,涵盖了日常开发中的方方面面,可谓工具类集大成者。

image.png

三、Hutool设计哲学

Hutool的设计思想是尽量减少重复的定义,让项目中的util这个package尽量少,总的来说有如下的几个思想:

  • 方法优先于对象
  • 自动识别优于用户定义
  • 便捷性与灵活性并存
  • 适配与兼容
  • 可选依赖原则
  • 无侵入原则

开发者不再需要重复造轮子,或引入各种依赖包,Hutool帮您 All In One。

四、包含组件

一个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和Word的封装
hutool-socket 基于Java的NIO和AIO的Socket封装
hutool-jwt JSON Web Token (JWT)封装实现

可以根据需求对每个模块单独引入,也可以通过引入hutool-all方式引入所有模块。

五、安装

🍊Maven

在项目的pom.xml的dependencies中加入以下内容:

<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.7.15</version>
</dependency>Copy to clipboardErrorCopied
复制代码

🍐Gradle

implementation 'cn.hutool:hutool-all:5.7.15'
复制代码

📥下载jar

点击以下链接,下载hutool-all-X.X.X.jar即可:

🔔️注意 Hutool 5.x支持JDK8+,对Android平台没有测试,不能保证所有工具类或工具方法可用。 如果你的项目使用JDK7,请使用Hutool 4.x版本(不再更新)

🚽编译安装

访问Hutool的Gitee主页:gitee.com/dromara/hut… 下载整个项目源码(v5-master或v5-dev分支都可)然后进入Hutool项目目录执行:

./hutool.sh installCopy to clipboardErrorCopied
复制代码

然后就可以使用Maven引入了。

六、常用轮子

类型转换工具类-Convert

Convert类可以说是一个工具方法类,里面封装了针对Java常见类型的转换,用于简化类型转换。

//转换为字符串
int a = 1;
//aStr为"1"
String aStr = Convert.toStr(a);

//转换为指定类型数组
String[] b = {"1", "2", "3", "4"}; 
Integer[] bArr = Convert.toIntArray(b);

//转换为日期对象
String dateStr = "2017-05-06"; 
Date date = Convert.toDate(dateStr); 

//转换为列表 
String[] strArr = {"a", "b", "c", "d"}; 
List<String> strList = Convert.toList(String.class, strArr);
复制代码

日期时间工具-DateUtil

主要提供日期和字符串之间的转换,以及提供对日期的定位(一个月前等等)。

//当前时间 
Date date = DateUtil.date(); 

//Calendar转Date
Date date = DateUtil.date(Calendar.getInstance()); 

//时间戳转Date
Date date = DateUtil.date(System.currentTimeMillis()); 

//自动识别格式转换 
String dateStr = "2017-03-01"; 
Date date = DateUtil.parse(dateStr); 

//自定义格式化转换 
Date date = DateUtil.parse(dateStr, "yyyy-MM-dd"); 

//格式化输出日期 
String format = DateUtil.format(date, "yyyy-MM-dd"); 

//获得年的部分 
int year = DateUtil.year(date); 

//获得月份,从0开始计数 
int month = DateUtil.month(date); 

//获取某天的开始、结束时间 
Date beginOfDay = DateUtil.beginOfDay(date); 
Date endOfDay = DateUtil.endOfDay(date); 

//计算偏移后的日期时间 
Date newDate = DateUtil.offset(date, DateField.DAY_OF_MONTH, 2); 

//计算日期时间之间的偏移量 
long betweenDay = DateUtil.between(date, newDate, DateUnit.DAY);
复制代码

文件工具类-FileUtil

在IO操作中,文件的操作相对来说是比较复杂的,但也是使用频率最高的部分,我们几乎所有的项目中几乎都躺着一个叫做FileUtil或者FileUtils的工具类,我想Hutool应该将这个工具类纳入其中,解决用来解决大部分的文件操作问题。

总体来说,FileUtil类包含以下几类操作工具:

  1. 文件操作:包括文件目录的新建、删除、复制、移动、改名等
  2. 文件判断:判断文件或目录是否非空,是否为目录,是否为文件等等。
  3. 绝对路径:针对ClassPath中的文件转换为绝对路径文件。
  4. 文件名:主文件名,扩展名的获取
  5. 读操作:包括类似IoUtil中的getReader、readXXX操作
  6. 写操作:包括getWriter和writeXXX操作
//新建文件夹
FileUtil.mkdir("D:\");

//新建文件
FileUtil.file("D:\", "F:\");

//新建临时文件
FileUtil.createTempFile(FileUtil.file("D:\"));

//是否非空,是否为目录,是否为文件
FileUtil.isEmpty(FileUtil.file("D:\"));
FileUtil.isDirectory("D:\");
FileUtil.isFile("D:\");
复制代码

字符串工具-StrUtil

这个工具的用处类似于Apache Commons Lang中的StringUtil,之所以使用StrUtil而不是使用StringUtil是因为前者更短,而且Str这个简写我想已经深入人心了,大家都知道是字符串的意思。常用的方法例如isBlankisNotBlankisEmptyisNotEmpty这些我就不做介绍了,判断字符串是否为空,下面我说几个比较好用的功能。

//判断是否为空字符串 String str = "test"; 
StrUtil.isEmpty(str); 
StrUtil.isNotEmpty(str); 

//去除字符串的前后缀 
StrUtil.removeSuffix("a.jpg", ".jpg"); 
StrUtil.removePrefix("a.jpg", "a."); 

//格式化字符串 
String template = "这只是个占位符:{}"; 
String str2 = StrUtil.format(template, "我是占位符"); 
复制代码

反射工具-ReflectUtil

Java的反射机制,可以让语言变得更加灵活,对对象的操作也更加“动态”,因此在某些情况下,反射可以做到事半功倍的效果。Hutool针对Java的反射机制做了工具化封装,封装包括:

  1. 获取构造方法
  2. 获取字段
  3. 获取字段值
  4. 获取方法
  5. 执行方法(对象方法和静态方法)
//获取某个类的所有方法 
Method[] methods = ReflectUtil.getMethods(PmsBrand.class); 

//获取某个类的指定方法 
Method method = ReflectUtil.getMethod(PmsBrand.class, "getId"); 

//使用反射来创建对象 
PmsBrand pmsBrand = ReflectUtil.newInstance(PmsBrand.class); 

//反射执行对象的方法 
ReflectUtil.invoke(pmsBrand, "setId", 1);
复制代码

数字工具-NumberUtil

数字工具针对数学运算做工具性封装

double n1 = 1.234; 
double n2 = 1.234; double result; 

//对float、double、BigDecimal做加减乘除操作 
result = NumberUtil.add(n1, n2); 
result = NumberUtil.sub(n1, n2); 
result = NumberUtil.mul(n1, n2); 
result = NumberUtil.div(n1, n2); 

//保留两位小数 
BigDecimal roundNum = NumberUtil.round(n1, 2); 
String n3 = "1.234"; 

//判断是否为数字、整数、浮点数 
NumberUtil.isNumber(n3); 
NumberUtil.isInteger(n3); 
NumberUtil.isDouble(n3);
复制代码

数组工具-ArrayUtil

数组工具类主要针对原始类型数组和泛型数组相关方案进行封装。

//判空
int[] a = {}; 
int[] b = null;
ArrayUtil.isEmpty(a); 
ArrayUtil.isEmpty(b);

//新建泛型数组
String[] newArray = ArrayUtil.newArray(String.class, 3);
//泛型数组合并
int[] result = ArrayUtil.addAll(a, b);

//过滤
Integer[] a = {1,2,3,4,5,6}; 
// [2,4,6] 
Integer[] filter = ArrayUtil.filter(a, (Editor<Integer>) t -> (t % 2 == 0) ? t : null);

//是否包含
boolean flag = ArrayUtil.contains(a, 1);
//使用间隔符将一个数组转为字符串
String result = ArrayUtil.join(new int[]{1,2,3,4}, "-");
复制代码

信息脱敏工具-DesensitizedUtil

在数据处理或清洗中,可能涉及到很多隐私信息的脱敏工作,因此Hutool针对常用的信息封装了一些脱敏方法。

现阶段支持的脱敏数据类型包括:

  1. 用户id
  2. 中文姓名
  3. 身份证号
  4. 座机号
  5. 手机号
  6. 地址
  7. 电子邮件
  8. 密码
  9. 中国大陆车牌,包含普通车辆、新能源车辆
  10. 银行卡

整体来说,所谓脱敏就是隐藏掉信息中的一部分关键信息,用*代替,自定义隐藏可以使用StrUtil.hide方法完成。

// 身份证脱敏 5***************1X 
DesensitizedUtil.idCardNum("51343620000320711X", 1, 2);

// 手机号脱敏 180****1999 
DesensitizedUtil.mobilePhone("18049531999");

// 密码脱敏 ********** 
DesensitizedUtil.password("1234567890");
复制代码

身份证工具-IdcardUtil

IdcardUtil现在支持大陆15位、18位身份证,港澳台10位身份证。

工具中主要的方法包括:

  1. isValidCard 验证身份证是否合法
  2. convert15To18 身份证15位转18位
  3. getBirthByIdCard 获取生日
  4. getAgeByIdCard 获取年龄
  5. getYearByIdCard 获取生日年
  6. getMonthByIdCard 获取生日月
  7. getDayByIdCard 获取生日天
  8. getGenderByIdCard 获取性别
  9. getProvinceByIdCard 获取省份
String ID_18 = "321083197812162119"; 
String ID_15 = "150102880730303"; 

//是否有效 boolean valid = IdcardUtil.isValidCard(ID_18); 
boolean valid15 = IdcardUtil.isValidCard(ID_15); 

//转换 
String convert15To18 = IdcardUtil.convert15To18(ID_15); 
Assert.assertEquals(convert15To18, "150102198807303035"); 

//年龄 
DateTime date = DateUtil.parse("2017-04-10");
int age = IdcardUtil.getAgeByIdCard(ID_18, date); 
Assert.assertEquals(age, 38); 
int age2 = IdcardUtil.getAgeByIdCard(ID_15, date); 
Assert.assertEquals(age2, 28); 

//生日 
String birth = IdcardUtil.getBirthByIdCard(ID_18); 
Assert.assertEquals(birth, "19781216"); 
String birth2 = IdcardUtil.getBirthByIdCard(ID_15); 
Assert.assertEquals(birth2, "19880730");

//省份 
String province = IdcardUtil.getProvinceByIdCard(ID_18); 
Assert.assertEquals(province, "江苏"); 
String province2 = IdcardUtil.getProvinceByIdCard(ID_15); 
Assert.assertEquals(province2, "内蒙古");
复制代码

Bean工具-BeanUtil

// Bean转Map
SubPerson person = new SubPerson(); 
person.setAge(14); 
person.setOpenid("11213232"); 
person.setName("测试A11"); 
person.setSubName("sub名字"); 
Map<String, Object> map = BeanUtil.beanToMap(person);

//Map转Bean 
SubPerson person = BeanUtil.mapToBean(map, SubPerson.class, false); 

//Bean属性拷贝 
SubPerson copyPerson = new SubPerson(); 
BeanUtil.copyProperties(person, copyPerson); 
复制代码

集合工具-CollUtil

这个工具主要增加了对数组、集合类的操作。

//数组转换为列表 
String[] array = new String[]{"a", "b", "c", "d", "e"}; 
List<String> list = CollUtil.newArrayList(array); 

//join:数组转字符串时添加连接符号 
String joinStr = CollUtil.join(list, ","); 

//将以连接符号分隔的字符串再转换为列表 
List<String> splitList = StrUtil.split(joinStr, ','); 

//创建新的Map、Set、List 
HashMap<Object, Object> newMap = CollUtil.newHashMap(); 
HashSet<Object> newHashSet = CollUtil.newHashSet(); 
ArrayList<Object> newList = CollUtil.newArrayList(); 

//判断列表是否为空 
CollUtil.isEmpty(list);
复制代码

Map工具-MapUtil

MapUtil是针对Map的一一列工具方法的封装,包括getXXX的快捷值转换方法。

  • isEmptyisNotEmpty 判断Map为空和非空方法,空的定义为null或没有值
  • newHashMap 快速创建多种类型的HashMap实例
  • createMap 创建自定义的Map类型的Map
  • of 此方法将一个或多个键值对加入到一个新建的Map中,下面是栗子:
Map<Object, Object> colorMap = MapUtil.of(new String[][] { 
    {"RED", "#FF0000"}, 
    {"GREEN", "#00FF00"}, 
    {"BLUE", "#0000FF"} 
});
复制代码

网络工具-NetUtil

NetUtil 工具中主要的方法包括:

  1. longToIpv4 根据long值获取ip v4地址
  2. ipv4ToLong 根据ip地址计算出long型的数据
  3. isUsableLocalPort 检测本地端口可用性
  4. isValidPort 是否为有效的端口
  5. isInnerIP 判定是否为内网IP
  6. localIpv4s 获得本机的IP地址列表
  7. toAbsoluteUrl 相对URL转换为绝对URL
  8. hideIpPart 隐藏掉IP地址的最后一部分为 * 代替
  9. buildInetSocketAddress 构建InetSocketAddress
  10. getIpByHost 通过域名得到IP
  11. isInner 指定IP的long是否在指定范围内
String ip= "127.0.0.1"; 
long iplong = 2130706433L; 

//根据long值获取ip v4地址 
String ip= NetUtil.longToIpv4(iplong); 

//根据ip地址计算出long型的数据 
long ip= NetUtil.ipv4ToLong(ip); 

//检测本地端口可用性 
boolean result= NetUtil.isUsableLocalPort(6379); 

//是否为有效的端口 
boolean result= NetUtil.isValidPort(6379); 

//隐藏掉IP地址 
String result =NetUtil.hideIpPart(ip);
复制代码

图片工具-ImgUtil

针对awt中图片处理进行封装,这些封装包括:缩放、裁剪、转为黑白、加水印等操作。

//缩放图片
ImgUtil.scale( 
    FileUtil.file("d:/face.jpg"), 
    FileUtil.file("d:/face_result.jpg"), 
    0.5f //缩放比例
);

//裁剪图片
ImgUtil.cut( 
    FileUtil.file("d:/face.jpg"), 
    FileUtil.file("d:/face_result.jpg"), 
    new Rectangle(200, 200, 100, 100) //裁剪的矩形区域 
);
//`slice` 按照行列剪裁切片(将图片分为20行和20列)
ImgUtil.slice(FileUtil.file("e:/test2.png"), FileUtil.file("e:/dest/"), 10, 10);

//转换图片格式
ImgUtil.convert(FileUtil.file("e:/test2.png"), FileUtil.file("e:/test2Convert.jpg"));

//转黑白
ImgUtil.gray(FileUtil.file("d:/logo.png"), FileUtil.file("d:/result.png"));

//添加文字水印
ImgUtil.pressText(
    FileUtil.file("e:/pic/face.jpg"), 
    FileUtil.file("e:/pic/test2_result.png"),
    "版权所有", Color.WHITE, //文字 
    new Font("黑体", Font.BOLD, 100), //字体 
    0, //x坐标修正值。 默认在中间,偏移量相对于中间偏移 
    0, //y坐标修正值。 默认在中间,偏移量相对于中间偏移 
    0.8f//透明度:alpha 必须是范围 [0.0, 1.0] 之内(包含边界值)的一个浮点数字 
);

//添加图片水印
ImgUtil.pressImage( 
    FileUtil.file("d:/picTest/1.jpg"), 
    FileUtil.file("d:/picTest/dest.jpg"), 
    ImgUtil.read(FileUtil.file("d:/picTest/1432613.jpg")), //水印图片 
    0, //x坐标修正值。 默认在中间,偏移量相对于中间偏移 
    0, //y坐标修正值。 默认在中间,偏移量相对于中间偏移 
    0.1f 
);

// 旋转180度
BufferedImage image = ImgUtil.rotate(ImageIO.read(FileUtil.file("e:/pic/366466.jpg")), 180); ImgUtil.write(image, FileUtil.file("e:/pic/result.png"));
复制代码

JSON工具-JSONUtil

//Json字符解析
String html = "{\"name\":\"Something must have been changed since you leave\"}"; 
JSONObject jsonObject = JSONUtil.parseObj(html); jsonObject.getStr("name");

//Json字符创建
SortedMap<Object, Object> sortedMap = new TreeMap<Object, Object>() { 
    private static final long serialVersionUID = 1L; 
    { 
        put("attributes", "a"); 
        put("b", "b"); 
        put("c", "c"); 
    }
}; 
JSONUtil.toJsonStr(sortedMap);

//XML字符转json
String s = "<sfzh>123</sfzh><sfz>456</sfz><name>aa</name><gender>1</gender>"; 
JSONObject json = JSONUtil.parseFromXml(s); 
json.get("sfzh"); 
json.get("name");

//json转Bean
String json = "{\"ADT\":[[{\"BookingCode\":[\"N\",\"N\"]}]]}"; 
Price price = JSONUtil.toBean(json, Price.class); 
price.getADT().get(0).get(0).getBookingCode().get(0);
复制代码

加密解密工具-SecureUtil

对称加密

  • SecureUtil.aes
  • SecureUtil.des

摘要算法

  • SecureUtil.md5
  • SecureUtil.sha1
  • SecureUtil.hmac
  • SecureUtil.hmacMd5
  • SecureUtil.hmacSha1

非对称加密

  • SecureUtil.rsa
  • SecureUtil.dsa

UUID

  • SecureUtil.simpleUUID 方法提供无“-”的UUID

密钥生成

  • SecureUtil.generateKey 针对对称加密生成密钥
  • SecureUtil.generateKeyPair 生成密钥对(用于非对称加密)
  • SecureUtil.generateSignature 生成签名(用于非对称加密)

Http客户端工具类-HttpUtil

HttpUtil是应对简单场景下Http请求的工具类封装,此工具封装了HttpRequest对象常用操作,可以保证在一个方法之内完成Http请求。

此模块基于JDK的HttpUrlConnection封装完成,完整支持https、代理和文件上传。

// 最简单的HTTP请求,可以自动通过header等信息判断编码,不区分HTTP和HTTPS 
String result1= HttpUtil.get("https://www.baidu.com");

// Post请求文件上传
HashMap<String, Object> paramMap = new HashMap<>(); 
//文件上传只需将参数中的键指定(默认file),值设为文件对象即可,对于使用者来说,文件上传与普通表单提交并无区别 
paramMap.put("file", FileUtil.file("D:\\face.jpg")); 
String result= HttpUtil.post("https://www.baidu.com", paramMap);

//文件下载
String fileUrl = "http://mirrors.sohu.com/centos/8.4.2105/isos/x86_64/CentOS-8.4.2105-x86_64-dvd1.iso"; 
//将文件下载后保存在E盘,返回结果为下载文件大小 
long size = HttpUtil.downloadFile(fileUrl, FileUtil.file("e:/")); 
System.out.println("Download size: " + size);
复制代码

Excel工具-ExcelUtil

//读取excel文件
ExcelReader reader = ExcelUtil.getReader("d:/aaa.xlsx"); 
List<List<Object>> readAll = reader.read();
List<Map<String,Object>> readAll = reader.readAll();
List<Person> all = reader.readAll(Person.class);

//写出到客户端
// 通过工具类创建writer,默认创建xls格式 
ExcelWriter writer = ExcelUtil.getWriter(); 
// 一次性写出内容,使用默认样式,强制输出标题 
writer.write(rows, true); 
//out为OutputStream,需要写出到的目标流 
//response为HttpServletResponse对象 
response.setContentType("application/vnd.ms-excel;charset=utf-8"); 
//test.xls是弹出下载对话框的文件名,不能为中文,中文请自行编码 
response.setHeader("Content-Disposition","attachment;filename=test.xls"); 
ServletOutputStream out=response.getOutputStream(); 
writer.flush(out, true); 
// 关闭writer,释放内存 
writer.close(); 
//此处记得关闭输出Servlet流 
IoUtil.close(out);
复制代码

图形验证码-CaptchaUtil

image.png

//定义图形验证码的长和宽 
LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(200, 100); 
//图形验证码写出,可以写出到文件,也可以写出到流 
lineCaptcha.write("d:/line.png"); 
//输出code 
Console.log(lineCaptcha.getCode()); 
//验证图形验证码的有效性,返回boolean值 
lineCaptcha.verify("1234"); 
//重新生成验证码 
lineCaptcha.createCode(); 
lineCaptcha.write("d:/line.png"); 
//新的验证码 
Console.log(lineCaptcha.getCode()); 
//验证图形验证码的有效性,返回boolean值 
lineCaptcha.verify("1234");
复制代码

七、项目地址