官网
//官网 https://hutool.cn/ // 中文文档 https://www.hutool.cn/docs/#/ //API文档 https://apidoc.gitee.com/dromara/hutool/
注意点
注意 Hutool 5.x支持JDK8+,对Android平台没有测试,不能保证所有工具类或工具方法可用。 如果你的项目使用JDK7,请使用Hutool 4.x版本
功能简介
一个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-all
方式引入所有模块。
cn.hutool hutool-all 5.5.8
由来
在Java的世界中,Http客户端之前一直是Apache家的HttpClient占据主导,但是由于此包较为庞大,API又比较难用,因此并不使用很多场景。而新兴的OkHttp、Jodd-http固然好用,但是面对一些场景时,学习成本还是有一些的。很多时候,我们想追求轻量级的Http客户端,并且追求简单易用。而JDK自带的HttpUrlConnection可以满足大部分需求。Hutool针对此类做了一层封装,使Http请求变得无比简单。
介绍
Hutool-http针对JDK的HttpUrlConnection做一层封装,简化了HTTPS请求、文件上传、Cookie记忆等操作,使Http请求变得无比简单。
Hutool-http的核心集中在两个类:
HttpRequest HttpResponse 同时针对大部分情境,封装了HttpUtil工具类。
hutool-http优点
根据URL自动判断是请求HTTP还是HTTPS,不需要单独写多余的代码。
表单数据中有File对象时自动转为
multipart/form-data
表单,不必单做做操作。默认情况下Cookie自动记录,比如可以实现模拟登录,即第一次访问登录URL后后续请求就是登录状态。
自动识别304跳转并二次请求
自动识别页面编码,即根据header信息或者页面中的相关标签信息自动识别编码,最大可能避免乱码。
自动识别并解压Gzip格式返回内容
2.1 简单请求
==GET==
最简单的使用莫过于用HttpUtil工具类快速请求某个页面:
// 最简单的HTTP请求,可以自动通过header等信息判断编码,不区分HTTP和HTTPS String content = HttpUtil.get("http://kjfeng44.usa3v.vip/show.html"); System.out.println(content);//打印结果为 访问当前网页 鼠标右键->查看源代码 一样的信息 // 最简单的HTTP请求,可以自动通过header等信息判断编码,不区分HTTP和HTTPS String result1= HttpUtil.get("https://www.baidu.com"); // 当无法识别页面编码的时候,可以自定义请求页面的编码 String result2= HttpUtil.get("https://www.baidu.com", CharsetUtil.CHARSET_UTF_8); //可以单独传入http参数,这样参数会自动做URL编码,拼接在URL中 HashMapparamMap = new HashMap<>(); paramMap.put("city", "北京"); //类似 https://www.baidu.com?city='北京' String result3= HttpUtil.get("https://www.baidu.com", paramMap);
一行代码即可搞定,当然Post请求也很简单:
==Post==请求只需使用Map预先制定form表单项即可
//POST请求 HashMapparamMap = new HashMap<>(); paramMap.put("city", "北京"); String result1 = HttpUtil.post(url, paramMap);
2.2 文件上传下载
==上传文件 使用post请求==
HashMapparamMap = new HashMap<>(); //文件上传只需将参数中的键指定(默认file),值设为文件对象即可,对于使用者来说,文件上传与普通表单提交并无区别 paramMap.put("file", FileUtil.file("D:\\face.jpg")); String result= HttpUtil.post("https://www.baidu.com", paramMap);
==下载文件==
因为Hutool-http机制问题,请求页面返回结果是一次性解析为byte[]的,如果请求URL返回结果太大(比如文件下载),那内存会爆掉,因此针对文件下载HttpUtil单独做了封装。文件下载在面对大文件时采用流的方式读写,内存中只是保留一定量的缓存,然后分块写入硬盘,因此大文件情况下不会对内存有压力。
String fileUrl = "http://android-screenimgs.25pp.com/9/1043977_137265314002.jpg"; //将文件下载后保存在E盘,返回结果为下载文件大小 //第二参数 指定存放位置 long size = HttpUtil.downloadFile(fileUrl, FileUtil.file("e:/")); System.out.println("Download size: " + size);
2.3 HttpRequest请求
本质上,HttpUtil中的get和post工具方法都是HttpRequest对象的封装,因此如果想更加灵活操作Http请求,可以使用HttpRequest。
普通表单
通过链式构建请求,我们可以很方便的指定Http头信息和表单信息,最后调用execute方法即可执行请求,返回HttpResponse对象。HttpResponse包含了服务器响应的一些信息,包括响应的内容和响应的头信息。通过调用body方法即可获取响应内容。
我们以POST请求为例:
String url="http://localhost:8080/test/hello"; HashMapparamMap = new HashMap<>(); paramMap.put("name", "张三"); //链式构建请求 String result2 = HttpRequest.post(url) .header(Header.USER_AGENT, "Hutool http")//头信息,多个头信息多次调用此方法即可 .form(paramMap)//表单内容 即参数 .timeout(20000)//超时,毫秒 //.body() 执行后返回的字符串 去掉.body()后 返回的是HttpResponse 对象 .execute().body(); //获得请求对应方法的响应结果 Console.log(result2);
RestFul风格请求 ==json字符串==
String url="http://localhost:8080/test/hello"; //构建参数对象 HashMapparamMap = new HashMap<>(); paramMap.put("name", "zhangsan"); paramMap.put("realName","张三"); //转换为json字符串 String json = JSONUtil.toJsonStr(paramMap); String result2 = HttpRequest.post(url) .body(json)//携带字符串 //.body() 执行后返回的字符串 去掉.body()后 返回的是HttpResponse 对象 .execute().body(); System.out.println(result2);//HelloWord | zhangsan | 张三 打印请求方法返回的字符串
其他自定义项
指定请求头
自定义Cookie(cookie方法)
指定是否keepAlive(keepAlive方法)
指定表单内容(form方法)
指定请求内容,比如rest请求指定JSON请求体(body方法)
超时设置(timeout方法)
指定代理(setProxy方法)
指定SSL协议(setSSLProtocol)
简单验证(basicAuth方法)
2.4 httpresponse响应
HttpResponse是HttpRequest执行execute()方法后返回的一个对象,我们可以通过此对象获取服务端返回的:
Http状态码(getStatus方法)
返回内容编码(contentEncoding方法)
是否Gzip内容(isGzip方法)
返回内容(body、bodyBytes、bodyStream方法)
响应头信息(header方法)
此对象的使用非常简单,最常用的便是body方法,会返回字符串Http响应内容。如果想获取byte[]则调用bodyBytes即可。
获取响应参数
获取状态码 ==.getstatus()==
获取头信息 ==.header(Header.CONTENT_ENCODING)==
获取响应结果==.body()==
String url="http://localhost:8080/test/hello"; //构建参数对象 HashMapparamMap = new HashMap<>(); paramMap.put("name", "zhangsan"); paramMap.put("realName","张三"); //转换为json字符串 String json = JSONUtil.toJsonStr(paramMap); HttpResponse res = HttpRequest.post(url) .body(json)//携带字符串 //.body() 执行后返回的字符串 去掉.body()后 返回的是HttpResponse 对象 .execute()/*.body()*/; System.out.println(res.getStatus());//200 返回状态码 Console.log(res.header(Header.CONTENT_ENCODING));//预定义的头信息 Console.log(res.header("Content-Disposition"));//自定义头信息 Console.log(res.body());//请求结果
2.5 ==爬取页面标题==
其实核心就前两行代码,第一行请求页面内容,第二行正则定位所有标题行并提取标题部分。
这里我解释下正则部分:
ReUtil.findAll方法用于查找所有匹配正则表达式的内容部分,
第一个参数是正则表达式 (.*?) 有几个就写几个,每个是一个分组 从1开始 下面例子 取得就是第二个分组内容
第二个参数 是要匹配的正文内容 即 页面 鼠标右键 查看源代码所见到的内容
第三个参数2表示提取第一个括号(分组)中的内容,0表示提取所有正则匹配到的内容。
这个方法可以看下core模块中ReUtil章节了解详情。
//请求列表页 String listContent = HttpUtil.get("https://www.chinanews.com/china.shtml"); //使用正则获取所有标题 Listtitles = ReUtil.findAll("(.*?)", listContent, 2); for (String title : titles) { //打印标题 Console.log(title); }
2.6 代码搭建简易服务器
public static void main(String[] args) { HttpUtil.createServer(8888) //参数一为访问路径 参数二lambda表达式 类比httpservlet的 dopost doget方法 .addAction("/", (req, res)->{ //中文支持 res.setContentType("text/html;charset=UTF-8"); res.write("测试"); }) .start(); }
==Convert==类可以说是一个工具方法类,里面封装了针对Java常见类型的转换,用于简化类型转换。Convert类中大部分方法为toXXX,参数为Object,可以实现将任意可能的类型转换为指定类型。同时支持第二个参数defaultValue用于在转换失败时返回一个默认值。
3.1转换为字符串
//将int类型转换为String类型 int a = 1; //aStr为"1" String aStr = Convert.toStr(a); //将数组转换为String long[] b = {1,2,3,4,5}; //bStr为:"[1, 2, 3, 4, 5]" String bStr = Convert.toStr(b);
3.2转换为指定类型数组
//String类型转换为Integer数组 String[] b = { "1", "2", "3", "4" }; //结果为Integer数组 Integer[] intArray = Convert.toIntArray(b); //long类型数组 转化为 int类型数组 long[] c = {1,2,3,4,5}; //结果为Integer数组 Integer[] intArray2 = Convert.toIntArray(c);
3.3 转换为日期对象
从以下例子可以看出
==支持符号"-" 跟 "/" 不支持"年月日"==
==支持yyyy-MM-dd 这种格式 不支持 时分秒==
//支持 String a = "2017-05-06"; Date value = Convert.toDate(a); System.out.println(value);//2017-05-06 00:00:00 //不支持汉字式分割 a="2021年5月12号"; Date value2 = Convert.toDate(a); System.out.println(value2);//null //支持"/" a="2021/05/12"; Date value3 = Convert.toDate(a); System.out.println(value3);//2021-05-12 00:00:00 //不支持时分秒 a="2021/5/12/10/05/21"; Date value4 = Convert.toDate(a); System.out.println(value4);//null
3.4 转换为集合
//数组转换为集合 Object[] a = {"a", "你", "好", "", 1}; List> list = Convert.convert(List.class, a); System.out.println(list); //从4.1.11开始可以这么用 List> list1 = Convert.toList(a); System.out.println(list1);
日期时间包是Hutool的核心包之一,提供针对JDK中Date和Calendar对象的封装,封装对象如下:
DateUtil
针对日期时间操作提供一系列静态方法
DateTime
提供类似于Joda-Time中日期时间对象的封装,继承自Date类,并提供更加丰富的对象方法。
FastDateFormat
提供线程安全的针对Date对象的格式化和日期字符串解析支持。此对象在实际使用中并不需要感知,相关操作已经封装在DateUtil
和DateTime
的相关方法中。
DateBetween
计算两个时间间隔的类,除了通过构造新对象使用外,相关操作也已封装在DateUtil
和DateTime
的相关方法中。
TimeInterval
一个简单的计时器类,常用于计算某段代码的执行时间,提供包括毫秒、秒、分、时、天、周等各种单位的花费时长计算,对象的静态构造已封装在DateUtil
中。
DatePattern
提供常用的日期格式化模式,包括String
类型和FastDateFormat
两种类型。
4.1 转换功能
==Date、long、Calendar之间的相互转换==
//当前时间 Date date = DateUtil.date(); System.out.println(date);//2021-03-26 09:30:39 //当前时间 Date date2 = DateUtil.date(Calendar.getInstance()); System.out.println(date2);//2021-03-26 09:30:39 //当前时间 Date date3 = DateUtil.date(System.currentTimeMillis()); System.out.println(date3);//2021-03-26 09:30:39 //当前时间字符串,格式:yyyy-MM-dd HH:mm:ss String now = DateUtil.now(); System.out.println(now);//2021-03-26 09:30:39 //当前日期字符串,格式:yyyy-MM-dd String today = DateUtil.today(); System.out.println(today);//2021-03-26
4.2字符串转日期
自动识别一些常用格式 比如:
yyyy-MM-dd HH:mm:ss
yyyy-MM-dd
HH:mm:ss
yyyy-MM-dd HH:mm
yyyy-MM-dd HH:mm:ss.SSS
String dateStr = "2017-03-01"; Date date = DateUtil.parse(dateStr);
也可以这样操作 -指定当前输入的格式
String dateStr = "2017-03-01"; Date date = DateUtil.parse(dateStr, "yyyy-MM-dd");
4.3 获取时间部分属性
Date date = DateUtil.date(); System.out.println(date);//2021-03-26 09:36:51 //获得年的部分 int year = DateUtil.year(date); System.out.println(year);//2021 //获得月份,从0开始计数 int month = DateUtil.month(date); System.out.println(month);//2 //获得月份枚举 Month anEnum = DateUtil.monthEnum(date); System.out.println(anEnum);//MARCH System.out.println(anEnum.getValue());//2 //使用枚举获取某个月份的最后一天 第一个参数为月 第二个参数为是否闰年 int lastDay = anEnum.getLastDay(month, false); System.out.println(lastDay);//31 //.....
4.4 开始和结束时间
有的时候我们需要获得每天的开始时间、结束时间,每月的开始和结束时间等等,DateUtil也提供了相关方法:
String dateStr = "2017-03-01 22:33:23"; Date date = DateUtil.parse(dateStr); //一天的开始,结果:2017-03-01 00:00:00 Date beginOfDay = DateUtil.beginOfDay(date); System.out.println(beginOfDay);//2017-03-01 00:00:00 //一天的结束,结果:2017-03-01 23:59:59 Date endOfDay = DateUtil.endOfDay(date); System.out.println(endOfDay);//2017-03-01 23:59:59
4.5 日期偏移计算
日期或时间的偏移指针对某个日期增加或减少分、小时、天等等,达到日期变更的目的。Hutool也针对其做了大量封装
String dateStr = "2017-03-01 22:33:23"; Date date = DateUtil.parse(dateStr); //当前时间 加2天 参数1 当前时间 参数2 单位 参数3 值 Date newDate = DateUtil.offset(date, DateField.DAY_OF_MONTH, 2); System.out.println(newDate);//2017-03-03 22:33:23 //常用偏移,当前时间加3天 DateTime newDate2 = DateUtil.offsetDay(date, 3); System.out.println(newDate2);//2017-03-04 22:33:23 //常用偏移,当前时间 减3小时 DateTime newDate3 = DateUtil.offsetHour(date, -3); System.out.println(newDate3);//2017-03-01 19:33:23
==针对当前时间,提供了简化的偏移方法(例如昨天、上周、上个月等)==
//昨天 DateUtil.yesterday() //明天 DateUtil.tomorrow() //上周 DateUtil.lastWeek() //下周 DateUtil.nextWeek() //上个月 DateUtil.lastMonth() //下个月 DateUtil.nextMonth()
4.6 日期时间差
有时候我们需要计算两个日期之间的时间差(相差天数、相差小时数等等),Hutool将此类方法封装为between方法:
==两个时间点的的位置可以颠倒==
String dateStr1 = "2017-03-01 22:33:23"; Date date1 = DateUtil.parse(dateStr1); String dateStr2 = "2017-04-01 23:33:23"; Date date2 = DateUtil.parse(dateStr2); //相差一个月,31天 /参数一 时间点1 /参数二 时间点2 /参数3 单位 long betweenDay = DateUtil.between(date1, date2, DateUnit.DAY); System.out.println(betweenDay);
4.7 格式化时间差
有时候我们希望看到易读的时间差,比如XX天XX小时XX分XX秒,此时使用DateUtil.formatBetween
方法:
//Level.MINUTE表示精确到分 String formatBetween = DateUtil.formatBetween(between, Level.MINUTE); //输出:31天1小时 Console.log(formatBetween);
4.8 计时器功能
计时器用于计算某段代码或过程花费的时间
String dateStr1 = "2017-03-01 22:33:23"; Date date1 = DateUtil.parse(dateStr1); String dateStr2 = "2017-03-15 23:33:23"; Date date2 = DateUtil.parse(dateStr2); //Level.MINUTE表示精确到分 /参数一[顺序可颠倒] 时间点1 /参数二 时间点2 /参数三 单位 String formatBetween = DateUtil.formatBetween(date2,date1, BetweenFormatter.Level.MINUTE); //输出:14天1小时 Console.log(formatBetween); //直接输入Long类型的毫秒值 String formatBetween1 = DateUtil.formatBetween(9999999); //输出:2小时46分39秒999毫秒 Console.log(formatBetween1); //BetweenFormatter.Level.HOUR 输出多少小时 //参数1 Long类型的毫秒值 参数2 单位 String formatBetween2 = DateUtil.formatBetween(9999999, BetweenFormatter.Level.HOUR); //输出:2小时 Console.log(formatBetween2);
4.9 其他
==计算年龄/ 是否闰年 /星座属相等==
//年龄 DateUtil.ageOfNow("1990-01-30"); //是否闰年 DateUtil.isLeapYear(2017); // "摩羯座" String zodiac = DateUtil.getZodiac(Month.JANUARY.getValue(), 19); // "狗" String chineseZodiac = DateUtil.getChineseZodiac(1994);
由来
考虑工具类的局限性,在某些情况下使用并不简便,于是DateTime类诞生。DateTime对象充分吸取Joda-Time库的优点,并提供更多的便捷方法,这样我们在开发时不必再单独导入Joda-Time库便可以享受简单快速的日期时间处理过程。
说明
DateTime类继承于java.util.Date类,为Date类扩展了众多简便方法,这些方法多是DateUtil静态方法的对象表现形式,使用DateTime对象可以完全替代开发中Date对象的使用。
5.1 创建DateTime对象
DateTime
对象包含众多的构造方法,构造方法支持的参数有:
Date
Calendar
String(日期字符串,第二个参数是日期格式)
long 毫秒数
构建对象有两种方式:==DateTime.of()
和new DateTime()
:==
Date date = new Date();//当前时间 //new方式创建 DateTime time = new DateTime(date); Console.log(time);//2021-03-26 10:10:21 //静态方法 of方式创建 //静态方法 now() 获取当前时间 DateTime now = DateTime.now(); System.out.println(now);//2021-03-26 10:10:21 //需要传入date参数 DateTime dt = DateTime.of(date); System.out.println(dt);//2021-03-26 10:10:21
5.2使用DateTime对象
DateTime
的成员方法与DateUtil
中的静态方法所对应,因为是成员方法,因此可以使用更少的参数操作日期时间。
示例:获取日期成员(年、月、日等)
//参数一 为时间字符串 参数二 提供大量格式化的 字符串解析方式 DateTime dateTime = new DateTime("2017-01-05 12:34:23", DatePattern.NORM_DATETIME_FORMAT); //年,结果: int year = dateTime.year(); System.out.println(year);//2017 //月份,结果:一月 Month month = dateTime.monthEnum(); System.out.println(month);//JANUARY //日, 1月 5号 int day = dateTime.dayOfMonth(); System.out.println(day);//5 //一周中的哪一天 星期四 int week = dateTime.dayOfWeek(); System.out.println(week);//5
5.3 DateTime对象可变性
DateTime对象默认是可变对象(调用offset、setField、setTime方法默认变更自身),
但是这种可变性有时候会引起很多问题(例如多个地方共用DateTime对象)。
我们可以调用
setMutable(false)
方法使其变为不可变对象。在不可变模式下,
offset
、setField
方法返回一个新对象,setTime
方法抛出异常。
DateTime dateTime = new DateTime("2017-01-05 12:34:23", DatePattern.NORM_DATETIME_FORMAT); //默认情况下DateTime为可变对象,此时offset == dateTime DateTime offset = dateTime.offset(DateField.YEAR, 0); //设置为不可变对象后变动将返回新对象,此时offset != dateTime dateTime.setMutable(false); offset = dateTime.offset(DateField.YEAR, 0);
5.4 格式化为字符串
调用toString()
方法即可返回格式为yyyy-MM-dd HH:mm:ss
的字符串,
调用toString(String format)
可以返回指定格式的字符串。
DateTime dateTime = new DateTime("2017-01-05 12:34:23", DatePattern.NORM_DATETIME_FORMAT); //结果:默认yyyy-MM-dd HH:mm:ss 格式 String dateStr = dateTime.toString(); System.out.println(dateStr);//2017-01-05 12:34:23 //参数:yyyy/MM/dd 指定格式转字符串 String dateStr2 = dateTime.toString("yyyy/MM/dd"); System.out.println(dateStr2);//2017/01/05
io包的封装主要针对流、文件的读写封装,主要以工具类为主,提供常用功能的封装,这包括:
IoUtil
流操作工具类
FileUtil
文件读写和操作的工具类。
FileTypeUtil
文件类型判断工具类
WatchMonitor
目录、文件监听,封装了JDK1.7中的WatchService
ClassPathResource
针对ClassPath中资源的访问封装
FileReader
封装文件读取
FileWriter
封装文件写入
6.1 IoUtil工具类
IO工具类的存在主要针对InputStream、OutputStream、Reader、Writer封装简化,并对NIO相关操作做封装简化。总体来说,Hutool对IO的封装,主要是工具层面,我们努力做到在便捷、性能和灵活之间找到最好的平衡点。
拷贝
流的读写可以总结为从输入流读取,从输出流写出,这个过程我们定义为拷贝。这个是一个基本过程,也是文件、流操作的基础。
以文件流拷贝为例:
BufferedInputStream in = FileUtil.getInputStream("d:/test.txt"); BufferedOutputStream out = FileUtil.getOutputStream("d:/test2.txt"); long copySize = IoUtil.copy(in, out, IoUtil.DEFAULT_BUFFER_SIZE);
==copy==方法同样针对Reader、Writer、Channel等对象有一些重载方法,并提供可选的缓存大小。默认的,缓存大小为
1024
个字节,如果拷贝大文件或流数据较大,可以适当调整这个参数。针对NIO,提供了==
copyByNIO
==方法,以便和BIO有所区别。我查阅过一些资料,==使用NIO对文件流的操作有一定的提升!==
stream流转字符流
==IoUtil.getReader
==:将InputStream
转为BufferedReader
用于读取字符流,它是部分readXXX方法的基础。
==IoUtil.getWriter
==:将OutputStream
转为OutputStreamWriter
用于写入字符流,它是部分writeXXX的基础。
读取流中的内容
读取流中的内容总结下来,可以分为==read方法==和==readXXX==方法。
read
方法有诸多的重载方法,根据参数不同,可以读取不同对象中的内容,这包括:
InputStream
Reader
FileChannel
这三个重载大部分返回==String字符串==,为字符流读取提供极大便利。
readXXX
方法主要针对返回值做一些处理,例如:
readBytes
返回byte数组(读取图片等)
readHex
读取16进制字符串
readObj
读取序列化对象(反序列化)
readLines
按行读取
==
toStream
==方法则是将某些对象转换为流对象,便于在某些情况下操作:
String
转换为ByteArrayInputStream
File
转换为FileInputStream
写入到流
==IoUtil.write
==方法有两个重载方法,一个直接调用OutputStream.write
方法,另一个用于将对象转换为字符串(调用toString方法),然后写入到流中。
==IoUtil.writeObjects
== 用于将可序列化对象序列化后写入到流中。
write
方法并没有提供writeXXX,需要自己转换为String或byte[]。
关闭流
关闭操作会面临两个问题:
被关闭对象为空
对象关闭失败(或对象已关闭)
==IoUtil.close
==方法很好的解决了这两个问题。
6.2 FileUtil工具类
总体来说,FileUtil类包含以下几类操作工具:
文件操作:包括文件目录的新建、删除、复制、移动、改名等
文件判断:判断文件或目录是否非空,是否为目录,是否为文件等等。
绝对路径:针对ClassPath中的文件转换为绝对路径文件。
文件名:主文件名,扩展名的获取
读操作:包括类似IoUtil中的getReader、readXXX操作
写操作:包括getWriter和writeXXX操作
在FileUtil中,我努力将方法名与Linux相一致,例如创建文件的方法并不是createFile,而是touch
,这种统一对于熟悉Linux的人来说,大大提高了上手速度。当然,如果你不熟悉Linux,那FileUtil工具类的使用则是在帮助你学习Linux命令。这些类Linux命令的方法包括:
ls
列出目录和文件
touch
创建文件,如果父目录不存在也自动创建
mkdir
创建目录,会递归创建每层目录
del
删除文件或目录(递归删除,不判断是否为空),这个方法相当于Linux的delete命令
copy
拷贝文件或目录
file
创建文件或目录
这些方法提供了人性化的操作,例如touch
方法,在创建文件的情况下会自动创建上层目录(我想对于使用者来说这也是大部分情况下的需求),同样mkdir
也会创建父目录。
需要注意的是,
del
方法会删除目录而不判断其是否为空,这一方面方便了使用,另一方面也可能造成一些预想不到的后果(比如拼写错路径而删除不应该删除的目录),所以请谨慎使用此方法。
7.1图片工具ImgUtil
针对awt中图片处理进行封装,这些封装包括:缩放、裁剪、转为黑白、加水印等操作。
7.1.1 scale缩放图片
提供两种重载方法:其中一个是按照长宽缩放,另一种是按照比例缩放。
ImgUtil.scale( FileUtil.file("d:/face.jpg"), FileUtil.file("d:/face_result.jpg"), 0.5f//缩放比例 );
7.1.2 cut 剪裁图片
ImgUtil.cut( FileUtil.file("d:/face.jpg"), FileUtil.file("d:/face_result.jpg"), new Rectangle(200, 200, 100, 100)//裁剪的矩形区域 );
7.1.3 slice 行列剪裁图片
按照行列剪裁切片(将图片分为20行和20列)
ImgUtil.slice(FileUtil.file("e:/test2.png"), FileUtil.file("e:/dest/"), 10, 10);
7.1.4 convert 图片类型转换
==支持GIF->JPG、GIF->PNG、PNG->JPG、PNG->GIF(X)、BMP->PNG等==
ImgUtil.convert(FileUtil.file("e:/test2.png"), FileUtil.file("e:/test2Convert.jpg"));
7.1.5 gray 彩色转黑白
ImgUtil.gray(FileUtil.file("d:/logo.png"), FileUtil.file("d:/result.png"));
7.1.6 pressText 添加文字水印
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] 之内(包含边界值)的一个浮点数字 );
7.1.7 pressImage 添加图片水印
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 );
7.1.8 rotate旋转图片
// 旋转180度 BufferedImage image = ImgUtil.rotate(ImageIO.read(FileUtil.file("e:/pic/366466.jpg")), 180); ImgUtil.write(image, FileUtil.file("e:/pic/result.png"));
7.1.9 flip 水平翻转图片
ImgUtil.flip(FileUtil.file("d:/logo.png"), FileUtil.file("d:/result.png"));
7.2 Img 图片编辑器
7.2.1 图像切割
// 将face.jpg切割为原型保存为face_radis.png Img.from(FileUtil.file("e:/pic/face.jpg")) .cut(0, 0, 200)// .write(FileUtil.file("e:/pic/face_radis.png"));
7.2.2 图片压缩
==图片压缩只支持Jpg文件==
Img.from(FileUtil.file("e:/pic/1111.png")) .setQuality(0.8)//压缩比率 .write(FileUtil.file("e:/pic/1111_target.jpg"));
URL的结构如下:
[scheme:]scheme-specific-part[#fragment] [scheme:][//authority][path][?query][#fragment] [scheme:][//host:port][path][?query][#fragment]按照这个格式,UrlBuilder将URL分成scheme、host、port、path、query、fragment部分,其中path和query较为复杂,又使用
UrlPath
和UrlQuery
分别封装。
8.1简单使用
相比URL
对象,UrlBuilder更加人性化,例如:
URL url = new URL("www.hutool.cn")
此时会报java.net.MalformedURLException: no protocol
的错误,而使用UrlBuilder则会有默认协议:
// 输出 http://www.hutool.cn/ String buildUrl = UrlBuilder.create().setHost("www.hutool.cn").build();
8.2完整构建
// https://www.hutool.cn/aaa/bbb?ie=UTF-8&wd=test String buildUrl = UrlBuilder.create() .setScheme("https") .setHost("www.hutool.cn") .addPath("/aaa").addPath("bbb") .addQuery("ie", "UTF-8") .addQuery("wd", "test") .build();
8.3中文编码
当参数中有中文时,自动编码中文,默认UTF-8编码,也可以调用setCharset
方法自定义编码。
// https://www.hutool.cn/s?ie=UTF-8&ie=GBK&wd=%E6%B5%8B%E8%AF%95 String buildUrl = UrlBuilder.create() .setScheme("https") .setHost("www.hutool.cn") .addPath("/s") .addQuery("ie", "UTF-8") .addQuery("ie", "GBK") .addQuery("wd", "测试") .build();
8.4解析URL中文
当有一个URL字符串时,可以使用of
方法解析:
我们发现这个例子中,原URL中的参数a是没有编码的,b是编码过的,当用户提供此类混合URL时,Hutool可以很好的识别并全部decode,当然,调用build()之后,会全部再encode。
UrlBuilder builder = UrlBuilder.ofHttp("www.hutool.cn/aaa/bbb/?a=张三&b=%e6%9d%8e%e5%9b%9b#frag1", CharsetUtil.CHARSET_UTF_8); // 输出张三 Console.log(builder.getQuery().get("a")); // 输出李四 Console.log(builder.getQuery().get("b"));
8.5 URL特殊符号解析
有时候URL中会存在&
这种分隔符,谷歌浏览器会将此字符串转换为&
使用,Hutool中也同样如此
UrlBuilder主要应用于http模块,在构建HttpRequest时,用户传入的URL五花八门,为了做大最好的适应性,减少用户对URL的处理,使用UrlBuilder完成URL的规范化。
String urlStr = "https://mp.weixin.qq.com/s?__biz=MzI5NjkyNTIxMg==&mid=100000465&idx=1"; UrlBuilder builder = UrlBuilder.ofHttp(urlStr, CharsetUtil.CHARSET_UTF_8); // https://mp.weixin.qq.com/s?__biz=MzI5NjkyNTIxMg==&mid=100000465&idx=1 Console.log(builder.build());
DFA全称为:Deterministic Finite Automaton,即确定有穷自动机。因为本人算法学的不好
解释起来原理其实也不难,就是用所有关键字构造一棵树,然后用正文遍历这棵树,遍历到叶子节点即表示文章中存在这个关键字。
我们暂且忽略构建关键词树的时间,每次查找正文只需要O(n)复杂度就可以搞定。
针对DFA算法以及网上的一些实现,Hutool做了整理和改进,最终形成现在的Hutool-dfa模块。
9.1 构建关键词树
可以将关键词放入数据库中保存 用的时候进行构建即可
WordTree tree = new WordTree(); tree.addWord("大"); tree.addWord("大土豆"); tree.addWord("土豆"); tree.addWord("刚出锅"); tree.addWord("出锅");
9.2 查找关键词
//正文 String text = "我有一颗大土豆,刚出锅的";
==情况一:标准匹配,匹配到最短关键词,并跳过已经匹配的关键词==
// 匹配到【大】,就不再继续匹配了,因此【大土豆】不匹配 // 匹配到【刚出锅】,就跳过这三个字了,因此【出锅】不匹配(由于刚首先被匹配,因此长的被匹配,最短匹配只针对第一个字相同选最短) ListmatchAll = tree.matchAll(text, -1, false, false); Assert.assertEquals(matchAll.toString(), "[大, 土豆, 刚出锅]");
==情况二:匹配到最短关键词,不跳过已经匹配的关键词==
// 【大】被匹配,最短匹配原则【大土豆】被跳过,【土豆继续被匹配】 // 【刚出锅】被匹配,由于不跳过已经匹配的词,【出锅】被匹配 matchAll = tree.matchAll(text, -1, true, false); Assert.assertEquals(matchAll.toString(), "[大, 土豆, 刚出锅, 出锅]");
==情况三:匹配到最长关键词,跳过已经匹配的关键词==
// 匹配到【大】,由于到最长匹配,因此【大土豆】接着被匹配 // 由于【大土豆】被匹配,【土豆】被跳过,由于【刚出锅】被匹配,【出锅】被跳过 matchAll = tree.matchAll(text, -1, false, true); Assert.assertEquals(matchAll.toString(), "[大, 大土豆, 刚出锅]");
==情况四:匹配到最长关键词,不跳过已经匹配的关键词(最全关键词)==
// 匹配到【大】,由于到最长匹配,因此【大土豆】接着被匹配,由于不跳过已经匹配的关键词,土豆继续被匹配 // 【刚出锅】被匹配,由于不跳过已经匹配的词,【出锅】被匹配 matchAll = tree.matchAll(text, -1, true, true); Assert.assertEquals(matchAll.toString(), "[大, 大土豆, 土豆, 刚出锅, 出锅]");
除了
matchAll
方法,==WordTree
还提供了match
和isMatch
两个方法==,这两个方法只会查找第一个匹配的结果,这样一旦找到第一个关键字,就会停止继续匹配,大大提高了匹配效率。
特殊字符
有时候,正文中的关键字常常包含特殊字符,比如:"〓关键☆字",针对这种情况,Hutool提供了
StopChar
类,专门针对特殊字符做跳过处理,这个过程是在match
方法或matchAll
方法执行的时候自动去掉特殊字符。
CronUtil通过一个全局的定时任务配置文件,实现统一的定时任务调度。
10.1 配置文件
==corn.setting配置文件见同目录下附件==
对于Maven项目,首先在src/main/resources/config
下放入cron.setting文件(默认是这个路径的这个文件),然后在文件中放入定时规则,规则如下:
#注意点 本配置文件需要放在 resources/config包下 config包下 config包下 重要事情说三遍 #注意 中括号是要执行任务的包名 [com.example.demo2222.job] # TestJob 是定时任务类名 .run 是定时任务的方法名 # =号右边是 cron表达式 支持秒级需要开启前执行 CronUtil.setMatchSecond(true); TestJob.run = */2 * * * * *
中括号表示分组,也表示需要执行的类或对象方法所在包的名字,这种写法有利于区分不同业务的定时任务。
==TestJob.run
==表示需要执行的类名和方法名(通过反射调用,不支持Spring和任何框架的依赖注入),
==*/10 * * * *
==表示定时任务表达式,此处表示每10分钟执行一次,以上配置等同于:
com.company.aaa.job.TestJob.run = */10 * * * * com.company.aaa.job.TestJob2.run = */10 * * * *
10.2 启动定时任务
CronUtil.start();
如果想让执行的作业同定时任务线程同时结束,可以将定时任务设为守护线程,需要注意的是,此模式下会在调用stop时立即结束所有作业线程,请确保你的作业可以被中断:
//使用deamon模式, CronUtil.start(true);
10.3 关闭定时任务
CronUtil.stop();
10.4 cron表达式 秒/年匹配
考虑到Quartz表达式的兼容性,且存在对于秒级别精度匹配的需求,==Hutool可以通过设置使用秒匹配模式来兼容==
//支持秒级别定时任务 CronUtil.setMatchSecond(true);
此时Hutool可以兼容Quartz表达式(5位表达式、6位表达式都兼容)
10.5 动态添加定时任务
当然,如果你想动态的添加定时任务,
使用CronUtil.schedule(String schedulingPattern, Runnable task)
方法即可
(==使用此方法加入的定时任务不会被写入到配置文件==)。
public static void main(String[] args) { CronUtil.schedule("*/2 * * * * *", new Task() { @Override public void execute() { } }); // 支持秒级别定时任务 CronUtil.setMatchSecond(true); //开启定时任务 CronUtil.start(); }
由于大家对二维码的需求较多,对于二维码的生成和解析我认为应该作为简单的工具存在于Hutool中。考虑到自行实现的难度,因此Hutool针对被广泛接受的的zxing库进行封装。而由于涉及第三方包,因此归类到extra模块中。
11.1 导入jar包
考虑到Hutool的非强制依赖性,因此zxing需要用户自行引入:
com.google.zxing core 3.3.3
11.2 生成二维码
public static void main(String[] args) { // 生成指定url对应的二维码到文件,宽和高都是300像素 String url="http://kjfeng44.usa3v.vip/"; //参数一 为网址 参数二、三为:二维码宽高 参数四为 生成图片存放路径 QrCodeUtil.generate(url, 300, 300, FileUtil.file("d:/qrcode.jpg")); }
11.2 自定义参数
通过==QrConfig
==可以自定义二维码的生成参数,==例如长、宽、二维码的颜色、背景颜色、边距等参数 ,添加中央小logo== ,使用方法如下:
public static void main(String[] args) { String url="http://kjfeng44.usa3v.vip/"; // 生成指定url对应的二维码到文件,宽和高都是300像素 QrConfig config = new QrConfig(300, 300); // 设置边距,既二维码和背景之间的边距 config.setMargin(3); // 设置前景色,既二维码颜色(青色) config.setForeColor(Color.CYAN.getRGB()); // 设置背景色(灰色) config.setBackColor(Color.GRAY.getRGB()); // 添加二维码中央 小logo File generate = QrCodeUtil.generate( url, //二维码内容 QrConfig.create().setImg("e:/logo_small.jpg"), //附带logo FileUtil.file("e:/qrcodeWithLogo.jpg")//写出到的文件 生成二维码到文件,也可以到流 ); }
11.3 ==识别二维码==
识别逻辑在底部
public static void main(String[] args) { String url="http://kjfeng44.usa3v.vip/"; // 生成指定url对应的二维码到文件,宽和高都是300像素 QrConfig config = new QrConfig(300, 300); // 设置边距,既二维码和背景之间的边距 config.setMargin(3); // 设置前景色,既二维码颜色(青色) config.setForeColor(Color.CYAN.getRGB()); // 设置背景色(灰色) config.setBackColor(Color.GRAY.getRGB()); // 添加二维码中央 小logo File generate = QrCodeUtil.generate( url, //二维码内容 QrConfig.create().setImg("d:/logo.jpg"), //附带logo FileUtil.file("d:/qrcode.jpg")//写出到的文件 生成二维码到文件,也可以到流 ); //=======================下方代码为 识别二维码========================= //打印结果 http://kjfeng44.usa3v.vip/ String decode = QrCodeUtil.decode(FileUtil.file("d:/qrcode.jpg"));//传入二维码文件 System.out.println(decode); String decode1 = QrCodeUtil.decode(generate);//传入上面生成的二维码文件 System.out.println(decode1); }
详见官网