JDK8 新特性之新的日期和时间 API

目录

一:旧版日期时间 API 存在的问题

二:新日期时间 API介绍

三:JDK 8的日期和时间类

四:JDK 8的时间格式化与解析

五:JDK 8的 Instant 类

六:JDK 8的计算日期时间差类

七:JDK 8的时间校正器

八:JDK 8设置日期时间的时区

小结:

九:重复注解的使用

十:类型注解的使用


一:旧版日期时间 API 存在的问题

               1. 设计很差: 在 java.util java.sql 的包中都有日期类, java.util.Date 同时包含日期和时间,而 java.sql.Date 仅包含日期。此外用于格式化和解析的类在java.text 包中定义。
               2. 非线程安全: java.util.Date 是非线程安全的,所有的日期类都是可变的,这是 Java 日期类最大的问题之一。
               3. 时区处理麻烦:日期类并不提供国际化,没有时区支持,因此 Java 引入了 java.util.Calendar 和java.util.TimeZone类,但他们同样存在上述所有的问题。

 

public class Demo01 {
    public static void main(String[] args) {
        // 旧版日期时间 API 存在的问题
        // 1.设计部合理
        Date now = new Date(1985, 9, 23);
        System.out.println(now);

        // 2.时间格式化和解析是线程不安全的
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        for (int i = 0; i < 50; i++) {
            new Thread(() -> {
                try {
                    Date date = sdf.parse("2019-09-09");
                    System.out.println("date = " + date);
                } catch (ParseException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

二:新日期时间 API介绍

             JDK 8 中增加了一套全新的日期时间 API ,这套 API 设计合理,是线程安全的。新的日期及时间 API 位于 java.time 包中,下面是一些关键类。
  • LocalDate :表示日期,包含年月日,格式为 2019-10-16
  • LocalTime :表示时间,包含时分秒,格式为 16:38:54.158549300
  • LocalDateTime :表示日期时间,包含年月日,时分秒,格式为 2018-09-06T15:33:56.750
  • DateTimeFormatter :日期时间格式化类。
  • Instant:时间戳,表示一个特定的时间瞬间。
  • Duration:用于计算2个时间(LocalTime,时分秒)的距离
  • Period:用于计算2个日期(LocalDate,年月日)的距离
  • ZonedDateTime :包含时区的时间
           Java 中使用的历法是 ISO 8601 日历系统,它是世界民用历法,也就是我们所说的公历。平年有 365 天,闰年是 366天。此外Java 8 还提供了 4 套其他历法,分别是:
  • ThaiBuddhistDate:泰国佛教历
  • MinguoDate:中华民国历
  • JapaneseDate:日本历
  • HijrahDate:伊斯兰历

三:JDK 8的日期和时间类

               LocalDate LocalTime LocalDateTime 类的实例是不可变的对象,分别表示使用 ISO - 8601 日历系统的日期、时间、日期和时间。它们提供了简单的日期或时间,并不包含当前的时间信息,也不包含与时区相关的信息。

// LocalDate:获取日期时间的信息。格式为 2019-10-16
@Test
public void test01() {
// 创建指定日期
LocalDate fj = LocalDate.of(1985, 9, 23);
System.out.println("fj = " + fj); // 1985-09-23
// 得到当前日期
LocalDate nowDate = LocalDate.now();
System.out.println("nowDate = " + nowDate); // 2019-10-16
// 获取日期信息
System.out.println("年: " + nowDate.getYear());
System.out.println("月: " + nowDate.getMonthValue());
System.out.println("日: " + nowDate.getDayOfMonth());
System.out.println("星期: " + nowDate.getDayOfWeek());
}
// LocalTime类: 获取时间信息。格式为 16:38:54.158549300
@Test
public void test02() {
// 得到指定的时间
LocalTime time = LocalTime.of(12,15, 28, 129_900_000);
System.out.println("time = " + time);
// 得到当前时间
LocalTime nowTime = LocalTime.now();
System.out.println("nowTime = " + nowTime);
// 获取时间信息
System.out.println("小时: " + nowTime.getHour());
System.out.println("分钟: " + nowTime.getMinute());
System.out.println("秒: " + nowTime.getSecond());
System.out.println("纳秒: " + nowTime.getNano());
}
// LocalDateTime类: 获取日期时间信息。格式为 2018-09-06T15:33:56.750
@Test
public void test03() {
LocalDateTime fj = LocalDateTime.of(1985, 9, 23, 9, 10, 20);
System.out.println("fj = " + fj); // 1985-09-23T09:10:20
// 得到当前日期时间
LocalDateTime now = LocalDateTime.now();
System.out.println("now = " + now); // 2019-10-16T16:42:24.497896800
System.out.println(now.getYear());
System.out.println(now.getMonthValue());
System.out.println(now.getDayOfMonth());
System.out.println(now.getHour());
System.out.println(now.getMinute());
System.out.println(now.getSecond());
System.out.println(now.getNano());
}
             对日期时间的修改,对已存在的 LocalDate 对象,创建它的修改版,最简单的方式是使用 withAttribute 方法。
            withAttribute 方法会创建对象的一个副本,并按照需要修改它的属性。以下所有的方法都返回了一个修改属性的对象,他们不会影响原来的对象。
// LocalDateTime类: 对日期时间的修改
@Test
public void test05() {
LocalDateTime now = LocalDateTime.now();
System.out.println("now = " + now);
// 修改日期时间
LocalDateTime setYear = now.withYear(2078);
System.out.println("修改年份: " + setYear);
System.out.println("now == setYear: " + (now == setYear));
System.out.println("修改月份: " + now.withMonth(6));
System.out.println("修改小时: " + now.withHour(9));
System.out.println("修改分钟: " + now.withMinute(11));
// 再当前对象的基础上加上或减去指定的时间
LocalDateTime localDateTime = now.plusDays(5);
System.out.println("5天后: " + localDateTime);
System.out.println("now == localDateTime: " + (now == localDateTime));
System.out.println("10年后: " + now.plusYears(10));
System.out.println("20月后: " + now.plusMonths(20));
System.out.println("20年前: " + now.minusYears(20));
System.out.println("5月前: " + now.minusMonths(5));
System.out.println("100天前: " + now.minusDays(100));
}
日期时间的比较
// 日期时间的比较
@Test
public void test06() {
// 在JDK8中,LocalDate类中使用isBefore()、isAfter()、equals()方法来比较两个日期,可直接进行比较。
LocalDate now = LocalDate.now();
LocalDate date = LocalDate.of(2018, 8, 8);
System.out.println(now.isBefore(date)); // false
System.out.println(now.isAfter(date)); // true
}

四:JDK 8的时间格式化与解析

      通过 java.time.format.DateTimeFormatter 类可以进行日期时间解析与格式化。

// 日期格式化
    @Test
    public void test04() {
        // 创建一个日期时间
        LocalDateTime now = LocalDateTime.now();

        // 格式化
        // 指定时间的格式
        // JDK自带的时间格式
        // DateTimeFormatter dtf = DateTimeFormatter.ISO_DATE_TIME;
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy年MM月dd HH时mm分ss秒");

        String format = now.format(dtf);
        System.out.println("format = " + format);

        // 解析
        for (int i = 0; i < 50; i++) {
            new Thread(() -> {
                LocalDateTime parse = LocalDateTime.parse("2016年09月20 15时16分16秒", dtf);
                System.out.println("parse = " + parse);
            }).start();
        }
    }

五:JDK 8 Instant

   Instant 时间戳 / 时间线,内部保存了从 1970 1 1 00:00:00 以来的秒和纳秒。
  // 时间戳
    @Test
    public void test07() {
        // Instant内部保存了秒和纳秒,一般不是给用户使用的,而是方便我们程序做一些统计的.
        Instant now = Instant.now();
        System.out.println("now = " + now); // 2019-10-19T07:30:42.629520400Z

        Instant plus = now.plusSeconds(20);
        System.out.println("plus = " + plus);

        Instant minus = now.minusSeconds(20);
        System.out.println("minus = " + minus);

        // 得到秒纳秒
        System.out.println(now.getEpochSecond());
        System.out.println(now.getNano());
    }

六:JDK 8的计算日期时间差类

Duration/Period : 计算日期时间差。
1. Duration :用于计算 2 个时间 (LocalTime ,时分秒 ) 的距离
2. Period :用于计算 2 个日期 (LocalDate ,年月日 ) 的距离

// Duration/Period类: 计算日期时间差
    @Test
    public void test08() {
        // Duration计算时间的距离
        LocalTime now = LocalTime.now();
        LocalTime time = LocalTime.of(14, 15, 20);
        Duration duration = Duration.between(time, now);
        System.out.println("相差的天数:" + duration.toDays());
        System.out.println("相差的小时数:" + duration.toHours());
        System.out.println("相差的分钟数:" + duration.toMinutes());
        System.out.println("相差的秒数:" + duration.toSeconds());

        // Period计算日期的距离
        LocalDate nowDate = LocalDate.now();
        LocalDate date = LocalDate.of(1998, 8, 8);
        // 让后面的时间减去前面的时间
        Period period = Period.between(date, nowDate);
        System.out.println("相差的年:" + period.getYears());
        System.out.println("相差的月:" + period.getMonths());
        System.out.println("相差的天:" + period.getDays());
    }

七:JDK 8的时间校正器

       有时我们可能需要获取例如:将日期调整到 下一个月的第一天 等操作。可以通过时间校正器来进行。
  • TemporalAdjuster : 时间校正器。
  • TemporalAdjusters : 该类通过静态方法提供了大量的常用TemporalAdjuster的实现。
  // TemporalAdjuster类:自定义调整时间
    @Test
    public void test09() {
        LocalDateTime now = LocalDateTime.now();

        // 将日期调整到“下一个月的第一天”操作。
        TemporalAdjuster firstDayOfNextMonth = temporal -> {
            // temporal要调整的时间
            LocalDateTime dateTime = (LocalDateTime)temporal;
            return dateTime.plusMonths(1).withDayOfMonth(1); // 下一个月的第一天
        };

        // JDK中自带了很多时间调整器
        // LocalDateTime newDateTime = now.with(firstDayOfNextMonth);
        LocalDateTime newDateTime = now.with(TemporalAdjusters.firstDayOfNextYear());
        System.out.println("newDateTime = " + newDateTime);
    }

八:JDK 8设置日期时间的时区

                Java8 中加入了对时区的支持, LocalDate LocalTime LocalDateTime 是不带时区的,带时区的日期时间类分别为:ZonedDate ZonedTime ZonedDateTime
                其中每个时区都对应着 ID ID 的格式为 区域 / 城市 。例如 : Asia/Shanghai 等。
                ZoneId :该类中包含了所有的时区信息。
  // 设置日期时间的时区
    @Test
    public void test10() {
        // 1.获取所有的时区ID
        // ZoneId.getAvailableZoneIds().forEach(System.out::println);

        // 不带时间,获取计算机的当前时间
        LocalDateTime now = LocalDateTime.now(); // 中国使用的东八区的时区.比标准时间早8个小时
        System.out.println("now = " + now);

        // 2.操作带时区的类
        // now(Clock.systemUTC()): 创建世界标准时间
        ZonedDateTime bz = ZonedDateTime.now(Clock.systemUTC());
        System.out.println("bz = " + bz);

        // now(): 使用计算机的默认的时区,创建日期时间
        ZonedDateTime now1 = ZonedDateTime.now();
        System.out.println("now1 = " + now1); // 2019-10-19T16:19:44.007153500+08:00[Asia/Shanghai]

        // 使用指定的时区创建日期时间
        ZonedDateTime now2 = ZonedDateTime.now(ZoneId.of("America/Vancouver"));
        System.out.println("now2 = " + now2); // 2019-10-19T01:53:41.225898600-07:00[America/Vancouver]

        // 修改时区
        // withZoneSameInstant: 即更改时区,也更改时间
        ZonedDateTime withZoneSameInstant = now2.withZoneSameInstant(ZoneId.of("Asia/Shanghai"));
        System.out.println("withZoneSameInstant = " + withZoneSameInstant); // 2019-10-19T16:53:41.225898600+08:00[Asia/Shanghai]

        // withZoneSameLocal: 只更改时区,不更改时间
        ZonedDateTime withZoneSameLocal = now2.withZoneSameLocal(ZoneId.of("Asia/Shanghai"));
        System.out.println("withZoneSameLocal = " + withZoneSameLocal); // 2019-10-19T01:54:52.058871300+08:00[Asia/Shanghai]
    }

小结:

     
           详细学习了新的日期是时间相关类, LocalDate 表示日期 , 包含年月日 ,LocalTime 表示时间 , 包含时分 秒,LocalDateTime = LocalDate + LocalTime, 时间的格式化和解析 , 通过 DateTimeFormatter 类型进行 .
               学习了 Instant , 方便操作秒和纳秒 , 一般是给程序使用的 . 学习 Duration/Period 计算日期或时间的距离 , 还使用时间调整器方便的调整时间, 学习了带时区的 3 个类 ZoneDate/ZoneTime/ZoneDateTime
JDK 8 新的日期和时间 API 的优势:
1. 新版的日期和时间 API 中,日期和时间对象是不可变的。操纵的日期不会影响老值,而是新生成一个实例。
2. 新的 API 提供了两种不同的时间表示方式,有效地区分了人和机器的不同需求。
3. TemporalAdjuster 可以更精确的操纵日期,还可以自定义日期调整器。
4. 是线程安全的

九:重复注解的使用

         自从 Java 5 中引入 注解 以来,注解开始变得非常流行,并在各个框架和项目中被广泛使用。不过注解有一个很大的限制是:在同一个地方不能多次使用同一个注解。JDK 8 引入了重复注解的概念,允许在同一个地方多次使用同一个注解。在JDK 8 中使用 @Repeatable 注解定义重复注解。

重复注解的使用步骤:
1. 定义重复的注解容器注解
@Retention(RetentionPolicy.RUNTIME)
@interface MyTests {
MyTest[] value();
}
2. 定义一个可以重复的注解
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(MyTests.class)
@interface MyTest {
String value();
}
3. 配置多个重复的注解
@MyTest("tbc")
@MyTest("tba")
@MyTest("tba")
public class Demo01 {
@MyTest("mbc")
@MyTest("mba")
public void test() throws NoSuchMethodException {
}
}

4. 解析得到指定注解
// 3.配置多个重复的注解
@MyTest("tbc")
@MyTest("tba")
@MyTest("tba")
public class Demo01 {
@Test
@MyTest("mbc")
@MyTest("mba")
public void test() throws NoSuchMethodException {
// 4.解析得到类上的指定注解
MyTest[] tests = Demo01.class.getAnnotationsByType(MyTest.class);
for (MyTest test : tests) {
System.out.println(test.value());
}
// 得到方法上的指定注解
Annotation[] tests1 =
Demo01.class.getMethod("test").getAnnotationsByType(MyTest.class);
for (Annotation annotation : tests1) {
System.out.println("annotation = " + annotation);
}
}
}

完整案例如下:



import org.junit.Test;

import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
// 3.配置重复注解
@MyTest("ta")
@MyTest("tb")
@MyTest("tc")
public class Demo01 {
    @Test
    @MyTest("ma")
    @MyTest("mb")
    public void test()  {
    }

    public static void main(String[] args) throws NoSuchMethodException {
        // 4.解析重复注解
        // 获取类上的重复注解
        // getAnnotationsByType是新增的API用户获取重复的注解
        MyTest[] annotationsByType = Demo01.class.getAnnotationsByType(MyTest.class);
        for (MyTest myTest : annotationsByType) {
            System.out.println(myTest);
        }
        System.out.println("----------");
        // 获取方法上的重复注解
        MyTest[] tests = Demo01.class.getMethod("test").getAnnotationsByType(MyTest.class);
        for (MyTest test : tests) {
            System.out.println(test);
        }
    }
}

// 1.定义重复的注解容器注解
@Retention(RetentionPolicy.RUNTIME)
@interface MyTests { // 这是重复注解的容器
    MyTest[] value();
}

// 2.定义一个可以重复的注解
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(MyTests.class)
@interface MyTest {
    String value();
}

十:类型注解的使用

JDK 8 @Target 元注解新增了两种类型: TYPE_PARAMETER TYPE_USE
TYPE_PARAMETER :表示该注解能写在类型参数的声明语句中。 类型参数声明如: <T>
TYPE_USE :表示注解可以再任何用到类型的地方使用。
TYPE_PARAMETER的使用
@Target(ElementType.TYPE_PARAMETER)
@interface TyptParam {
}
public class Demo02<@TyptParam T> {
public static void main( String[] args) {
}
public <@TyptParam E> void test( String a) {
}
}
TYPE_USE的使用
@Target(ElementType.TYPE_USE)
@interface NotNull {
}
public class Demo02<@TyptParam T extends String> {
private @NotNull int a = 10;
public static void main(@NotNull String[] args) {
@NotNull int x = 1;
@NotNull String s = new @NotNull String();
}
public <@TyptParam E> void test( String a) {
}
}

完整案例如下:

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
import java.util.ArrayList;

public class Demo02 <@TypeParam T> {
    private @NotNull int a = 10;

    public void test(@NotNull String str, @NotNull int a) {
        @NotNull double d = 10.1;
    }

    public <@TypeParam E extends Integer> void test01() {
    }
}

@Target(ElementType.TYPE_USE)
@interface NotNull {
}

@Target(ElementType.TYPE_PARAMETER)
@interface TypeParam {
}
小结:
            通过 @Repeatable 元注解可以定义可重复注解, TYPE_PARAMETER 可以让注解放在泛型上, TYPE_USE 可以让注解放在类型的前面

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值