文章目录
1. 介绍
Hutool是一个小而全的Java工具类库,通过静态方法封装,降低相关API的学习成本,提高工作效率,使Java拥有函数式语言般的优雅,让Java语言也可以“甜甜的”。
Hutool中的工具方法来自每个用户的精雕细琢,它涵盖了Java开发底层代码中的方方面面,它既是大型项目开发中解决小问题的利器,也是小型项目中的效率担当.
2. 克隆
2.1 泛型克隆接口
我们知道,JDK中的Cloneable接口只是一个空接口,并没有定义成员,它存在的意义仅仅是指明一个类的实例化对象支持位复制(就是对象克隆),如果不实现这个类,调用对象的clone()方法就会抛出CloneNotSupportedException异常。而且,因为clone()方法在Object对象中,返回值也是Object对象,因此克隆后我们需要自己强转下类型。
因此,cn.hutool.core.clone.Cloneable接口应运而生。此接口定义了一个返回泛型的成员方法,这样,实现此接口后会提示必须实现一个public的clone方法,调用父类clone方法即可:
/**
* 猫猫类,使用实现Cloneable方式
* @author Looly
*
*/
private static class Cat implements Cloneable<Cat>{
private String name = "miaomiao";
private int age = 2;
@Override
public Cat clone() {
try {
return (Cat) super.clone();
} catch (CloneNotSupportedException e) {
throw new CloneRuntimeException(e);
}
}
}
2.2 泛型克隆类
但是实现此接口依旧有不方便之处,就是必须自己实现一个public类型的clone()方法,还要调用父类(Object)的clone方法并处理异常。于是cn.hutool.clone.CloneSupport类产生,这个类帮我们实现了上面的clone方法,因此只要继承此类,不用写任何代码即可使用clone()方法:
/**
* 狗狗类,用于继承CloneSupport类
* @author Looly
*
*/
private static class Dog extends CloneSupport<Dog>{
private String name = "wangwang";
private int age = 3;
}
2.3 深克隆
我们知道实现Cloneable接口后克隆的对象是浅克隆,要想实现深克隆,请使用:
ObjectUtil.cloneByStream(obj)
3. 类型转换
- 痛点
在Java开发中我们要面对各种各样的类型转换问题,尤其是从命令行获取的用户参数、从HttpRequest获取的Parameter等等,这些参数类型多种多样,我们怎么去转换他们呢?常用的办法是先整成String,然后调用XXX.parseXXX方法,还要承受转换失败的风险,不得不加一层try catch,这个小小的过程混迹在业务代码中会显得非常难看和臃肿。
3.1 类型转换工具类-Convert
Convert类可以说是一个工具方法类,里面封装了针对Java常见类型的转换,用于简化类型转换。Convert类中大部分方法为toXXX,参数为Object,可以实现将
任意可能的类型
转换为指定类型
。同时支持第二个参数defaultValue
用于在转换失败时返回一个默认值
。
- 转换为字符串
int a = 1;
//aStr为"1"
String aStr = Convert.toStr(a);
long[] b = {1,2,3,4,5};
//bStr为:"[1, 2, 3, 4, 5]"
String bStr = Convert.toStr(b);
- 转换为指定类型数组
String[] b = { "1", "2", "3", "4" };
//结果为Integer数组
Integer[] intArray = Convert.toIntArray(b);
long[] c = {1,2,3,4,5};
//结果为Integer数组
Integer[] intArray2 = Convert.toIntArray(c);
- 转换为日期对象
String a = "2017-05-06";
Date value = Convert.toDate(a);
- 转换为集合
Object[] a = {"a", "你", "好", "", 1};
List<?> list = Convert.convert(List.class, a);
//从4.1.11开始可以这么用
List<?> list = Convert.toList(a);
3.2 自定义类型转换-ConverterRegistry
Converter
类型转换接口,通过实现这个接口,重写convert方法,以实现不同类型的对象转换ConverterRegistry
类型转换登记中心。将各种类型Convert对象放入登记中心,通过convert方法查找目标类型对应的转换器,将被转换对象转换之。在此类中,存放着默认转换器和自定义转换器,默认转换器是Hutool中预定义的一些转换器,自定义转换器存放用户自定的转换器。
通过这种方式,实现类灵活的类型转换。使用方式如下:
int a = 3423;
ConverterRegistry converterRegistry = ConverterRegistry.getInstance();
String result = converterRegistry.convert(String.class, a);
Assert.assertEquals("3423", result);
- 自定义转换
//自定义转换器
public static class CustomConverter implements Converter<String>{
@Override
public String convert(Object value, String defaultValue) throws IllegalArgumentException {
return "Custom: " + value.toString();
}
}
...
//注册转换器, 执行转换
public void test6() {
ConverterRegistry converterRegistry = ConverterRegistry.getInstance();//注册转换器
//此处做为示例自定义String转换,因为Hutool中已经提供String转换,请尽量不要替换
//替换可能引发关联转换异常(例如覆盖String转换会影响全局)
converterRegistry.putCustom(String.class, CustomConverter.class);
int a = 454553;
String result = converterRegistry.convert(String.class, a,"3",true);//默认优先使用自定义转换器
Assert.assertEquals("Custom: 454553", result);
}
注意: convert(Class type, Object value, T defaultValue, boolean isCustomFirst)方法的最后一个参数可以选择转换时优先使用自定义转换器还是默认转换器。convert(Class type, Object value, T defaultValue)和convert(Class type, Object value)两个重载方法都是使用自定义转换器优先的模式。
ConverterRegistry
单例和对象模式
ConverterRegistry提供一个静态方法getInstance()返回全局单例对象,这也是推荐的使用方式,当然如果想在某个限定范围内自定义转换,可以实例化ConverterRegistry对象。
4. 日期时间
DateUtil
针对日期时间操作提供一系列静态方法
DateTime
提供类似于Joda-Time中日期时间对象的封装,继承自Date类,并提供更加丰富的对象方法。
·FastDateFormat ·提供线程安全的针对Date对象的格式化和日期字符串解析支持。此对象在实际使用中并不需要感知,相关操作已经封装在DateUtil和DateTime的相关方法中。
DateBetween
计算两个时间间隔的类,除了通过构造新对象使用外,相关操作也已封装在DateUtil和DateTime的相关方法中。
TimeInterval
一个简单的计时器类,常用于计算某段代码的执行时间,提供包括毫秒、秒、分、时、天、周等各种单位的花费时长计算,对象的静态构造已封装在DateUtil中。
DatePattern
提供常用的日期格式化模式,包括String类型和FastDateFormat两种类型。
4.1 日期枚举
考虑到
Calendar
类中表示时间的字段(field)都是使用int
表示,在使用中非常不便,因此针对这些int
字段,封装了与之对应的Enum枚举类,这些枚举类在DateUtil
和DateTime
相关方法中做为参数使用,可以更大限度的缩小参数限定范围
这些定义的枚举值可以通过getValue()
方法获得其与Calendar
类对应的int值,通过of(int)
方法从Calendar
中int
值转为枚举对象。
与Calendar
对应的这些枚举包括:
Month
表示月份,与Calendar中的int值一一对应。Week
表示周,与Calendar中的int值一一对应
@Test//通过月份枚举可以获得某个月的最后一天
/**
* 另外,Hutool还定义了季度枚举。Season.SPRING为第一季度,表示1~3月。季度的概念并不等同于季节,因为季节与月份并不对应,季度常用于统计概念。
*/
public void test() {
int lastDay = Month.of(Calendar.JANUARY).getLastDay(false);
System.out.println(lastDay);
}
4.2 月份枚举
时间枚举
DateUnit
主要表示某个时间单位对应的毫秒数,常用于计算时间差。
例如:DateUnit.MINUTE
表示分,也表示一分钟的毫秒数,可以通过调用其getMillis()
方法获得其毫秒数。
4.3 日期时间工具-DateUtil
考虑到Java本身对日期时间的支持有限,并且Date和Calendar对象的并存导致各种方法使用混乱和复杂,故使用此工具类做了封装。这其中的封装主要是日期和字符串之间的
转换
,以及提供对日期的定位(一个月前等等)。
对于Date对象
,为了便捷,使用了一个DateTime类来代替之,继承自Date对象,主要的便利在于,覆盖了toString()方法,返回yyyy-MM-dd HH:mm:ss
形式的字符串,方便在输出时的调用(例如日志记录等),提供了众多便捷的方法对日期对象操作
Date、long、Calendar
之间的相互转换
//当前时间
@Test
public void test3() {
//当前时间
Date date = DateUtil.date();
//当前时间
Date date2 = DateUtil.date(Calendar.getInstance());
//当前时间
Date date3 = DateUtil.date(System.currentTimeMillis());
//当前时间字符串,格式:yyyy-MM-dd HH:mm:ss
String now = DateUtil.now();
//当前日期字符串,格式:yyyy-MM-dd
String today= DateUtil.today();
System.out.println(date);
System.out.println(date2);
System.out.println(date3);
System.out.println(now);
System.out.println(today);
}
DateUtil.parse
方法会自动识别一些常用格式,包括:
yyyy-MM-dd HH:mm:ss
yyyy/MM/dd HH:mm:ss
yyyy.MM.dd HH:mm:ss
yyyy年MM月dd日 HH时mm分ss秒
yyyy-MM-dd
yyyy/MM/dd
yyyy.MM.dd
HH:mm:ss
HH时mm分ss秒
yyyy-MM-dd HH:mm
yyyy-MM-dd HH:mm:ss.SSS
yyyyMMddHHmmss
yyyyMMddHHmmssSSS
yyyyMMdd
EEE, dd MMM yyyy HH:mm:ss z
EEE MMM dd HH:mm:ss zzz yyyy
yyyy-MM-dd'T'HH:mm:ss'Z'
yyyy-MM-dd'T'HH:mm:ss.SSS'Z'
yyyy-MM-dd'T'HH:mm:ssZ
yyyy-MM-dd'T'HH:mm:ss.SSSZ
@Test
public void test4() {
String dateStr = "2017-03-01";
Date date = DateUtil.parse(dateStr);//yyyy-MM-dd HH:mm:ss
System.out.println(date);
//我们也可以使用自定义日期格式转化:
String dateStr2 = "2017-03-01";
Date date2 = DateUtil.parse(dateStr2, "yyyy-MM-dd");
System.out.println(date2);
}
- 格式化日期输出
String dateStr = "2017-03-01";
Date date = DateUtil.parse(dateStr);
//结果 2017/03/01
String format = DateUtil.format(date, "yyyy/MM/dd");
//常用格式的格式化,结果:2017-03-01
String formatDate = DateUtil.formatDate(date);
//结果:2017-03-01 00:00:00
String formatDateTime = DateUtil.formatDateTime(date);
//结果:00:00:00
String formatTime = DateUtil.formatTime(date);
- 获取Date对象的某个部分
Date date = DateUtil.date();
//获得年的部分
DateUtil.year(date);
//获得月份,从0开始计数
DateUtil.month(date);
//获得月份枚举
DateUtil.monthEnum(date);
//.....
- 开始和结束时间
有的时候我们需要获得每天的开始时间、结束时间,每月的开始和结束时间等等,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);
//一天的结束,结果:2017-03-01 23:59:59
Date endOfDay = DateUtil.endOfDay(date);
- 日期时间偏移
日期或时间的偏移指针对某个日期增加或减少分、小时、天等等,达到日期变更的目的。Hutool也针对其做了大量封装
String dateStr = "2017-03-01 22:33:23";
Date date = DateUtil.parse(dateStr);
//结果:2017-03-03 22:33:23
Date newDate = DateUtil.offset(date, DateField.DAY_OF_MONTH, 2);
//常用偏移,结果:2017-03-04 22:33:23
DateTime newDate2 = DateUtil.offsetDay(date, 3);
//常用偏移,结果:2017-03-01 19:33:23
DateTime newDate3 = DateUtil.offsetHour(date, -3);
针对当前时间,提供了简化的偏移方法(例如昨天、上周、上个月等)
//昨天
DateUtil.yesterday()
//明天
DateUtil.tomorrow()
//上周
DateUtil.lastWeek()
//下周
DateUtil.nextWeek()
//上个月
DateUtil.lastMonth()
//下个月
DateUtil.nextMonth()