现在的位置: 首页 > 编程语言 > 正文

Java8新特性之日期时间对象及一些其他特性

2020年02月13日 编程语言 ⁄ 共 8467字 ⁄ 字号 评论关闭

日期时间对象

关于日期时间的操作可以分为两种:

转换:与字符串的互相转换,与时间戳的互相转换 计算:计算两个时间点之间的间隔、时间点与时间段的计算(计算下周N、下个月D日、去年M月D日等等)

Java8 提供了三个类:LocalDateLocalTimeLocalDateTime,它们的形式如 2020-01-0112:30:002020-01-01 12:30:00

创建对象

获取类对象的方法非常非常简单

LocalDate now = LocalDate.now();LocalDate ld = LocalDate.of(2019, 1, 1);// 获取年月日now.getYear();now.getMonthValue(); // 如果你调用了 now.getMonth() ,那么它将返回给你一个大写的英文月份单词now.getDayOfMonth();// 顾名应该思义getDayOfWeek();getDayOfYear(); // 设置年月日LocalDate ld1 = ld.withYear(2021); // 2021-01-01LocalDate ld2 = ld.withMonth(12); // 2019-12-01LocalDate ld3 = ld.withDayOfMonth(12); // 2019-12-12// 你可能会纳闷,既然是设置,为什么不用单词 set 呢,而用 with// 因为,set 操作一般是改变调用对象本身,没有返回值;// 而 with 是在调用对象基础上另外创建一个新对象,设置好值后返回,没有改变调用对象// 如果你是那个打破砂锅的孩子,你可能会问:为什么不能改变调用对象?// 因为 LocalDate 是 final 修饰的(final 人称 Java 界的自宫之刀)// 从物理的角度来讲,目前人类无法改变时间(穿越)// 如果你有 ld.withMonth(13) 这种反人类历法的操作,当然是会抛出异常的

LocalTime 和 LocalDateTime 都有类似于 LocalDate 的方法,这里就不一一列举了(因为我感觉自己越来越像 api 文档了)

Java8 API 官方文档直通车

转换

日期时间对象 和 字符串 之间的互相转换:

// LocalDateTime 对象 -> 字符串DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");LocalDateTime now = LocalDateTime.now();String dateTimeStr = now.format(dtf);System.out.println(dateTimeStr);// 字符串 -> LocalDateTime 对象String str = "2022-01-30 12:15:20";LocalDateTime dateTime = LocalDateTime.parse(str, dtf);System.out.println(dateTime);

DateTimeFormatter 类还提供一些现成的 formatter ,比如

DateTimeFormatter.BASIC_ISO_DATE ==> DateTimeFormatter.ofPattern("yyyyMMdd")DateTimeFormatter.ISO_LOCAL_DATE ==> DateTimeFormatter.ofPattern("yyyy-MM-dd")// 更多 formatter 可以 api 文档中查询

学习的本质,不在于记住哪些知识,而在于它触发了你的思考。—— 迈克尔·桑德尔

日期时间 和 时间戳 之间的互相转换:

// LocalDateTime 对象 -> 时间戳LocalDateTime now = LocalDateTime.now();// 获取系统默认时区ZoneId systemDefaultZoneId = ZoneId.systemDefault();Instant instant = now.atZone(systemDefaultZoneId).toInstant();long timestamp = instant.toEpochMilli();System.out.println(timestamp);// 时间戳 -> LocalDateTime 对象long timestamp2 = 1578919583784L;Instant instant2 = Instant.ofEpochMilli(timestamp2);LocalDateTime dateTime2 = LocalDateTime.ofInstant(instant2, systemDefaultZoneId);System.out.println(dateTime2);

“我不明白为什么要把时间戳搞得这么麻烦!”

另外:java.util.Date 与 java.time.LocalDateTime 之间的转换需要通过 Instant 实现,它俩都没有提供直接的转换方法

// 获取系统默认时区ZoneId systemDefaultZoneId = ZoneId.systemDefault();// Date 转为 LocalDateTimeDate date3 = new Date();Instant instant3 = date3.toInstant();LocalDateTime localDateTime3 = LocalDateTime.ofInstant(instant3, systemDefaultZoneId);// LocalDateTime 转为 DateInstant instant4 = now.atZone(systemDefaultZoneId).toInstant();Date date4 = Date.from(instant4);

还有:LocalDateTime 可以由 LocalDate 和 LocalTime 组成,也可以拆分成它俩

LocalDate nowLocalDate = LocalDate.now();LocalTime nowLocalTime = LocalTime.now();LocalDateTime nowLocalDateTime = LocalDateTime.of(nowLocalDate, nowLocalTime);nowLocalDateTime.toLocalDate();nowLocalDateTime.toLocalTime();

计算

计算时间点与时间点之间的间隔:

// 计算日期时间之间的间隔LocalTime startTime = LocalTime.now();LocalTime endTime = startTime.plusHours(1).plusMinutes(50);Duration duration = Duration.between(startTime, endTime);// 间隔秒数duration.getSeconds();// 间隔天数duration.toDays();// 间隔小时数duration.toHours();// 间隔分钟数duration.toMinutes();

Duration.between(start, end) 的参数可以是 LocalDateTime 、LocalTime

它只会返回一个整数(舍掉小数后的整数,等同于 floor()),不会返回 1小时50分钟 这样的形式如果你想要 y年M个月d天 H小时m分钟s秒 这种形式,或许你自己动手组装一下了

// 计算日期之间的间隔LocalDate startDate = LocalDate.now();LocalDate endDate = LocalDate.of(2031, 1, 1);Period pe = Period.between(startDate, endDate);pe.getYears();pe.getMonths();pe.getDays();

时间点与时间段的计算:

public LocalDateTime plusYears(long years) { LocalDate newDate = date.plusYears(years); return with(newDate, time); } public LocalDateTime plusMonths(long months) { LocalDate newDate = date.plusMonths(months); return with(newDate, time); } public LocalDateTime plusWeeks(long weeks) { LocalDate newDate = date.plusWeeks(weeks); return with(newDate, time); } public LocalDateTime plusDays(long days) { LocalDate newDate = date.plusDays(days); return with(newDate, time); } public LocalDateTime plusHours(long hours) { return plusWithOverflow(date, hours, 0, 0, 0, 1); } public LocalDateTime plusMinutes(long minutes) { return plusWithOverflow(date, 0, minutes, 0, 0, 1); } public LocalDateTime plusSeconds(long seconds) { return plusWithOverflow(date, 0, 0, seconds, 0, 1); } public LocalDateTime plusNanos(long nanos) { return plusWithOverflow(date, 0, 0, 0, nanos, 1); } public LocalDateTime minusYears(long years) { return (years == Long.MIN_VALUE ? plusYears(Long.MAX_VALUE).plusYears(1) : plusYears(-years)); } public LocalDateTime minusMonths(long months) { return (months == Long.MIN_VALUE ? plusMonths(Long.MAX_VALUE).plusMonths(1) : plusMonths(-months)); } public LocalDateTime minusWeeks(long weeks) { return (weeks == Long.MIN_VALUE ? plusWeeks(Long.MAX_VALUE).plusWeeks(1) : plusWeeks(-weeks)); } public LocalDateTime minusDays(long days) { return (days == Long.MIN_VALUE ? plusDays(Long.MAX_VALUE).plusDays(1) : plusDays(-days)); } public LocalDateTime minusHours(long hours) { return plusWithOverflow(date, hours, 0, 0, 0, -1); } public LocalDateTime minusMinutes(long minutes) { return plusWithOverflow(date, 0, minutes, 0, 0, -1); } public LocalDateTime minusSeconds(long seconds) { return plusWithOverflow(date, 0, 0, seconds, 0, -1); } public LocalDateTime minusNanos(long nanos) { return plusWithOverflow(date, 0, 0, 0, nanos, -1); }

看吧,加减年数、月数、天数、小时数、分钟数、秒数、毫秒数都有

想怎么用就怎么用,举个小例子:

LocalDateTime now = LocalDateTime.now();// 30年后的今天(我还要上班,还没退休)LocalDateTime after30Years = now.plusYears(30L);System.out.println(after30Years);// 347个月后(我就能还清贷款了 ╥﹏╥)LocalDateTime after348Months = now.plusMonths(347L);System.out.println(after348Months);// 11天后(就是除夕了)LocalDateTime after10Days = now.plusDays(10L);System.out.println(after10Days);// 8小时前(我在上班)LocalDateTime before8Hours = now.minusHours(8L);System.out.println(before8Hours);// 3分钟前(我开始听 Let it go 这首歌)LocalDateTime before3Before = now.minusMinutes(3L);System.out.println(before3Before);// 10秒前(写下下面这条代码)LocalDateTime before10Second = now.minusSeconds(10L);System.out.println(before10Second);

其实不用区分什么加减的,也可以用 plusXxx 做减法,只要传入负数参数就行了

另外这里还有一类需求,比如:

明年的感恩节是哪天?(每年11月的第四个星期四为感恩节)下周五是哪天?

这就要用到时间校正器 TemporalAdjuster这里容我先介绍一下 TemporalAdjuster,它是一个函数式接口,只有一个方法 adjustInto

@FunctionalInterfacepublic interface TemporalAdjuster { Temporal adjustInto(Temporal temporal);}

Temporal 是一个接口,LocalDateTime 、LocalDate 、 LocalTime 都是它的实现类

在 LocalDateTime 、LocalDate 、 LocalTime 中都有 with(TemporalAdjuster adjuster) 这个方法用来实现上面提到的另类需求。

// 下周五LocalDateTime now = LocalDateTime.now();LocalDateTime nextFriday = now.with(dt -> { // dt 是 `Temporal` 对象,但实质上是调用对象的类型 LocalDateTime dateTime = (LocalDateTime) dt; // 非常可惜,没有 withDayOfWeek() 这个方法,要不然就会非常方便了 int dayOfWeekValue = dateTime.getDayOfWeek().getValue(); int fridayValue = DayOfWeek.FRIDAY.getValue(); return dateTime.plusWeeks(1L) .plusDays(fridayValue - dayOfWeekValue);});System.out.println(nextFriday);// 明年的感恩节(明年11月第四个星期四)LocalDate thanksGivingDay = LocalDate.now().with( t -> { LocalDate d = (LocalDate) t; // 明年11月1日 LocalDate newDate = d.plusYears(1L).withMonth(11).withDayOfMonth(1); int dayOfWeekValue = newDate.getDayOfWeek().getValue(); int thursdayValue = DayOfWeek.THURSDAY.getValue(); long plusWeeks = dayOfWeekValue > thursdayValue ? 4L : 3L; return newDate.plusWeeks(plusWeeks) .plusDays(thursdayValue - dayOfWeekValue);});System.out.println(thanksGivingDay);

其实 TemporalAdjusters 提供了许多 TemporalAdjuster 对象,就像上一节 Stream 中 Collectors 之于 Collector 一样 。

使用 TemporalAdjusters 能够十分方便的实现上面的需求

// 下个周五LocalDateTime nextFriday = LocalDateTime.now().with(TemporalAdjusters.next(DayOfWeek.FRIDAY));System.out.println(nextFriday);// 明年的感恩节(明年11月第四个星期四)LocalDate date = LocalDate.now().plusYears(1L).withMonth(11);LocalDate thanksGivingDay = date.with(TemporalAdjusters.dayOfWeekInMonth(4, DayOfWeek.THURSDAY));System.out.println(thanksGivingDay);

注意: TemporalAdjusters.next(DayOfWeek day) 方法返回的是 接下来第一个周五,并不是我们一般理解的 下周五,比如说:今天 2020-01-13(周一),那么返回的就是 2020-01-17 四天后的周五。

另外 TemporalAdjusters 并不止提供了上面这2个方法,还有很多其他方法, API 文档 中给出了足够多的例子,一看就明白了。

建议有兴趣的同学,去阅读一些源码,参考 Java8 代码逻辑,然后用其他编程语言实现相同的日期时间操作,因为在其他编程中(比如 javaScript)也会经常用到日期时间的操作

其他特性

罗列出来表示我知道他们,但不表示我理解他们,所以 Let It Go!

接口中的默认方法

接口中的静态方法

Optional 类

Optional<String> op = Optional.of(str);

它是用来标识这个变量有可能为空。

如果一个变量有可能为空,Java8 之前我们每次使用这个变量时,都必须判断它是否为空。现在也是!!

Optional 并不能避免空指针异常,仅仅是表示标识变量可能为空。打个比方:

前面一条路上埋了地雷,但从表面上完全看不出来,除非我们走一步都扔石头试一下,否则说不准哪一步就炸了。而 Optional就是用来标识地雷位置的,我们知道了哪个位置有雷,就会绕着走,从而能够安全通过

另外 Optional 还提供了一个设置默认值的功能,挺好玩的。

Integer pageSize = null;// 以前我们设置默认值//pageSize = pageSize == null ? 10 : pageSize;//System.out.println(pageSize);// 使用 Optional 设置默认值pageSize = Optional.ofNullable(pageSize) .orElse(20);System.out.println(pageSize);// 自定义默认值Integer defaultPageSize = Optional.ofNullable(pageSize) .orElseGet(() -> { return new Integer(50); });

结语

Java8 新特性系列随便到此就结束了。

最最关键的,还是多看 Java 官方 API 文档!!

我在想可能我们被矫枉过正了。各种各样技术群最多的回答都是:去问百度!! 百度全知道吗?!

说实在的,在今天这个时代,是个人都能在网络上发表文章言论,然后大家再互相转载,假的都能成为真的!没有经过验证就转载的;在当时有效,现在过时了的;随便在文章中一个转载链接的 ... 比比皆是

总结

以上所述是小编给大家介绍的Java8 新特性之日期时间对象及一些其他特性,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

本文标题: Java8 新特性之日期时间对象及一些其他特性

以上就上有关Java8新特性之日期时间对象及一些其他特性的全部内容,学步园全面介绍编程技术、操作系统、数据库、web前端技术等内容。

抱歉!评论已关闭.