掌握Java 8日期时间API:系统开发必备的Java API与框架使用指南
课程目标包括:了解重要的 Java API,并且要掌握一些必备框架的使用,这些内容在系统开发中是标配,是需要掌握的。

日期时间API
Java 8 发布了新的 Date-Time API(JSR 310),以此来进一步强化对日期与时间的处理。
- 格式转换不够方便。
Java 8 在 java.time 包中提供了许多新的 API。其中有两个 API 较为重要,分别如下:
新的 java.time 包包含了处理时钟(clock)的操作。
之前的日期时间 API 状况不佳,通常大家都会自行编写 DateTimeUtil 工具类。后来出现了 Joda-Time 开源项目,它确实为广大程序员解决了大问题。(这就是为什么程序员不仅要会使用框架和 Java API,还需要明白其原理。如果底层本身存在缺陷或实现不好,我们最起码要知道,当然我们也可以对其进行增强或自己实现。)
LocalDate 和 LocalTime 类以及 LocalDateTime 类在处理时区并非必须的情况下可以被使用。以下是代码示例:
import java.time.LocalDate;
import java.time.LocalTime;
引入 Java 中的时间类 LocalDateTime;使用 Java 中的时间类 LocalDateTime;导入 Java 里的时间类 LocalDateTime;将 Java 中的时间类 LocalDateTime 进行导入;利用 Java 中的时间类 LocalDateTime;借助 Java 中的时间类 LocalDateTime
import java.time.Month;
public class Java8Tester {
公共静态的 void 主函数(字符串数组 args){
创建了一个名为 Java8Tester 的对象 java8tester,其类型为 Java8Tester,通过 new 关键字进行实例化。
java8tester 对本地日期时间进行了测试。
}
该方法的目的是检验本地日期时间的相关功能和特性。
// 获取当前的日期时间
LocalDateTime 现在的时间 = LocalDateTime.now();
System.out 会打印出 "当前时间: " 以及 currentTime 的值。
当前时间转换为本地日期,得到的本地日期记为 date1 。
System 输出了 "date1: " 与 date1 进行拼接后的内容。
当前时间获取到的月份为 month,这个 month 就是具体的月份数值。
获取当前时间的月份中的日期,将其赋值给变量 day,即 day 等于 currentTime 获取到的月份中的日期。
当前时间的秒数为 currentTime.getSecond() 所获取的值,将其赋值给整数 seconds 。
System.out.println("秒数为: " + seconds);
LocalDateTime date2;currentTime 的日期设置为 10 号,年份设置为 2012 年,得到 date2。
System 输出了 "date2: " 与 date2 进行拼接后的内容。
// 12 december 2014
LocalDate 是一个日期类,通过调用其 of 方法来创建一个特定日期的对象。这里创建的日期是 2014 年 12 月 12 日,其中月份使用了 Month.DECEMBER 来表示 12 月,然后将年、月、日作为参数传递给 of 方法,最终得到了 date3 这个 LocalDate 对象。
System.out 会打印出 "date3: " 以及 date3 的值。
// 22 小时 15 分钟
LocalTime 被设定为 22 时 15 分。 LocalTime 等于 22 点和 15 分组成的时间。 LocalTime 是由 22 以及 15 构成的时间。 LocalTime 为 22 时 15 分所对应的时间。 LocalTime 被设定成 22 点 15 分的那个时间。
System.out 输出 "date4: " 与 date4 进行拼接后的内容
// 解析字符串
LocalTime 为 date5 ,其值是通过 "20:15:30" 解析得到的。
System.out 会打印出 "date5: " 与 date5 相连的内容
}
}
如果我们需要考虑到时区,就可以使用时区的日期时间API:
引入 Java 中的时间类 ZonedDateTime ;
import java.time.ZoneId;
public class Java8Tester {
public static void main(String args[]){
Java8Tester java8tester = new Java8Tester();
java8tester 对 ZonedDateTime 进行测试。
}
通过该方法可以验证 ZonedDateTime 的各种功能和特性。
// 获取当前时间日期
ZonedDateTime 这个变量名为 date1 的对象,是通过 ZonedDateTime.parse 方法来进行解析的,解析的字符串内容为"2015-12-03T10:15:30+05:30[Asia/Shanghai]"。
System.out.println("date1: " + date1);
ZoneId 的 id 为 "Europe/Paris",通过 ZoneId.of 方法获取到该 ZoneId
System.out.println("区域 ID:" + id);
获取系统默认的时区,将其赋值给 ZoneId 类型的变量 currentZone,即 ZoneId currentZone 等于 ZoneId.systemDefault() 所返回的时区对象。
System.out 会打印出 "当期时区: " 与 currentZone 进行拼接后的内容。 即会输出当前的时区信息,其中包含了 currentZone 的值。 它用于在控制台输出关于当期时区的相关信息。
}
}
Lombok
现在许多 java 工程都应用了 Lombok ,由此可见它很实用。同时,也有反对的文章,可参见:
)。
lombok 能够以简单注解的形式,助力我们简化并消除一些必要但显得冗余的 Java 代码,像常见的 Getter&Setter、toString()、构造函数等。lombok 不但便于编写,还能让我们的代码更为简洁。lombok 提供了一个功能完备的 jar 包,可便捷地与我们的项目进行整合。
引入依赖:
org.projectlombok
lombok
1.18.22
provided
常见注解:
完整注解参见:
最新的 IDEA 已经将 Lombok 插件内置其中,它能够像丝滑般发挥实用作用。
Lombok 只是对代码进行了简化,然而对于背后 Java 语言的基本规范,我们是应当知晓的。这是大家必须要掌握的内容,希望大家能够进行自我检查。
Lombok实现浅析:
在使用 Lombok 时,只需添加相应的注解,就无需再为其编写任何代码。那么,自动生成的代码究竟是怎样产生的呢?
核心在于对注解的解析。JDK5 在引入注解的同时,还提供了两种解析的方式。
运行时能够被解析的注解,需要把@Retention设定为 RUNTIME,如此一来就能够借助反射获取到该注解。java.lang.reflect 反射包提供了一个接口叫 AnnotatedElement 。这个接口定义了获取注解信息的一些方法。Class、Constructor、Field、Method、Package 等都实现了该接口。对反射熟悉的朋友应该对这种解析方式很熟悉。
编译时解析有两种机制,分别简单描述下:
1)Annotation Processing Tool
apt 从 JDK5 开始出现,到 JDK7 时被标记为过期且不推荐使用,在 JDK8 中被彻底删除。从 JDK6 起,可以使用 Pluggable Annotation Processing API 来对其进行替换。apt 被替换主要有以下 2 点原因:
可插拔的注解处理 API 。
JSR 269 在 JDK6 被加入。它作为 apt 的替代方案而存在。此方案解决了 apt 的两个问题。javac 在执行时会调用实现了该 API 的程序。基于此,我们能够对编译器进行一些增强。
Lombok 实际上是一个实现了“JSR 269 API”的程序。在利用 javac 时,它发挥作用的具体流程具体如下:
javac 会对源代码展开分析,进而生成了一棵抽象语法树(AST)。在运行过程中,它会调用实现了“JSR 269 API”的 Lombok 程序。此时,Lombok 会对第一步得到的 AST 进行处理,去找到带有@Data 注解的类所对应的语法树(AST),接着对该语法树(AST)进行修改,添加 getter 和 setter 方法定义的相应树节点。之后,javac 会使用修改后的抽象语法树(AST)来生成字节码文件,也就是给 class 增加新的节点(代码块)。
读了 Lombok 源码后,发现对应注解的实现都在 HandleXXX 里。例如,@Getter 注解的实现在 HandleGetter.handle()。另外,还有一些其他类库也使用这种方式来实现,像 Google Auto、Dagger 等。
前面在讲 Spring AOP 时提到了动态代理实现,其底层是字节码增强,而这与 Lombok 的底层原理是一致的。这和元数据的理念极为相似,无论是在元数据层面,还是在元元数据层面,每一层抽象描述层的增强都能够让前一层的动态化能力得以实现。
关于字节码增强,感兴趣的同学自行baidu(
)
Swagger
Swagger 是一个用于生成、描述以及调用 RESTful 接口的 Web 服务。通俗来讲,它能把项目中所有(打算暴露的)接口呈现在页面上,同时还具备可以进行接口调用以及测试的功能。
Swagger 遵循了 OpenAPI 规范。OpenAPI 是 Linux 基金会的一个项目。它试图通过定义一种语言来规范 RESTful 服务开发过程,这种语言用于描述 API 格式或 API 定义。
Swagger 有以下 3 个重要的作用:
为什么需要swagger?
前面讲过,随着技术的发展,前后端分离架构越来越普及。我们曾了解到,以前在 jvm 内部调用别人的库需看 javadoc。后来,基于 RPC 的分布式调用,即便使用同种语言(java)也需看 javadoc,若不是同种语言调用则需要其他的调用描述。同理,前后端工程师需要一种统一沟通的接口描述方式,而 swagger 就是用来解决这个问题的。服务提供方主要采用基于 RESTful 规范的方式,其接口描述有规范,即 OpenAPI 规范,此规范也是后来面向 API 编程的一个基础设施。
常用注解:
表示标识这个类是swagger的资源