javaee论坛

普通会员

225648

帖子

344

回复

358

积分

楼主
发表于 2019-11-03 15:53:07 | 查看: 65 | 回复: 0

在Java8之前,所有关于时间和日期的API都存在各种使用方面的缺陷,因此建议使用新的时间和日期API,分别从旧的时间和日期的API的缺点以及解决方法、Java8新的时间和日期API进行讲解。

旧的时间和日期的API的缺陷

Java的java.util.Date和java.util.Calendar类易用性差,不支持时区,而且都不是线程安全的。

Date如果不格式化,打印出的日期可读性差。

ThuSep1213:47:34CST2019

可以使用SimpleDateFormat对时间进行格式化,但SimpleDateFormat是线程不安全的,SimpleDateFormat的format方法源码如下:

privateStringBufferformat(Datedate,StringBuffertoAppendTo,FieldDelegatedelegate){//Convertinputdatetotimefieldlistcalendar.setTime(date);booleanuseDateFormatSymbols=useDateFormatSymbols();for(inti=0;i<compiledPattern.length;){inttag=compiledPattern[i]>>>8;intcount=compiledPattern[i++]&0xff;if(count==255){count=compiledPattern[i++]<<16;count|=compiledPattern[i++];}switch(tag){caseTAG_QUOTE_ASCII_CHAR:toAppendTo.append((char)count);break;caseTAG_QUOTE_CHARS:toAppendTo.append(compiledPattern,i,count);i+=count;break;default:subFormat(tag,count,delegate,toAppendTo,useDateFormatSymbols);break;}}returntoAppendTo;}

其中calendar是共享变量,并且这个共享变量没有做线程安全控制。当多个线程同时使用相同的SimpleDateFormat对象【如用static修饰的SimpleDateFormat】调用format方法时,多个线程会同时调用calendar.setTime方法,可能一个线程刚设置好time值另外的一个线程马上把设置的time值给修改了导致返回的格式化时间可能是错误的。

在多并发情况下使用SimpleDateFormat需注意。

SimpleDateFormat除了format是线程不安全以外,parse方法也是线程不安全的。parse方法实际调用alb.establish(calendar).getTime()方法来解析,alb.establish(calendar)方法里主要完成了

重置日期对象cal的属性值使用calb中中属性设置cal返回设置好的cal对象

但是这三步不是原子操作,导致解析出来的时间可以是错误的。

Date对时间处理比较麻烦,比如想获取某年、某月、某星期,以及n天以后的时间,如果用Date来处理的话真是太难了,并且Date类的getYear、getMonth这些方法都被弃用了。

多线程并发如何保证线程安全

避免线程之间共享一个SimpleDateFormat对象,每个线程使用时都创建一次SimpleDateFormat对象=>创建和销毁对象的开销大

对使用format和parse方法的地方进行加锁=>线程阻塞性能差

使用ThreadLocal保证每个线程最多只创建一次SimpleDateFormat对象=>较好的方法

Java8新的时间和日期API

Java8的日期和时间类包含LocalDate、LocalTime、Instant、Duration以及Period,这些类都包含在java.time包中,Java8新的时间API的使用方式,包括创建、格式化、解析、计算、修改,下面我们看下如何去使用。

LocalDate只会获取年月日

//创建LocalDate//获取当前年月日LocalDatelocalDate=LocalDate.now();//构造指定的年月日LocalDatelocalDate1=LocalDate.of(2019,9,12);//获取年、月、日、星期几intyear=localDate.getYear();intyear1=localDate.get(ChronoField.YEAR);Monthmonth=localDate.getMonth();intmonth1=localDate.get(ChronoField.MONTH_OF_YEAR);intday=localDate.getDayOfMonth();intday1=localDate.get(ChronoField.DAY_OF_MONTH);DayOfWeekdayOfWeek=localDate.getDayOfWeek();intdayOfWeek1=localDate.get(ChronoField.DAY_OF_WEEK);

LocalTime只会获取时分秒

//创建LocalTimeLocalTimelocalTime=LocalTime.of(14,14,14);LocalTimelocalTime1=LocalTime.now();//获取小时inthour=localTime.getHour();inthour1=localTime.get(ChronoField.HOUR_OF_DAY);//获取分intminute=localTime.getMinute();intminute1=localTime.get(ChronoField.MINUTE_OF_HOUR);//获取秒intsecond=localTime.getMinute();intsecond1=localTime.get(ChronoField.SECOND_OF_MINUTE);

LocalDateTime获取年月日时分秒,相当于LocalDate+LocalTime

//创建LocalDateTimeLocalDateTimelocalDateTime=LocalDateTime.now();LocalDateTimelocalDateTime1=LocalDateTime.of(2019,Month.SEPTEMBER,10,14,46,56);LocalDateTimelocalDateTime2=LocalDateTime.of(localDate,localTime);LocalDateTimelocalDateTime3=localDate.atTime(localTime);LocalDateTimelocalDateTime4=localTime.atDate(localDate);//获取LocalDateLocalDatelocalDate2=localDateTime.toLocalDate();//获取LocalTimeLocalTimelocalTime2=localDateTime.toLocalTime();

Instant获取秒数,用于表示一个时间戳(精确到纳秒)

如果只是为了获取秒数或者毫秒数,可以使用System.currentTimeMillis()。

//创建Instant对象Instantinstant=Instant.now();//获取秒数longcurrentSecond=instant.getEpochSecond();//获取毫秒数longcurrentMilli=instant.toEpochMilli();

Duration表示一个时间段

//Duration.between()方法创建Duration对象LocalDateTimefrom=LocalDateTime.of(2017,Month.JANUARY,1,00,0,0);//2017-01-0100:00:00LocalDateTimeto=LocalDateTime.of(2019,Month.SEPTEMBER,12,14,28,0);//2019-09-1514:28:00Durationduration=Duration.between(from,to);//表示从from到to这段时间longdays=duration.toDays();//这段时间的总天数longhours=duration.toHours();//这段时间的小时数longminutes=duration.toMinutes();//这段时间的分钟数longseconds=duration.getSeconds();//这段时间的秒数longmilliSeconds=duration.toMillis();//这段时间的毫秒数longnanoSeconds=duration.toNanos();//这段时间的纳秒数

修改LocalDate、LocalTime、LocalDateTime、Instant。

LocalDate、LocalTime、LocalDateTime、Instant为不可变对象,修改这些对象对象会返回一个副本。

增加、减少年数、月数、天数等,以LocalDateTime为例:

LocalDateTimelocalDateTime=LocalDateTime.of(2019,Month.SEPTEMBER,12,14,32,0);//增加一年localDateTime=localDateTime.plusYears(1);localDateTime=localDateTime.plus(1,ChronoUnit.YEARS);//减少一个月localDateTime=localDateTime.minusMonths(1);localDateTime=localDateTime.minus(1,ChronoUnit.MONTHS);//通过with修改某些值//修改年为2020localDateTime=localDateTime.withYear(2020);localDateTime=localDateTime.with(ChronoField.YEAR,2020);//时间计算//获取该年的第一天LocalDatelocalDate=LocalDate.now();LocalDatelocalDate1=localDate.with(firstDayOfYear());

TemporalAdjusters包含许多静态方法,可以直接调用,以下列举一些:

方法名描述dayOfWeekInMonth返回同一个月中每周的第几天firstDayOfMonth返回当月的第一天firstDayOfNextMonth返回下月的第一天firstDayOfNextYear返回下一年的第一天firstDayOfYear返回本年的第一天firstInMonth返回同一个月中第一个星期几lastDayOfMonth返回当月的最后一天lastDayOfNextMonth返回下月的最后一天lastDayOfNextYear返回下一年的最后一天lastDayOfYear返回本年的最后一天lastInMonth返回同一个月中最后一个星期几next/previous返回后一个/前一个给定的星期几nextOrSame/previousOrSame返回后一个/前一个给定的星期几,如果这个值满足条件,直接返回

格式化时间

LocalDatelocalDate=LocalDate.of(2019,9,12);Strings1=localDate.format(DateTimeFormatter.BASIC_ISO_DATE);Strings2=localDate.format(DateTimeFormatter.ISO_LOCAL_DATE);//自定义格式化DateTimeFormatterdateTimeFormatter=DateTimeFormatter.ofPattern("yyyy-MM-dd");Strings3=localDate.format(dateTimeFormatter);

解析时间

LocalDatelocalDate1=LocalDate.parse("20190912",DateTimeFormatter.BASIC_ISO_DATE);LocalDatelocalDate2=LocalDate.parse("2019-09-12",DateTimeFormatter.ISO_LOCAL_DATE);总结

和SimpleDateFormat相比,DateTimeFormatter是线程安全的。

Instant的精确度更高,可以精确到纳秒级。

Duration可以便捷得到时间段内的天数、小时数等。

LocalDateTime能够快速地获取年、月、日、下一月等。

TemporalAdjusters类中包含许多常用的静态方法,避免自己编写工具类。


您需要登录后才可以回帖 登录 | 立即注册

触屏版| 电脑版

技术支持 历史网 V2.0 © 2016-2017