介绍一个强大易用的日期和时间库:Joda-Time

在 Java 中处理日期和时间是很常见的需求,基础的工具类就是我们熟悉的 Date 和 Calendar,之前我也曾经写过一篇文章利用这两个类,怎么处理时间,然而这些工具类的 api 使用并不是很方便和强大,于是就诞生了Joda-Time 这个专门处理日期时间的库。而且 Joda-Time 很优秀,用了他之后再也停不下来,其在 Java 8 出现前的很长时间内成为 Java 中日期时间处理的事实标准,用来弥补 JDK 的不足。项目中要想使用 Joda-Time 很简单,只需要引入依赖: <dependency> <groupId>joda-time</groupId> <artifactId>joda-time</artifactId> <version>2.9.9</version> </dependency> 下面说一些常用的例子,供平时参考 创建任意时间对象 DateTime dateTime=new DateTime(2018, 03, 31, 16, 57,55); 创建当前时间对象 DateTime dateTime=new DateTime(); 格式化时间输出 DateTime dateTime = new DateTime(); System.out.println(dateTime.toString("yyyy-MM-dd HH:mm:ss")); 解析文本格式时间 DateTimeFormatter format = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"); DateTime dateTime = DateTime.parse("2018-03-31 17:01:45", format); 在当前日期上加上 90 天并输出结果 DateTime dateTime = new DateTime(); System.out.println(dateTime.plusDays(90).toString("yyyy-MM-dd HH:mm:ss"); 获取今天的开始时间 DateTime nowTime = new DateTime(); DateTime startOfDay = nowTime.withTimeAtStartOfDay(); 获取今天的结束时间 DateTime nowTime = new DateTime(); DateTime endOfDay = nowTime.millisOfDay().withMaximumValue(); 计算两个日期的相隔天数 DateTime nowTime = new DateTime(); DateTime futureTime = new DateTime(2019, 10, 1, 0, 0, 0); Int days = Days.daysBetween(nowTime, futureTime).getDays(); 本月的第一天和最后一天 DateTime dateTime = new DateTime(); DateTime firstDateTimeOfMonth = dateTime.dayOfMonth().withMinimumValue().withTimeAtStartOfDay(); DateTime lastDateTimeOfMonth = dateTime.dayOfMonth().withMaximumValue().withTimeAtStartOfDay(); 上个月的第一天和最后一天 DateTime dateTime = new DateTime(); DateTime startDate = dateTime.plusMonths(-1).dayOfMonth().withMinimumValue().withTimeAtStartOfDay(); DateTime endDate = dateTime.plusMonths(-1).dayOfMonth().withMaximumValue().millisOfDay().withMaximumValue(); 在每天的6:30处理一些东西 DateTime dt=new DateTime().withHourOfDay(6).withMinuteOfHour(30).withSecondOfMinute(0); 在每月的7号的6:30处理一些东西 DateTime dt=new DateTime().withDayOfMonth(7).withHourOfDay(6).withMinuteOfHour(30).withSecondOfMinute(0); 在每年的8月的7号的6:30处理一些东西 DateTime dt=new DateTime().withMonthOfYear(8).withDayOfMonth(7).withHourOfDay(6).withMinuteOfHour(30).withSecondOfMinute(0); 获取年终 月、日、时、分、秒、毫秒 DateTime dt = new DateTime(); //获取当前时间的年 int year = dt.getYear(); //获取当前时间的月 int month = dt.getMonthOfYear(); //获取当前时间是一年中的第几天 int dayOfYear = dt.getDayOfYear(); //获取一个月中的天 int day = dt.getDayOfMonth(); //获取一周中的周几 int week = dt.getDayOfWeek(); //一天中的第几小时(取整) int hour = dt.getHourOfDay(); //获取星期年 int weekOfyear = dt.getWeekyear(); //当前时间的秒中的毫秒 int ms = dt.getMillisOfSecond(); //获取当前时间的秒 int second = dt.getSecondOfDay(); //获取当前时间的毫秒 long millis = dt.getMillis(); 判断时间跨度是否包含当前时间,某个时间 Interval interval = new Interval(new DateTime(2018, 1, 1, 0, 0, 0), new DateTime(2018, 3, 30, 0, 0, 0)); System.out.println(interval.containsNow()); boolean contained = interval.contains(new DateTime("2012-03-01")); 与JDK互转换 //通过jdk时间对象构造 Date date = new Date(); DateTime dateTime = new DateTime(date); Calendar calendar = Calendar.getInstance(); dateTime = new DateTime(calendar); // Joda-time 各种操作..... dateTime = dateTime.plusDays(1) // 增加天 .plusYears(1)// 增加年 .plusMonths(1)// 增加月 .plusWeeks(1)// 增加星期 .minusMillis(1)// 减分钟 .minusHours(1)// 减小时 .minusSeconds(1);// 减秒数 // 计算完转换成jdk 对象 Date date2 = dateTime.toDate(); Calendar calendar2 = dateTime.toCalendar(Locale.CHINA); 解析 CST 格式的时间字符串 DateTimeFormatter format = DateTimeFormat.forPattern("EEE MMM dd HH:mm:ss zzz yyyy"); DateTime dateTime = DateTime.parse("Fri Mar 30 00:00:00 CST 2018", format); 当然作为一个强大的 时间日期 处理工具类,他还有更多更强大的 API,可以参看: ...

March 31, 2018 · 2 分钟 · Bridge Li

记一次使用 lombok 小小的成长感悟

公司项目里面用了 lombok,感觉这个东西真是个好东西,然后公司也用的简单,所以也没仔细看文档就开始想当然的用了,然后就悲剧了,今天就记录一下这件事,写一下经验教训,具体怎么用,大家可以看最后的参考。 lombok 有一个很好用的注解:@Data,当时以为这个注解就是相当于:@Getter和@Setter,所以有一次要重写 equals 和 hashcode 方法,然后就让 IDE 自动生成了,当时也没仔细看生成的是什么样子,然后就发现了 bug,仔细一看生成的 equals 方法原来是这样的: package cn.bridgeli.demo; import lombok.Data; import java.util.Objects; /** * Created by bridgeli on 2018/2/25. */ @Data public class LomBokTest { private Integer id; private String username; @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } if (!super.equals(o)) { return false; } LomBokTest that = (LomBokTest) o; return Objects.equals(id, that.id); } @Override public int hashCode() { return Objects.hash(super.hashCode(), id); } } 当时还以为是 idea 的 bug,因为是 idea 自动生成的,之前用 eclipse 自动生成从没问题,eclipse 自动生成的是这样的: ...

February 25, 2018 · 2 分钟 · Bridge Li

NullPointerException in Java with no StackTrace

这周一个项目遇到一个问题,同事查看日志发现抛出:NullPointerException,却没有堆栈信息,然后同事感觉很奇怪,因为打日志的方法,打印的确实是:e,而不是很多人不明所以的打印的:e.getMessage()。然后我看了一下想起来我看过某本书上说过的,JIT 优化。当某个异常抛出很多次之后,由于 Java 虚拟机 JIT 优化,会省略堆栈信息。往上面翻日志肯定可以会找到报错的地方,当然会出现报错的信息太多,比较难翻。写这篇文章的本来想找找那本书,参考一下的,结果忘了是那本书了,一时没找到,不过这个问题虽然不是非常常见,但是网上还是有很多说明的,所以就简单说说 JVM 有一个参数:OmitStackTraceInFastThrow 来控制是否开启此优化。关于此参数的简单说明: JVM参数-XX:-OmitStackTraceInFastThrow参数可以关掉JVM对堆栈信息的优化。如果设置了这个参数,那么异常堆栈就能完整输出了。 “在服务器中的VM编译器现在提供准确的所有的“冷”内置异常堆栈回溯功能。为了性能考虑,当这些异常被抛出很多次时,这个方法会被重新编译,此后编译器将使用一种更快的抛出异常的方式,即抛出预先分配好的不带堆栈信息的异常。要完全关闭掉这种预分配的异常,就需要使用-XX:-OmitStackTraceInFastThrow参数。” 相关stackoverflow讨论:https://stackoverflow.com/questions/2411487/nullpointerexception-in-java-with-no-stacktrace 笨神小程序 JVMPocket 对此参数的解释截图: 另,看到网上有部分人建议关闭此参数,个人是建议的: 由于Java 在虚拟机中运行,天生是比 c 要慢的,为了优化此问题,那些 JVM 大神们搞出了 JIT 优化,对性能有了大幅提升。 异常信息使我们应该特别关注了,当出现了异常应该尽快发现问题解决问题,而不是这个异常发生很多次了但是一直不知道。 打详细堆栈信息是很影响性能的,如果这个异常出现很多次了,没必要每次都打出来异常,看一个地方就知道了,而不是让他一直打,影响性能。 那有人说了当遇到此问题时,堆栈信息看不到,日志又比较难翻怎么办?我们好像也不能动态的添加 JVM 的启动参数,所以保险起见还是关闭此参数。个人认为:这个异常这么常出现,而且是 JIT 的优化导致看不到堆栈信息了,可以找一台机器重启一下就好了,没必要因小失大,我们从笨神的小程序也可以看到 JVM 默认是开启此参数的,开启肯定是有其道理的。

January 7, 2018 · 1 分钟 · Bridge Li

你假笨JVM参数 – 007 UseGCLogFileRotation NumberOfGCLogFiles GCLogFileSize

你假笨的第七次分享,也是你假笨在 2017 年的最后一次关于 JVM 的分享: 序号:007 时间:2017-08-10 参数: -XX:UseGCLogFileRotation -XX:NumberOfGCLogFiles -XX:GCLogFileSize 含义: 这次分享了3个设置滚动记录GC日志的参数 通过参数-Xloggc:xxx可指定GC日志文件路径 普通情况下,GC日志文件内容会不断积累,进程重启后日志文件会被覆盖 这次分享的3个参数在设置-Xloggc参数的前提下有效 -XX:UseGCLogFileRotation Enabled GC log rotation, requires -Xloggc. 打开或关闭GC日志滚动记录功能,要求必须设置 -Xloggc参数 -XX:NumberOfGCLogFiles Set the number of files to use when rotating logs, must be >= 1. The rotated log files will use the following naming scheme, .0, .1, …, .n-1. 设置滚动日志文件的个数,必须大于1 日志文件命名策略是,.0, .1, …, .n-1,其中n是该参数的值 -XX:GCLogFileSize The size of the log file at which point the log will be rotated, must be >= 8K. 设置滚动日志文件的大小,必须大于8k 当前写日志文件大小超过该参数值时,日志将写入下一个文件 ...

December 31, 2017 · 1 分钟 · Bridge Li

你假笨JVM参数 – 006 ExplicitGCInvokesConcurrent

你假笨的第六次分享: 序号:006 时间:2017-07-31 参数:-XX:ExplicitGCInvokesConcurrent 含义: Enables invoking of concurrent GC by using the System.gc() request. This option is disabled by default and can be enabled only together with the -XX:+UseConcMarkSweepGC option. System.gc()是正常FULL GC,会STW 打开此参数后,在做System.gc()时会做background模式CMS GC,即并行FULL GC,可提高FULL GC效率 注,该参数在允许systemGC且使用CMS GC时有效 举例: -XX:+ExplicitGCInvokesConcurrent 相关参数: -XX:DisableExplicitGC 控制是否允许System.gc(),默认允许 小程序截图: 分享记录:

December 23, 2017 · 1 分钟 · Bridge Li

你假笨JVM参数 – 005 CMSScavengeBeforeRemark

你假笨的第五次分享: 序号:005 时间:2017-07-24 参数:-XX:CMSScavengeBeforeRemark 含义: Enable scavenging attempts before the CMS remark step. 开启或关闭在CMS重新标记阶段之前的清除(YGC)尝试 CMS并发标记阶段与用户线程并发进行,此阶段会产生已经被标记了的对象又发生变化的情况,若打开此开关,可在一定程度上降低CMS重新标记阶段对上述“又发生变化”对象的扫描时间,当然,“清除尝试”也会消耗一些时间 注,开启此开关并不会保证在标记阶段前一定会进行清除操作 小程序截图: 分享记录:

December 17, 2017 · 1 分钟 · Bridge Li

你假笨JVM参数 – 004 MaxTenuringThreshold

你假笨的第四次分享: 序号:004 时间:2017-07-21 参数:-XX:MaxTenuringThreshold 含义: Sets the maximum tenuring threshold for use in adaptive GC sizing. The largest value is 15. The default value is 15 for the parallel (throughput) collector, and 6 for the CMS collector. 在可自动调整对象晋升老年代年龄阈值的GC中,该参数用于设置上述年龄阈值的最大值 参数值最大为15 Parallel Scavenge中默认值为15,CMS中默认值为6,G1中默认值为15 小程序截图: 分享记录:

December 10, 2017 · 1 分钟 · Bridge Li

你假笨JVM参数 – 003 CompileCommand

你假笨的第三次分享: 序号:003 时间:2017-07-19 参数:-XX:CompileCommand 含义: Specifies a command to perform on a method. 该参数用于定制编译需求,比如过滤某个方法不做JIT编译 若未指定方法描述符,则对全部同名方法执行命令操作,具体如何指定见下文[举例] 可使用星号通配符(*)指定类或方法,具体如何使用见下文[举例] 该参数可多次指定,或使用 换行符(\n)分隔参数后的多个命令 解析完该命令后,JIT编译器会读取.hotspot_compiler文件中的命令,该参数也可写在.hotspot_compiler文件中 可使用-XX:CompileCommandFile指定.hotspot_compiler文件为其他文件 用法: -XX:CompileCommand=command,method[,option] 命令: exclude,跳过编译指定的方法 compileonly,只编译指定的方法 inline/dontinline,设置是否内联指定方法 print,打印生成的汇编代码 break,JVM以debug模式运行时,在方法编译开始处设置断点 quiet,不打印在此命令之后、通过-XX:CompileCommand指定的编译选项 log,记录指定方法的编译日志,若未指定,则记录所有方法的编译日志 其他命令,option,help 举例: 设置编译器跳过编译com.jvmpocket.Dummy类test方法的4种写法 -XX:CompileCommand=exclude,com/jvmpocket/Dummy.test -XX:CompileCommand=exclude,com/jvmpocket/Dummy::test -XX:CompileCommand=exclude,com.jvmpocket.Dummy::test -XX:CompileCommand="exclude com/jvmpocket/Dummy test" 设置编译器只跳过编译java.lang.String类int indexOf(String)方法 -XX:CompileCommand=“exclude,java/lang/String.indexOf,(Ljava/lang/String;)I” 设置编译器跳过编译所有类的indexOf方法 -XX:CompileCommand=exclude,*.indexOf 小程序截图: 分享记录:

November 25, 2017 · 1 分钟 · Bridge Li

MySQL : The last packet successfully received from the server was XXX milliseconds ago

14年毕业写完论文没事干的时候,自己玩微信公众号开发,当时想做一个自然语言交互,其实就是想试一下lucene,但是当时建索引的时候偶尔会报这个错,一致不知道具体原因,去网上搜索但是天下文章一大抄,你抄我来我抄他,也没找到原因,后来因为工作中也没遇到过,感觉应该是自己当时水平不行就忘了这件事,前几天 fatsjson 和 druid 的作者温少突然在一个群里面说有人通过阿里工单反馈这个问题,他给追踪了一下,找到了原因,原来还是还是有人遇到这个问题,今天记录一下,希望对遇到这个问题的小伙伴有帮助,报错的信息大概就是: Caused by: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: The last packet successfully received from the server was 20,820,001 milliseconds ago. The last packet sent successfully to the server was 20,820,002 milliseconds ago. is longer than the server configured value of 'wait_timeout'. You should consider either expiring and/or testing connection validity before use in your application, increasing the server configured values for client timeouts, or using the Connector/J connection property 'autoReconnect=true' to avoid this problem. at sun.reflect.GeneratedConstructorAccessor29.newInstance(Unknown Source) ………… 下面是温少分享的截图 ...

November 11, 2017 · 1 分钟 · Bridge Li

你假笨说JVM参数 – 002 StringTableSize

没想到距离第一次整理你假笨的分享已经过去两个多月了,近期会继续整理一系列你假笨关于JVM参数的分享,下面是第二次: 序号:002 时间:2017-07-14 参数:-XX:StringTableSize 含义:Number of buckets in the interned String table String.intern() 被调用时会往 Hashtable 插入一个 String(若该 String 不存在),这里的 Table 就是 StringTable,此参数就是这个 StringTable 的大小,若此参数设置过小,明显的问题就是过多的hash碰撞,造成在查找字符串时比较消耗 CPU 资源 JDK 1.6 起,当冲突次数超过 100 次会自动 rehash,即便如此,若此参数设置过小会导致不断的 rehash,依然会过度消耗 CPU 资源 建议将此参数设置的值稍大一些,以减少 hash 冲突 使用方法:-XX:ReservedCodeCacheSize=__ 小程序截图: 分享记录: 感谢你假笨

November 5, 2017 · 1 分钟 · Bridge Li