介绍一款超厉害的国产Java工具——Hutool。Hutool是一个Java工具包类库,对文件、流、加密解密、转码、正则、线程、XML等JDK方法进行封装,组成各类Util工具类。适用于不少项目以及Web开发,而且与其余框架没有耦合性。html
=========================================================java
DateUtil
针对日期时间操做提供一系列静态方法DateTime
提供相似于Joda-Time中日期时间对象的封装,继承DateFastDateFormat
提供线程安全的针对Date对象的格式化和日期字符串解析支持。此对象在实际使用中并不须要感知,相关操做已封装在DateUtil和DateTime的相关方法中。DateBetween
计算两个时间间隔的类,除了经过构造新对象使用外,相关操做也已封装在DateUtil和DateTime的相关方法中。TimeInterval
一个简单的计时器类,经常使用于计算某段代码的执行时间,提供包括毫秒、秒、分、时、天、周等各类单位的花费时长计算,对象的静态构造已封装在DateUtil中。DatePattern
提供经常使用的日期格式化模式,包括String和FastDateFormat两种类型。主要须要了解DateUtil类,DateTime类以及DatePattern类就能够应对大多数时间日期的操做。mysql
DateUtil中都是静态方法, 下面是一些简单方法;git
now():String //当前日期时间 yyyy-MM-dd hh:mm:ss
today():String //今天日期 yyyy-MM-dd
date():DateTime
/*当前时间的DateTime对象(至关于new DateTime()或者new Date()), 此外还提供一个重载方法,传递long类型参数,是给定一个Unix时间戳, 返回这个时间戳的时间。*/
lastWeek():DateTime //上周今天(往前7天)
lastMonth():DateTime //上个月今天(往前一个月)
nextWeek():DateTime //下周今天(日后7天)
nextMonth():DateTime //下个月今天(日后一个月)
yesterday():DateTime //昨天同时
tomorrow():DateTime //明天同时
currentSeconds():long //毫秒数
thisYear():int //年份
thisMonth():int //月份(从0开始)
thisWeekOfMonth():int //本月周次(从1开始)
thisWeekOfYear():int //本年周次(从1开始)
thisDayOfMonth():int //本月第几天(从1开始)
thisDayOfWeek():int //本周第几天(从1开始)
thisHour(boolean is24HourClock):int //当前小时
thisMinute():int //当前分
thisSecond():int //当前秒
复制代码
将一些固定格式的字符串-->Date对象:ajax
yyyy-MM-dd hh:mm:ss
yyyy-MM-dd
hh:mm:ss
yyyy-MM-dd hh:mm
若是你的日期格式不是这几种格式,则须要指定日期格式,对于以上格式还有专门的方法对应:
parseDateTime parseDate ParseTime
DateUtil.parse()
DateUtil.parse(String,String) //Date 转换为指定格式的Date对象
复制代码
须要将日期时间格式化输出,Hutool提供了一些方法实现:
DateUtil.formatDateTime(Date date):String //将返回“yyyy-MM-dd hh:mm:ss”格式字符串
DateUtil.formatDate(Date date):String //将返回“yyyy-MM-dd“格式字符串
DateUtil.formatTime(Date date):String //将返回“hh:mm:ss“格式字符串
DateUtil.format(Date,String):String //将返回指定格式的字符串
复制代码
Hutool能够很方便的得到某天/某月/某年的开始时刻和结束时刻:正则表达式
beginOfDay(Date):Date 一天开始时刻
endOfDay(Date):Date 一天结束时刻
beginOfMonth(Date):Date endOfMonth(Date):Date beginOfYear(Date):Date endOfYear(Date):Date getBeginTimeOfDay 得到给定日期当天的开始时间,开始时间是00:00 getEndTimeOfDay 得到给定日期当天的结束时间,结束时间是23:59。 复制代码
须要指定日期作偏移,则使用offsiteDay、offsiteWeek、offsiteMonth来得到指定日期偏移天、偏移周、偏移月,指定的偏移量正数向将来偏移,负数向历史偏移。
若是以上还不能知足偏移要求,则使用offsiteDate偏移制定量,其中参数calendarField为偏移的粒度大小(小时、天、月等)使用Calendar类中的常数。算法
1.between方法
Hutool能够方便的计算时间间隔,使用 DateUtil.between(Date begin,Date end,DateUnit):long
计算间隔,方法有三个参数,前两个分别是开始时间和结束时间,第三个参数是DateUnit的枚举,
表示差值以什么为单位。
DateUnit的取值能够是DateUnit.DAY(天),DateUnit.HOUR(小时),DateUnit.WEEK(周),
DateUnit.SECOND(秒),DateUnit.MINUTE(分钟);
2.formatBetween方法
也能够将差值转为指定具体的格式,好比 XX天XX小时XX分钟XX秒 这样的格式,可使用
DateUtil.formatBetween(Date begin,Date end,Level):String 方法,有三个参数,
前两个依然是开始和结束时间,第三个参数表示精确度,好比Level.SECOND表示精确到秒,
即XX天XX小时XX分钟XX秒的格式。
3.diff方法
返回两个日期的时间差,参数diffField定义了这个差的单位,单位的定义在DateUtil的常量中,
例如DateUtil.SECOND_MS表示两个日期相差的秒数。
复制代码
用了Hutool能够瞬间计算年龄,你还须要担忧什么虚岁周岁吗?不须要,Hutool说多少就是多少。咱们可使用以下方法:sql
age(Date begin,Date end):int 出生和去世日期计算年龄
ageOfNow(String birthday):int 计算到当前日期的年龄
ageOfNow(Date birthday):int 计算到当前日期的年龄
复制代码
使用DateUtil.isLeapYear(in year):boolean判断是否为闰年。shell
了解了DateUtil,再来看DateTime应该是很简单的,由于DateTime里面的大多数方法和DateUtil是相似的。DateTime类继承自java.util.Date,彻底能够替代Date的使用而且还有其余的实用方法!数据库
首先来了解一下DateTime的构造方法:
方法一:使用new
DateTime dt=new DateTime(); //建立当前时刻
DateTime dt=new DateTime(Date date); //使用Date对象构造
DateTime dt=new DateTime(long timeMillis); //使用毫秒数构造
方法二:使用of()方法
DateTime dt=DateTime.of();
方法三:使用now()方法
DateTime dt=DateTime.now(); //建立当前时刻
DateTime里面还有两个很是实用的方法,就是before(Date when):boolean 和 after(Date when):boolean,
它们能够判断时间的前后。
复制代码
最后了解一下DatePattern类,这个类主要是提供了一些指定的时间日期格式(都是String类型),包括地区的区别表示:
格式化模板:
DatePattern.CHINESE_DATE_PATTERN //yyyy年MM月dd日
DatePattern.NORM_DATE_PATTERN //yyyy-MM-dd
DatePattern.NORM_TIME_PATTERN //HH:mm:ss
DatePattern.NORM_DATETIME_PATTERN //yyyy-MM-dd HH:mm:ss
DatePattern.UTC_PATTERN //yyyy-MM-dd'T'HH:mm:ss'Z'
DatePattern.PURE_DATE_PATTERN //yyyyMMdd
DatePattern.PURE_TIME_PATTERN //HHmmss
DatePattern.PURE_DATETIME_PATTERN //yyyyMMddHHmmss
复制代码
做者:「612星球的一只天才猪」路小磊
原文连接:https://blog.csdn.net/tianc_pig/article/details/87826810
https://my.oschina.net/looly/blog/268552
来源:CSDN oschina
复制代码
=========================================================
字符串工具指cn.hutool.core.util.StrUtil
类,其中对String的多种方法进行了封装而且提供了其余方便实用的方法。StrUtil中的方法是静态方法。
这里的空有两层含义:一是null或者“”(空串),二是不可见字符构成的字符串(不可见字符串),好比由空格构成的字符串(“ ”)。针对两种状况Hutool提供了不一样的方法来解决。
相似于Apache Commons Lang
中的StringUtil,之因此使用StrUtil而不是使用StringUtil是由于前者更短,并且Str这个简写我想已经深刻人心。经常使用的方法, 例如isBlank、isNotBlank、isEmpty、isNotEmpty这些就不作介绍了,判断字符串是否为空,下面我说几个比较好用的功能。
经常使用的方法以下表所示:
isBlank(CharSequence arg0):boolean //判断字符串是否为null或者””或者是不可见字符串
isEmpty(charSequence arg0):boolean //判断字符串是否为null或者””
hasBlank(CharSequence…arg0):boolean //判断多个字符串中是否有null或者””或者是不可见字符串
hasEmpty(CharSequence…arg0):boolean //判断多个字符串中是否有null或者””
表单登陆时,经常使用hasEmpty()方法。
复制代码
sub()方法至关于subString()方法;
有三个参数:第一个是截取的字符串,后两个是首尾位置。sub()方法有一个最大的特色就是容错率很是高,而且-1表示最后一个字符,-2表示倒数第二个字符,以此类推。 而且首尾位置也能够颠倒。
String testStr="my name is smartPig";
String result01=StrUtil.sub(testStr, 0, 4); //my n
/*虽然是4-0,可是实际上仍是算成0-4*/
String result02=StrUtil.sub(testStr, 4, 0); //my n
String result03=StrUtil.sub(testStr, -1, 3); //name is smartPi
String result04=StrUtil.sub(testStr, -4, 3); //name is smar
复制代码
format方法相似于JDBC中PreparedStatement中的?占位符,能够在字符串中使用”{}”做为占位符。
String testStr="{} name is smart{}";
String result=StrUtil.format(testStr, "my","Pig"); //my对应第一个{},Pig对应第二个{}
//结果以下:my name is smartPig
复制代码
Hutool能够去除字符串的前缀/后缀,这个特色很是适用于去除文件的后缀名。相关方法以下表:
removeSuffix(CharSequence str, CharSequence suffix) //去除后缀
removeSuffixIgnoreCase(CharSequence str, CharSequence suffix) //去除后缀,忽略大小写
removePrefix(CharSequence str, CharSequence suffix) //去除前缀
removePrefixIgnoreCase(CharSequence str, CharSequence suffix) //去除前缀,忽略大小写
复制代码
Hutool定义了一些字符常量,能够灵活使用。部分常量以下所示:
复制代码
StrUtil.DOT //点.
StrUtil.DOUBLE_DOT //双点..
StrUtil.UNDERLINE //下划线_
StrUtil.EMPTY //横杠_
StrUtil.BACKSLASH //反斜杠\
StrUtil.DASHED //破折-
StrUtil.BRACKET_END //右中扩号]
StrUtil.BRACKET_START //左中括号[
StrUtil.COLON //冒号:
StrUtil.COMMA //逗号,
StrUtil.DELIM_END //右大括号}
StrUtil.DELIM_START //左大括号{
复制代码
Hutool能够很容易的实现字符串逆序。可使用StrUtil.reverse(String str):String方法。
复制代码
把String.getByte(String charsetName)方法封装在这里了,原生的String.getByte()这个方法太坑了,使用系统编码,
常常会有人跳进来致使乱码问题,因此就加了这两个方法强制指定字符集了,包了个try抛出一个运行时异常,
省的我得在业务代码里处理那个恶心的UnsupportedEncodingException。
复制代码
做者:「612星球的一只天才猪」路小磊
原文连接:https://blog.csdn.net/tianc_pig/article/details/87944463
https://my.oschina.net/looly/blog/262775
来源:CSDN oschina
复制代码
=========================================================
随机工具的主要方法以下:
RandomUtil.randomInt 得到指定范围内的随机数
RandomUtil.randomEle 随机得到列表中的元素
RandomUtil.randomString 得到一个随机的字符串(只包含数字和字符)
RandomUtil.randomNumbers 得到一个只包含数字的字符串
RandomUtil.randomNumber 得到一个随机数
RandomUtil.randomChar 得到随机字符
复制代码
这些方法都有一些重载方法,以下表:
randomInt():int //得到一个随机整数
randomInt(int limit):int //得到一个<limit的随机整数
randomInt(int min,int max):int //得到一个随机整数n(min<=n<max)
randomChar():char //得到一个随机字符
randomChar(Strint str):char //从指定字符串中得到一个随机字符
randomNumber():int //得到一个随机数
randomNumbers(int length):String //得到长度为length由数字组成的字符串
randomString(int length):String //得到一个长度为length的字符串,只包含数字和字符
randomString(String str,int length):String //从str中随机产生指定长度的字符串(全部字符来源于str)
randomEle(T array[]):T //从数组中随机获取一个数据
randomEle(List<T> list):T //从List中随机获取一个数据
randomEle(List<T> list,int limit):T //从List的前limit个中随机获取一个数据(limit从1开始)
复制代码
做者:「612星球的一只天才猪」
原文连接:https://blog.csdn.net/tianc_pig/article/details/88034976
来源:CSDN
复制代码
=========================================================
使用 isMatch(String regex,CharSequence content):boolean
第一个参数是正则,第二个是匹配的内容。
复制代码
delFirst(String regex,CharSequence content):String //删除匹配的第一个字符串,返回删除后的字符串
delAll(String regex,CharSequence content):String //删除匹配的全部字符串,返回删除后的字符串
复制代码
使用findAll(String regex, CharSequence content, int group): List<String>
能够得到全部匹配的内容
复制代码
使用replaceAll(CharSequence content, String regex, String replacementTemplate):String
能够替换匹配的内容。
复制代码
使用escape(CharSequence arg0):String
能够将用于正则表达式中的某些特殊字符进行转义,变成转义字符。
复制代码
做者:「612星球的一只天才猪」
原文连接:https://blog.csdn.net/tianc_pig/article/details/88134882
来源:CSDN
复制代码
=========================================================
PageUtil的静态方法
复制代码
transToStartEnd(int,int):int[] //将页数和页容量转换为数据表中的起始位置
/*第一个参数是页码数,第二个参数是每一页的容量, 返回值是一个长度为2的数组,分别表示开始位置和结束位置。*/
/*得到指定页的结果集 @param pageNum 页数(从1开始) @param pageSize 每页容量 int startEnd[]=PageUtil.transToStartEnd(pageNum, pageSize);*/
totalPage(int,int):int //由总记录数和页容量得到页数
//第一个参数是总的记录数,第二个参数是每页的容量,返回值是总页数。
/* @param totalCount 总记录数 @param pageSize 每页的记录数 int totalPage = PageUtil.totalPage(totalCount, pageSize);*/
彩虹分页算法
rainbow(int,int):int[]
//第一个参数表示当前页码,第二个参数表示总页数,这种状况默认是一次显示全部的页码
//(这个方法我的以为没有什么用)
rainbow(int,int,int):int[]
//第一个参数表示当前页码,第二个参数表示总页数,第三个参数表示每一次最多显示的页数
复制代码
彩虹分页算法应用于网页中须要常常显示【上一页】1 2 3 4 5 【下一页】这种页码的状况,通常都根据当前的页码动态的更新所显示出来的页码,由于每次所显示的页码的数量是固定的。
/** * 测试分页彩虹算法 * @param curPage 当前页数 * @param totalPage 总页数 */
public static String rainbowPage(int curPage,int totalPage) {
int rainbow[]=PageUtil.rainbow(curPage,totalPage);
return Arrays.toString(rainbow);
}
/** * 测试分页彩虹算法 * @param curPage 当前页数 * @param totalPage 总页数 * @param size 每次显示的页数量 */
public static String rainbowPage(int curPage,int totalPage,int size) {
int rainbow[]=PageUtil.rainbow(curPage,totalPage,size);
return Arrays.toString(rainbow);
}
复制代码
MyBatis就提供了分页工具,而且支持插件分页
做者:「612星球的一只天才猪」
原文连接:https://blog.csdn.net/tianc_pig/article/details/88628323
来源:CSDN
复制代码
=========================================================
主要增长了对数组、集合类的操做。
将集合转换为字符串,这个方法仍是挺经常使用,是StrUtil.split的反方法。这个方法的参数支持各类类型对象的集合,最后链接每一个对象时候调用其toString()方法。以下:
String[] col= new String[]{a,b,c,d,e};
String str = CollectionUtil.join(col, "#"); //str -> a#b#c#d#e
复制代码
功能是:将给定的多个集合放到一个列表(List)中,根据给定的Comparator对象排序,而后分页取数据。这个方法很是相似于数据库多表查询后排序分页,这个方法存在的意义也是在此。sortPageAll2功能和sortPageAll的使用方式和结果是 同样的,区别是sortPageAll2使用了BoundedPriorityQueue这个类来存储组合后的列表,不知道哪一种性能更好一些,因此就都保留了。以下:
Comparator<Integer> comparator = new Comparator<Integer>(){ //Integer比较器
@Override
public int compare(Integer o1, Integer o2) {
return o1.compareTo(o2);
}
};
//新建三个列表,CollectionUtil.newArrayList方法表示新建ArrayList并填充元素
List<Integer> list1 = CollectionUtil.newArrayList(1, 2, 3);
List<Integer> list2 = CollectionUtil.newArrayList(4, 5, 6);
List<Integer> list3 = CollectionUtil.newArrayList(7, 8, 9);
//参数表示把list1,list2,list3合并并按照从小到大排序后,取0~2个(包括第0个,不包括第2个),结果是[1,2]
@SuppressWarnings("unchecked")
List<Integer> result = CollectionUtil.sortPageAll(0, 2, comparator, list1, list2, list3);
System.out.println(result); //输出 [1,2]
复制代码
主要是对Entry<Long, Long>
按照Value的值作排序,使用局限性较大
这个方法传入一个栈对象,而后弹出指定数目的元素对象,弹出是指pop()方法,会从原栈中删掉。
这些方法是新建相应的数据结构,数据结构元素的类型取决于你变量的类型,以下:
HashMap<String, String> map = CollectionUtil.newHashMap();
HashSet<String> set = CollectionUtil.newHashSet();
ArrayList<String> list = CollectionUtil.newArrayList();
复制代码
在给定数组里末尾加一个元素,其实List.add()也是这么实现的,这个方法存在的意义是只有少许的添加元素时使用,由于内部使用了System.arraycopy,每调用一次就要拷贝数组一次。这个方法也是为了在某些只能使用数组的状况下使用,省去了先要转成List,添加元素,再转成Array。
从新调整数据的大小,若是调整后的大小比原来小,截断,若是比原来大,则多出的位置空着。(貌似List在扩充的时候会用到相似的方法)
将多个数据合并成一个数组
这个方法来源于Python的一个语法糖,给定开始和结尾以及步进,就会生成一个等差数列(列表)
int[] a1 = CollectionUtil.range(6); //[0,1,2,3,4,5]
int[] a2 = CollectionUtil.range(4, 7); //[4,5,6]
int[] a3 = CollectionUtil.range(4, 9, 2); //[4,6,8]
复制代码
对集合切片,其余类型的集合会转换成List,封装List.subList方法,自动修正越界等问题,彻底避免IndexOutOfBoundsException异常。
判断集合是否为空(包括null和没有元素的集合)。
此方法也是来源于Python的一个语法糖,给定两个集合,而后两个集合中的元素一一对应,成为一个Map。此方法还有一个重载方法,能够传字符,而后给定分隔符,字符串会被split成列表。
String[] keys = new String[]{"a", "b", "c"};
Integer[] values = new Integer[]{1, 2, 3};
Map<String, Integer> map = CollectionUtil.zip(keys,values);
System.out.println(map); // {b=2, c=3, a=1}
String a = "a,b,c";
String b = "1,2,3";
Map<String, String> map2 = CollectionUtil.zip(a,b, ",");
System.out.println(map2); // {b=2, c=3, a=1}
复制代码
做者:路小磊
原文连接:https://my.oschina.net/looly/blog/262786
来源:oschina
复制代码
=========================================================
主要是封装了一些反射的方法,而这个类中最有用的方法是scanPackage方法,这个方法会扫描classpath下全部类,这个在Spring中是特性之一,主要为Hulu框架中类扫描的一个基础
此方法惟一的参数是包的名称,返回结果为此包以及子包下全部的类。方法使用很简单,可是过程复杂一些,包扫面首先会调用 getClassPaths方法得到ClassPath,而后扫描ClassPath,若是是目录,扫描目录下的类文件,或者jar文件。若是是jar包,则直接从jar包中获取类名。这个方法的做用显而易见,就是要找出全部的类,在Spring中用于依赖注入,在Hulu中则用于找到Action类。固然,也能够传一个ClassFilter对象,用于过滤不须要的类。
此方法同Class对象的·getMethods·方法,只不过只返回方法的名称(字符串),封装很是简单。
此方法是得到当前线程的ClassPath,核心是Thread.currentThread().getContextClassLoader().getResources的调用。
此方法用于得到java的系统变量定义的ClassPath。
此方法封装了强制类型转换,首先会调用对象自己的cast方法,失败则尝试是否为基本类型(int,long,double,float等),再失败则尝试日期、数字和字节流,总之这是一个包容性较好的类型转换方法,省去咱们在不知道类型的状况下屡次尝试的繁琐。
此方法被parse方法调用,专门用于将字符集串转换为基本类型的对象(Integer,Double等等)。能够说这是一个一站式的转换方法,JDK的方法名太别扭了,例如你要转换成Long,你得调用Long.parseLong方法,直接Long.parse不就好了……真搞不懂,因此才有了这个方法。
这个方法比较别扭,就是把例如Integer类变成int.class,貌似没啥用处,忘了哪里用了,若是你能用到,就太好了。
后者只是得到当前线程的ClassLoader,前者在获取失败的时候获取ClassUtil这个类的ClassLoader。
实例化对象,封装了Class.forName(clazz).newInstance()方法。
克隆对象。对于有些对象没有实现Cloneable接口的对象想克隆下真是费劲,例如封装Redis客户端的时候,配置对象想克隆下基本不可能,因而写了这个方法,原理是使用ObjectOutputStream复制对象流。
做者:路小磊
原文连接:https://my.oschina.net/looly/blog/268087
来源:oschina
复制代码
=========================================================
在Java工具中,文件操做应该也是使用至关频繁的,可是Java对文件的操做因为牵涉到流,因此较为繁琐,各类Stream也是眼花缭乱,所以大部分项目里的util包中我想都有一个FileUtil的类,而本类就是对众多FileUtil的总结。
这些方法都是按照Linux命令来命名的,方便熟悉Linux的用户见名知意,例如:
ls 返回给定目录的全部文件对象列表,路径能够是相对ClassPath路径或者绝对路径,不能够是压缩包里的路径。
listFileNames 则是返回指定目录下的全部文件名,支持jar等压缩包。
touch 建立文件,若是给定路径父目录不存在,也一同建立。
del 删除文件或者目录,目录下有嵌套目录或者文件会一块儿删除。
mkdir 建立目录,父目录不存在自动建立。
createTempFile 建立临时文件,在程序运行完毕的时候,这个文件会被删除。
copy 复制文件或目录,目标文件对象能够是目录,自动用原文件名,能够选择是否覆盖目标文件。
move 移动文件或目录,原理是先复制,再删除原文件或目录
isExist 文件或者目录是否存在。
复制代码
getAbsolutePath 得到绝对路径,若是给定路劲已是绝对路径,返回原路径,不然根据ClassPath
或者给定类的相对位置得到其绝对位置
close 对于实现了Closeable接口的对象,能够直接调用此方法关闭,且是静默关闭,关闭出错将不
会有任何调试信息。这个方法也是使用很是频繁的,例如文件流的关闭等等。
equals 比较两个文件是否相同
复制代码
getBufferedWriter 得到带缓存的写入对象,能够写字符串等。
getPrintWriter 对 getBufferedWriter的包装,能够有println等方法按照行写出。
getOutputStream 会的文件的写出流想对象。
writeString直接写字符串到文件,会覆盖以前的内容。
appendString 追加字符串到文本。
writeLines appendLines 覆盖写入和追加文本列表,每一个元素都是一行。
writeBytes 写字节码。
writeStream 写流中的内容到文件里。
复制代码
getReader 得到带缓存的Reader对象。
readLines 按行读取文件中的数据,返回List,每个元素都是一行文本。
load 按照给定的ReaderHandler对象读取文件中的数据,ReaderHandler是一个借口,实现后就能够操做Reader对象了,这个方法存在是为了不用户手动调用close方法。
readString 直接读出文件中的全部文本。
readBytes 读字节码
复制代码
isModifed 文件是否被修改过,须要传入一个时间戳,用来比对最后修改时间。
getExtension 得到文件的扩展名。
复制代码
做者:路小磊
原文连接:https://my.oschina.net/looly/blog/288525
来源:oschina
复制代码
=========================================================
举个例子: 我有一个用户表,这个表根据用户名被Hash到不一样的数据库实例上,我要找出这些用户中最热门的5个,怎么作?我是这么作的:
在每一个数据库实例上找出最热门的5个
将每一个数据库实例上的这5条数据按照热门程度排序,最后取出前5条
复制代码
这个过程看似简单,可是你应用服务器上的代码要写很多。首先须要Query N个列表,加入到一个新列表中,排序,再取前5。这个过程不但代码繁琐,并且牵涉到多个列表,很是浪费空间。因而,BoundedPriorityQueu
e应运而生。
/** * 有界优先队列 */
public class BoundedPriorityQueueDemo {
public static void main(String[] args) {
//初始化队列,设置队列的容量为5(只能容纳5个元素),
//元素类型为integer使用默认比较器,在队列内部将按照从小到大排序
BoundedPriorityQueue<Integer> queue = new BoundedPriorityQueue<Integer>(5);
//初始化队列,使用自定义的比较器
queue = new BoundedPriorityQueue<>(5, new Comparator<Integer>(){
@Override
public int compare(Integer o1, Integer o2) {
return o1.compareTo(o2);
}
});
//定义了6个元素,当元素加入到队列中,会按照从小到大排序,
//当加入第6个元素的时候,队列末尾(最大的元素)将会被抛弃
int[] array = new int[]{5,7,9,2,3,8};
for (int i : array) {
queue.offer(i);
}
//队列能够转换为List
ArrayList<Integer> list = queue.toList();
System.out.println(queue);
}
}
原理: 设定好队列的容量,而后把全部的数据add或者offer进去(两个方法相同),就会获得前5条数据。
复制代码
做者:路小磊
原文连接:https://my.oschina.net/looly/blog/389940
来源:oschina
复制代码
=========================================================
Java中定时任务使用的最多的就是quartz了,可是这个框架太过庞大,并且也不须要用到这么多东西,使用方法也是比较复杂。因而便寻找新的框架代替。用过Linux的crontab的人都知道,使用其定时的表达式能够很是灵活的定义定时任务的时间以及频率(Linux的crontab精确到分,而quaeta的精确到秒,不过对来讲精确到分已经够用了,精确到秒的可使用Timer能够搞定),而后就是crontab的配置文件,能够把定时任务很清晰的罗列出来。(记得当时Spring整合quartz的时候那XML看的眼都花了)。因而我便找到了一个轻量调度框架——cron4j
为了隐藏这个框架里面的东西,对其作了封装,所谓封装,就是把任务调度放在一个配置文件里,而后启动便可(与Linux的crontab很是像)。
对于Maven项目,首先在src/main/resources/config下放入cron4j.setting文件(默认是这个路径的这个文件),而后在文件中放入定时规则,规则以下:
#注释
[包名]
TestJob = */10 * * * *
TestJob2 = */10 * * * *
第二行等号前面是要执行的定时任务类名,等号后面是定时表达式。
TestJob是一个实现了Runnable接口的类,在start()方法里就能够加逻辑。
复制代码
关于定时任务表达式,它与Linux的crontab表达式如出一辙,具体请看这里: www.cnblogs.com/peida/archi…
调用CronUtil.start()既可启动定时任务服务,CrontabUtil.stop()关闭服务。想动态的添加定时任务,使用CronUtil.schedule(String schedulingPattern, Runnable task)方法便可(使用此方法加入的定时任务不会被写入到配置文件)。
做者:路小磊
原文连接:https://my.oschina.net/looly/blog/379677
来源:oschina
复制代码
=========================================================
在Java开发中会面对各类各样的类型转换问题,尤为是从命令行获取的用户参数、从HttpRequest获取的Parameter等等,这些参数类型多种多样,怎么去转换他们呢?经常使用的办法是先整成String,而后调用xxx.parsexxx方法,还要承受转换失败的风险,不得不加一层try catch,这个小小的过程混迹在业务代码中会显得很是难看和臃肿,因而把这种类型转换的任务封装在了Conver类中。
1. toStr、toInt、toLong、toDouble、toBool方法
这几个方法基本代替了JDK的XXX.parseXXX方法,传入两个参数,第一个是Object类型的被转换的值,
第二个参数是默认值。这些方法作转换并不抛出异常,当转换失败或者提供的值为null时,
只会返回默认值,返回的类型所有使用了包装类,方便咱们须要null的状况。
2. 半角转全角toSBC和全角转半角toDBC
在不少文本的统一化中这两个方法很是有用,主要对标点符号的全角半角转换。
复制代码
做者:路小磊
原文连接:https://my.oschina.net/looly/blog/270829
来源:oschina
复制代码
=========================================================
使用单例不外乎两种方式:
在对象里加个静态方法getInstance()来获取。
此方式能够参考 【转】线程安全的单例模式[https://my.oschina.net/looly/blog/152865] 这篇博客,
可分为饿汉和饱汉模式。
经过Spring这类容器统一管理对象,用的时候去对象池中拿。Spring也能够经过配置决定懒汉或者饿汉模式
复制代码
说实话更倾向于第二种,可是Spring更对的的注入,而不是拿,因而想作Singleton这个类,维护一个单例的池,用这个单例对象的时候直接来拿就能够,这里用的懒汉模式。只是想把单例的管理方式换一种思路,但愿管理单例的是一个容器工具,而不是一个大大的框架,这样能大大减小单例使用的复杂性。
import com.xiaoleilu.hutool.Singleton;
/** * 单例样例 */
public class SingletonDemo {
/** * 动物接口 */
public static interface Animal{
public void say();
}
/** * 狗实现 */
public static class Dog implements Animal{
@Override
public void say() {
System.out.println("汪汪");
}
}
/** * 猫实现 */
public static class Cat implements Animal{
@Override
public void say() {
System.out.println("喵喵");
}
}
public static void main(String[] args) {
Animal dog = Singleton.get(Dog.class);
Animal cat = Singleton.get(Cat.class);
//单例对象每次取出为同一个对象,除非调用Singleton.destroy()或者remove方法
System.out.println(dog == Singleton.get(Dog.class)); //true
System.out.println(cat == Singleton.get(Cat.class)); //true
dog.say(); //汪汪
cat.say(); //喵喵
}
}
复制代码
若有兴趣能够看下这个类,实现很是简单,一个HashMap用于作为单例对象池,经过newInstance()实例化对象(不支持带参数的构造方法),不管取仍是建立对象都是线程安全的(在单例对象数量很是庞大且单例对象实例化很是耗时时可能会出现瓶颈),考虑到在get的时候使双重检查锁,可是并非线程安全的,故直接加了synchronized作为修饰符,欢迎在此给出建议。
做者:路小磊
原文连接:https://my.oschina.net/looly/blog/284922
来源:oschina
复制代码
=========================================================
对于JDK自带的Properties读取的Properties文件,有不少限制,首先是ISO8859-1编码致使无法加中文的value和注释(用日本的那个插件在Eclipse里能够读写,放到服务器上读就费劲了),再就是不支持变量分组等功能,所以有了Setting类。
配置文件中使用变量这个需求由来已久,在Spring中PropertyPlaceholderConfigurer类就用于在ApplicationContext.xml中使用Properties文件中的变量替换。 分组的概念我第一次在Linux的rsync的/etc/rsyncd.conf配置文件中有所了解,发现特别实用具体你们能够百度。
而这两种功能后来在jodd的Props才有所发现,它的这个配置文件扩展类十分强大,甚至支持多行等等功能,原本想直接使用,避免重复造轮子,但是发现不少特性彻底用不到,并且没有须要的便捷功能,因而便造了Setting这个轮子。
配置文件格式example.setting
<!-- lang: shell -->
# -------------------------------------------------------------
# ----- Setting File with UTF8-----
# ----- 数据库配置文件 -----
# -------------------------------------------------------------
#中括表示一个分组,其下面的全部属性归属于这个分组,在此分组名为demo,也能够没有分组
[demo]
#自定义数据源设置文件,这个文件会针对当前分组生效,用于给当前分组配置单独的数据库链接池参数,
#没有则使用全局的配置
ds.setting.path = config/other.setting
#数据库驱动名,若是不指定,则会根据url自动断定
driver = com.mysql.jdbc.Driver
#JDBC url,必须
url = jdbc:mysql://fedora.vmware:3306/extractor
#用户名,必须
user = root${driver}
#密码,必须,若是密码为空,请填写 pass =
pass = 123456
复制代码
配置文件能够放在任意位置,具体Setting类如何寻在在构造方法中提供了多种读取方式,具体稍后介绍。如今说下配置文件的具体格式, Setting配置文件相似于Properties文件,规则以下:
注释用#开头表示,只支持单行注释,空行和没法正常被识别的键值对也会被忽略,可做为注释,可是建议显式指定注释。
键值对使用key = value 表示,key和value在读取时会trim掉空格,因此不用担忧空格。
分组为中括号括起来的内容(例如配置文件中的[demo]),中括号如下的行都为此分组的内容,无分组至关于空字符分组,即[]。若某个key是name,分组是group,加上分组后的key至关于group.name。
支持变量,默认变量命名为{driver}会被替换为com.mysql.jdbc.Driver,为了性能,Setting建立的时候构造方法会指定是否开启变量替换,默认不开启。
<!-- lang: java -->
import java.io.IOException;
import com.xiaoleilu.hutool.CharsetUtil;
import com.xiaoleilu.hutool.FileUtil;
import com.xiaoleilu.hutool.Setting;
/** * Setting演示样例类 */
public class SettingDemo {
public static void main(String[] args) throws IOException {
//-------------------------初始化
//读取classpath下的XXX.setting,不使用变量
Setting setting = new Setting("XXX.setting");
//读取classpath下的config目录下的XXX.setting,不使用变量
setting = new Setting("config/XXX.setting");
//读取绝对路径文件/home/looly/XXX.setting(没有就建立,关于touch请查阅FileUtil)
//第二个参数为自定义的编码,请保持与Setting文件的编码一致
//第三个参数为是否使用变量,若是为true,则配置文件中的每一个key均可以被以后的条目中的value引用形式为 ${key}
setting = new Setting(FileUtil.touch("/home/looly/XXX.setting"), CharsetUtil.UTF_8, true);
//读取与SettingDemo.class文件同包下的XXX.setting
setting = new Setting("XXX.setting", SettingDemo.class, CharsetUtil.UTF_8, true);
//--------------------------使用
//获取key为name的值
setting.getString("name");
//获取分组为group下key为name的值
setting.getString("name", "group1");
//当获取的值为空(null或者空白字符时,包括多个空格),返回默认值
setting.getStringWithDefault("name", "默认值");
//完整的带有key、分组和默认值的得到值得方法
setting.getStringWithDefault("name", "group1", "默认值");
//若是想得到其它类型的值,能够调用相应的getXXX方法,参数类似
//有时候须要在key对应value不存在的时候(没有这项设置的时候)告知用户,故有此方法打印一个debug日志
setting.getWithLog("name");
setting.getWithLog("name", "group1");
//从新读取配置文件,能够启用一个定时器调用此方法来定时更新配置
setting.reload();
//当经过代码加入新的键值对的时候,调用store会保存到文件,可是会覆盖原来的文件,并丢失注释
setting.setSetting("name1", "value");
setting.store("/home/looly/XXX.setting");
//得到全部分组名
setting.getGroups();
//将key-value映射为对象,原理是原理是调用对象对应的setXX方法
//setting.toObject();
//设定变量名的正则表达式。
//Setting的变量替换是经过正则查找替换的,若是Setting中的变量名和其余冲突,
//能够改变变量的定义方式
//整个正则匹配变量名,分组1匹配key的名字
setting.setVarRegex("\\$\\{(.*?)\\}");
}
}
复制代码
对Properties的简单封装Props(版本2.0.0开始提供)
对于Properties的普遍使用使我也无能为力,有时候遇到Properties文件又想方便的读写也不容易,因而对Properties作了简单的封装,提供了方便的构造方法(与Setting一致),并提供了与Setting一致的getXXX方法来扩展Properties类,Props类继承自Properties,因此能够兼容Properties类,具体再也不作介绍,有兴趣能够看下cn.hutool.setting.dialect.Props
做者:路小磊
原文连接:https://my.oschina.net/looly/blog/302407
来源:oschina
复制代码
=========================================================
AES和DES同属对称加密算法,数据发信方将明文(原始数据)和加密密钥一块儿通过特殊加密算法处理后,使其变成复杂的加密密文发送出去。收信方收到密文后,若想解读原文,则须要使用加密用过的密钥及相同算法的逆算法对密文进行解密,才能使其恢复成可读明文。在对称加密算法中,使用的密钥只有一个,发收信双方都使用这个密钥对数据进行加密和解密,这就要求解密方事先必须知道加密密钥。
在Java世界中,AES、DES加密解密须要使用Cipher对象构建加密解密系统,Hutool中对这一对象作再包装,简化了加密解密过程。
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>4.1.2</version>
</dependency>
复制代码
** AES加密解密
String content = "test中文";
byte[] key = SecureUtil.generateKey(SymmetricAlgorithm.AES.getValue()).getEncoded();//随机生成密钥
AES aes = SecureUtil.aes(key);//构建
byte[] encrypt = aes.encrypt(content);//加密
byte[] decrypt = aes.decrypt(encrypt);//解密
String encryptHex = aes.encryptHex(content);//加密为16进制表示
String decryptStr = aes.decryptStr(encryptHex);//解密为原字符串
** DES加密解密, DES的使用方式与AES基本一致
String content = "test中文";
byte[] key = SecureUtil.generateKey(SymmetricAlgorithm.DES.getValue()).getEncoded();//随机生成密钥
DES des = SecureUtil.des(key);//构建
byte[] encrypt = des.encrypt(content);//加密解密
byte[] decrypt = des.decrypt(encrypt);
String encryptHex = des.encryptHex(content);//加密为16进制,解密为原字符串
String decryptStr = des.decryptStr(encryptHex);
复制代码
Hutool中针对JDK支持的全部对称加密算法作了封装,封装为SymmetricCrypto类,AES和DES两个类是此类的简化表示。经过实例化这个类传入相应的算法枚举便可使用相同方法加密解密字符串或对象。
Hutool支持的对称加密算法枚举有:
AES
ARCFOUR
Blowfish
DES
DESede
RC2
PBEWithMD5AndDES
PBEWithSHA1AndDESede
PBEWithSHA1AndRC2_40
复制代码
这些枚举所有在SymmetricAlgorithm中被列举
对称加密对象的使用也很是简单:
String content = "test中文";
byte[] key = SecureUtil.generateKey(SymmetricAlgorithm.AES.getValue()).getEncoded();//随机生成密钥
SymmetricCrypto aes = new SymmetricCrypto(SymmetricAlgorithm.AES, key);//构建
byte[] encrypt = aes.encrypt(content);//加密
byte[] decrypt = aes.decrypt(encrypt);//解密
String encryptHex = aes.encryptHex(content);//加密为16进制表示
String decryptStr = aes.decryptStr(encryptHex);//解密为字符串
复制代码
做者:路小磊
原文连接:https://my.oschina.net/looly/blog/1504160
来源:oschina
复制代码
Hutool工具是一个国产开源Java工具集,旨在简化Java开发中繁琐的过程,Hutool-crypto模块即是针对JDK加密解密作了大大简化。
此文主要介绍利用Hutool-crypto简化非对称加密解密。
对于非对称加密,最经常使用的就是RSA和DSA,在Hutool中使用AsymmetricCrypto对象来负责加密解密。
非对称加密有公钥和私钥两个概念,私钥本身拥有,不能给别人,公钥公开。根据应用的不一样,咱们能够选择使用不一样的密钥加密:
签名:使用私钥加密,公钥解密。用于让全部公钥全部者验证私钥全部者的身份而且用来防止私钥全部者发布的内容被篡改,
可是不用来保证内容不被他人得到。
加密:用公钥加密,私钥解密。用于向公钥全部者发布信息,这个信息可能被他人篡改,可是没法被他人得到。
复制代码
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>4.1.2</version>
</dependency>
复制代码
在非对称加密中,咱们能够经过AsymmetricCrypto(AsymmetricAlgorithm algorithm)构造方法,经过传入不一样的算法枚举,得到其加密解密器。
固然,为了方便,咱们针对最经常使用的RSA和DSA算法构建了单独的对象:RSA和DSA。
以RSA为例,介绍使用RSA加密和解密
在构建RSA对象时,能够传入公钥或私钥,当使用无参构造方法时,Hutool将自动生成随机的公钥私钥密钥对:
RSA rsa = new RSA();
rsa.getPrivateKey()//得到私钥
rsa.getPrivateKeyBase64()
rsa.getPublicKey()//得到公钥
rsa.getPublicKeyBase64()
byte[] encrypt = rsa.encrypt(StrUtil.bytes("我是一段测试aaaa", CharsetUtil.CHARSET_UTF_8),
KeyType.PublicKey);//公钥加密,私钥解密
byte[] decrypt = rsa.decrypt(encrypt, KeyType.PrivateKey);
Assert.assertEquals("我是一段测试aaaa", StrUtil.str(decrypt, CharsetUtil.CHARSET_UTF_8));
//私钥加密,公钥解密
byte[] encrypt2 = rsa.encrypt(StrUtil.bytes("我是一段测试aaaa", CharsetUtil.CHARSET_UTF_8), KeyType.PrivateKey);
byte[] decrypt2 = rsa.decrypt(encrypt2, KeyType.PublicKey);
Assert.assertEquals("我是一段测试aaaa", StrUtil.str(decrypt2, CharsetUtil.CHARSET_UTF_8));
复制代码
对于加密和解密能够彻底分开,对于RSA对象,若是只使用公钥或私钥,另外一个参数能够为null
有时候想自助生成密钥对能够:
KeyPair pair = SecureUtil.generateKeyPair("RSA");
pair.getPrivate();
pair.getPublic();
复制代码
自助生成的密钥对是byte[]形式,咱们可使用Base64.encode方法转为Base64,便于存储为文本。
固然,若是使用RSA对象,也可使用encryptStr和decryptStr加密解密为字符串
已知私钥和密文,如何解密密文?
String PRIVATE_KEY = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAIL7pbQ+5KKGYRhw7jE31hmA"
+ "f8Q60ybd+xZuRmuO5kOFBRqXGxKTQ9TfQI+aMW+0lw/kibKzaD/EKV91107xE384qOy6IcuBfaR5lv39OcoqNZ"
+ "5l+Dah5ABGnVkBP9fKOFhPgghBknTRo0/rZFGI6Q1UHXb+4atP++LNFlDymJcPAgMBAAECgYBammGb1alndta"
+ "xBmTtLLdveoBmp14p04D8mhkiC33iFKBcLUvvxGg2Vpuc+cbagyu/NZG+R/WDrlgEDUp6861M5BeFN0L9O4hz"
+ "GAEn8xyTE96f8sh4VlRmBOvVdwZqRO+ilkOM96+KL88A9RKdp8V2tna7TM6oI3LHDyf/JBoXaQJBAMcVN7fKlYP"
+ "Skzfh/yZzW2fmC0ZNg/qaW8Oa/wfDxlWjgnS0p/EKWZ8BxjR/d199L3i/KMaGdfpaWbYZLvYENqUCQQCobjsuCW"
+ "nlZhcWajjzpsSuy8/bICVEpUax1fUZ58Mq69CQXfaZemD9Ar4omzuEAAs2/uee3kt3AvCBaeq05NyjAkBme8SwB0iK"
+ "kLcaeGuJlq7CQIkjSrobIqUEf+CzVZPe+AorG+isS+Cw2w/2bHu+G0p5xSYvdH59P0+ZT0N+f9LFAkA6v3Ae56OrI"
+ "wfMhrJksfeKbIaMjNLS9b8JynIaXg9iCiyOHmgkMl5gAbPoH/ULXqSKwzBw5mJ2GW1gBlyaSfV3AkA/RJC+adIjsRGg"
+ "JOkiRjSmPpGv3FOhl9fsBPjupZBEIuoMWOC8GXK/73DHxwmfNmN7C9+sIi4RBcjEeQ5F5FHZ";
RSA rsa = new RSA(PRIVATE_KEY, null);
String a = "2707F9FD4288CEF302C972058712F24A5F3EC62C5A14AD2FC59DAB93503AA0FA17113A020EE4EA35EB53F"
+ "75F36564BA1DABAA20F3B90FD39315C30E68FE8A1803B36C29029B23EB612C06ACF3A34BE815074F5EB5AA3A"
+ "C0C8832EC42DA725B4E1C38EF4EA1B85904F8B10B2D62EA782B813229F9090E6F7394E42E6F44494BB8";
byte[] aByte = HexUtil.decodeHex(a);
byte[] decrypt = rsa.decrypt(aByte, KeyType.PrivateKey);
Assert.assertEquals("虎头闯杭州,多抬头看天,切勿只管种地", StrUtil.str(decrypt, CharsetUtil.CHARSET_UTF_8));
复制代码
做者:路小磊
原文连接:https://my.oschina.net/looly/blog/1523094
来源:oschina
复制代码
=========================================================
cn.hutool.core.bean
包下的DynaBean、BeanDesc、BeanDesc.PropDesc、BeanPath、BeanUtil
做者:豢龙先生
原文连接:https://blog.csdn.net/sanjun333/article/details/90645420
来源:CSDN
复制代码
=========================================================
使用Hutool爬取开源中国的开源资讯, 演示Hutool-http的http请求功能.
1.打开oschina的主页,咱们找到最显眼的开源资讯模块,而后点击“更多”,打开“开源资讯”板块。
使用Hutool-http配合ReUtil请求并提取页面内容很是简单,代码以下:
//请求列表页
String listContent = HttpUtil.get("http://www.oschina.net/action/ajax/get_more_news_list?newsType=&p=2");
//使用正则获取全部标题
List<String> titles = ReUtil.findAll("<span class=\"text-ellipsis\">(.*?)</span>", listContent, 1);
for (String title : titles) {
Console.log(title);//打印标题
}
第一行请求页面内容,第二行正则定位全部标题行并提取标题部分。
这里解释下正则部分:
ReUtil.findAll方法用于查找全部匹配正则表达式的内容部分,第二个参数1表示提取第一个括号(分组)中的内容,
0表示提取全部正则匹配到的内容。这个方法能够看下core模块中ReUtil章节了解详情。
<span class=\"text-ellipsis\">(.*?)</span>这个正则就是咱们上面分析页面源码后获得的正则,
其中(.*?)表示咱们须要的内容,.表示任意字符,*表示0个或多个,?表示最短匹配,整个正则的意思就是。
,以<span class=\"text-ellipsis\">开头,</span>结尾的中间全部字符,中间的字符要达到最短。
?的做用其实就是将范围限制到最小,否则</span>极可能匹配到后面去了。
复制代码
不得不说,抓取自己并不困难,尤为配合Hutool会让这项工做变得更加简单快速,而其中的难点即是分析页面和定位咱们须要的内容。
真正的内容抓取分为连个部分:
并且在抓取过程当中咱们也会遇到各类问题,包括但不限于:
做者:路小磊
原文连接:https://my.oschina.net/looly/blog/1575851
来源:oschina
复制代码
=========================================================
使用Hutool [生成和验证] 图形验证码
随着攻击防御作的愈来愈全面,而图形验证码又是一种简单有效的防攻击和防抓取手段,所以应用愈来愈广。而Hutool中抽象了验证码的实现,也提供了几个简单的验证码实现,从而大大减小服务端开发成本。
因为对验证码需求量巨大,且我以前项目中有所积累,所以在Hutool中加入验证码生成和校验功能。
了解Hutool的更多信息请访问:http://hutool.cn/
<dependency>
<groupId>com.xiaoleilu</groupId>
<artifactId>hutool-all</artifactId>
<version>3.2.3</version>
</dependency>
复制代码
验证码功能位于com.hutool.captcha
包中,核心接口为ICaptcha,此接口定义了如下方法:
createCode 建立验证码,实现类需同时生成随机验证码字符串和验证码图片
getCode 获取验证码的文字内容
verify 验证验证码是否正确,建议忽略大小写
write 将验证码写出到目标流中
复制代码
其中write方法只有一个OutputStream,ICaptcha实现类能够根据这个方法封装写出到文件等方法。
AbstractCaptcha为一个ICaptcha抽象实现类,此类实现了验证码文本生成、非大小写敏感的验证、写出到流和文件等方法,经过继承此抽象类只需实现createImage方法定义图形生成规则便可。
//定义图形验证码的长和宽
LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(200, 100);
//LineCaptcha lineCaptcha = new LineCaptcha(200, 100, 4, 150);
//图形验证码写出,能够写出到文件,也能够写出到流
lineCaptcha.write("d:/line.png");
//验证图形验证码的有效性,返回boolean值
lineCaptcha.verify("1234");
复制代码
//定义图形验证码的长、宽、验证码字符数、干扰元素个数
CircleCaptcha captcha = CaptchaUtil.createCircleCaptcha(200, 100, 4, 20);
//CircleCaptcha captcha = new CircleCaptcha(200, 100, 4, 20);
//图形验证码写出,能够写出到文件,也能够写出到流
captcha.write("d:/circle.png");
//验证图形验证码的有效性,返回boolean值
captcha.verify("1234");
复制代码
//定义图形验证码的长、宽、验证码字符数、干扰线宽度
ShearCaptcha captcha = CaptchaUtil.createShearCaptcha(200, 100, 4, 4);
//ShearCaptcha captcha = new ShearCaptcha(200, 100, 4, 4);
//图形验证码写出,能够写出到文件,也能够写出到流
captcha.write("d:/shear.png");
//验证图形验证码的有效性,返回boolean值
captcha.verify("1234");
复制代码
ICaptcha captcha = ...;
captcha.write(response.getOutputStream());
//Servlet的OutputStream记得自行关闭!
复制代码
做者:路小磊
原文连接:https://my.oschina.net/looly/blog/1591079
来源:oschina
复制代码
=========================================================
cron模块,用于定时发送邮件
extra模块,MailUtil,用于发送邮件
poi模块,WordWriter,用于生成日报的word
复制代码
demo项目能够访问码云地址获取:https://gitee.com/loolly_admin/daily-work
src/main/java
cn/hutool/example/dailyWork/
DailyWorkGenerator.java ---- 日报生成器,用于生成Word文档
MailSendTask.java ---- 邮件发送任务,用于发送邮件
DailyWorkMain.java ---- 定时任务主程序,用于启动定时任务
src/main/resources
config/
cron.setting ---- 定时任务配置文件
mail.setting ---- 邮箱配置文件
复制代码
1.生成日报Word
/** 标题字体 */
private static final Font TITLE_FONT = new Font("黑体", Font.PLAIN, 22);
/** 正文字体 */
private static final Font MAIN_FONT = new Font("宋体", Font.PLAIN, 14);
/** * 生成日报 * * @return 日报word文件 */
public static File generate() {
// 一、准备文件
File wordFile = FileUtil.file(StrUtil.format("每日工做汇报_{}.docx", DateUtil.today()));
if(FileUtil.exist(wordFile)) {
// 若是文件存在,删除之(可能上次发送遗留)
wordFile.delete();
}
// 生成并写出word
Word07Writer writer = new Word07Writer(wordFile);
writer.addText(ParagraphAlignment.CENTER, TITLE_FONT, "工做日报");
writer.addText(MAIN_FONT, "");
writer.addText(MAIN_FONT, "尊敬的领导:");
writer.addText(MAIN_FONT, " 今天我在Hutool群里摸鱼,什么工做也没作。");
writer.close();
return wordFile;
}
复制代码
2.发送邮件在mail.setting中配置发件箱信息
# 发件人(必须正确,不然发送失败)
from = Hutool<hutool@yeah.net>
# 用户名(注意:若是使用foxmail邮箱,此处user为qq号)
user = hutool
# 密码
pass = XXXX
#使用 STARTTLS安全链接
startttlsEnable = true
#使用 SSL安全链接
sslEnable = true
=========================================================================
// 今天的日期,格式相似:2019-06-20
String today = DateUtil.today();
// 生成汇报Word
File dailyWorkDoc = DailyWorkGenerator.generate();
// 发送邮件
MailUtil.sendHtml("hutool@foxmail.com", StrUtil.format("{} 工做日报", today), "请见附件。", dailyWorkDoc);
StaticLog.debug("{} 工做日报已发送给领导!", today);
复制代码
3.定时发送,将刚才的发送邮件做为定时任务加入到配置文件:
[cn.hutool.example.dailyWork]
# 天天下午6点定时发送
MailSendTask.execute = 00 00 18 * * *
复制代码
4.启动定时任务
// 设置秒匹配(只有在定时任务精确到秒的时候使用)
CronUtil.setMatchSecond(true);
// 启动定时任务,自动加载配置文件中的内容
CronUtil.start();
复制代码
5.结果
做者:路小磊
原文连接:https://my.oschina.net/looly/blog/3064203
来源:oschina
复制代码
=========================================================
* Slf4j
全称Simple Logging Facade for JAVA,真正的日志门面,只提供接口方法,当配合特定的日志实现时,
须要引入相应的桥接包
* Common-logging
Apache提供的一个通用的日志接口,common-logging会经过动态查找的机制,在程序运行时自动找出真正
使用的日志库,本身也自带一个功能很弱的日志实现。
* 差异:
Common-logging 动态查找日志实现(程序运行时找出日志实现),
Slf4j 则是静态绑定(编译时找到实现),动态绑定由于依赖ClassLoader寻找和载入日志实现,所以相似于
OSGI那种使用独立ClassLoader就会形成没法使用的状况。
Slf4j 支持参数化的log字符串,避免了以前为了减小字符串拼接的性能损耗而不得不写的
if(logger.isDebugEnable()),如今能够直接写:logger.debug(“current user is: {}”, user)。
复制代码
Log4j
Log4j多是Java世界里最出名的日志框架了,支持各类目的地各类级别的日志输出。
LogBack
Log4j做者的又一力做(据说是受不了收费文档搞了个开源的,不须要桥接包完美适配Slf4j),感受迄今为止最棒的日志框架了,一直都在用,配置文件够简洁,性能足够好。
JDK Logging 从JDK1.4开始引入,不得不说,你去Google下这个JDK自带的日志组件,并不如Log4j和LogBack之类好用,没有配置文件,日志级别很差理解,想顺心的用估计还得本身封装下,总之你们已经被Log4j惯坏了,JDK的设计并不能被你们认同,惟一的优势想就是不用引入新额jar包。
看了以上介绍,若是你不是混迹(深陷)Java多年的老手,估计会蒙圈儿了吧,那你确定会问,要门面干吗。有了手机就有手机壳、手机膜,框架也同样,门面的做用更多的仍是三个字:解耦合。说白了,加入一个项目用了一个日志框架,想换咋整啊?那就一行一行的找日志改呗,想一想都是噩梦。因而,门面出来了,门面说啦, 你用个人格式写日志,把日志想写哪儿写哪儿,例如Slf4j-api加上后,想换日志框架,直接把桥接包一换就行。方便极了。
说实话,如今Slf4j基本能够是Java日志的一个标准了,按照它写基本能够实现全部日志实现通吃,可是就有人不服,还写了门面的门面(没错,这我的就是我)。
若是看过Netty的源码,推荐你看下io.netty.util.internal.logging这个包里内容,会发现Netty又对日志封装了一层。
不管是Netty的日志模块仍是Hutool-log模块,思想相似于Common Logging,作动态日志实现查找,
而后找到相应的日志实现来写入日志,核心代码以下:
/** * 决定日志实现 * @return 日志实现类 */
public static Class<? extends AbstractLog> detectLog(){
List<Class<? extends AbstractLog>> logClassList = Arrays.asList(
Slf4jLog.class,
Log4jLog.class,
Log4j2Log.class,
ApacheCommonsLog.class,
JdkLog.class
);
for (Class<? extends AbstractLog> clazz : logClassList) {
try {
clazz.getConstructor(Class.class).newInstance(LogFactory.class).info("Use Log Framework: [{}]", clazz.getSimpleName());
return clazz;
} catch (Error | Exception e) {
continue;
}
}
return JdkLog.class;
}
复制代码
按顺序实例化相应的日志实现,若是实例化失败(通常是ClassNotFoundException),说明jar不存在,那实例化下一个,经过不停的尝试,最终若是没有引入日志框架,那使用JDK Logging(这个确定会有的),固然这种方式也和Common-logging存在相似问题,不过不用到跨ClassLoader仍是很好用的。
对于JDK Logging,也作了一些适配,使之能够与Slf4j的日志级别作对应,这样就将各个日志框架差别化降到最小。另外一方面,若是你看过个人这篇日志,那你必定了解了个人类名自动识别功能,这样你们在复制类名的时候,就不用修改日志的那一行代码了,在全部类中,日志的初始化只有这一句:
Log log = LogFactory.get();
//=======实现方式 =======
/** * @return 得到调用者的日志 * 经过堆栈引用得到当前类名。 */
public static Log get() {
return getLog(new Exception().getStackTrace()[1].getClassName());
}
/** * 日志统一接口 */
public interface Log extends TraceLog, DebugLog, InfoLog, WarnLog, ErrorLog 这样就实现了单一使用,各个日志框架灵活引用的做用了。 复制代码
做者:路小磊
原文连接:https://my.oschina.net/looly/blog/543000
来源:oschina
复制代码
=========================================================