hi,你好!欢迎访问本站!登录
本站由网站地图腾讯云宝塔系统阿里云强势驱动
当前位置:首页 - 教程 - 杂谈 - 正文 君子好学,自强不息!

Java8系列 (六) 新的日期和时候API

2019-11-18杂谈搜奇网54°c
A+ A-

概述

在Java8之前, 我们平常都是运用 SimpleDateFormat 来剖析和花样化日期时候, 但它是线程不安全的。

    @Test
    public void test() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 10; i++) {
            executorService.execute(() -> {
                try {
                    Date date = sdf.parse("20191103091515");
                    System.out.println(date.toString());
                } catch (ParseException e) {
                    e.printStackTrace();
                }
            });
        }
        executorService.shutdown();
    }

屡次运转上面这段顺序, 会报差别的非常, 下面是个中的一种

Exception in thread "pool-1-thread-2" Exception in thread "pool-1-thread-4" Exception in thread "pool-1-thread-3" java.lang.NumberFormatException: empty String
    at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1842)
    at sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110)
    at java.lang.Double.parseDouble(Double.java:538)
    at java.text.DigitList.getDouble(DigitList.java:169)
    at java.text.DecimalFormat.parse(DecimalFormat.java:2089)
    at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1867)
    at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514)
    at java.text.DateFormat.parse(DateFormat.java:364)
    at com.java8.action.Demo.lambda$test$0(Demo.java:25)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)

缘由也很简单, 检察一下源码, 发明 SimpleDateFormat 类继续了父类 DateFormat 的成员变量  protected Calendar calendar; , 而Calendar 类没有被 final 润饰, 是能够被修正的。

回到上面这个题目, 看一下 SimpleDateFormat 的剖析日期时候的API

进入 establish() 要领内里看一下

到此, 已基础清楚明了, 由于每次 SimpleDateFormat 剖析日期时候都邑清空一下它的成员变量 calendar 的值, 所以当多个线程并发接见同一个 SimpleDateformat 时, 就会有线程不安全题目。

处置惩罚体式格局也很简单, 你能够运用 ThreadLocal 类寄存 SimpleDateFormat 对象, 让每一个线程具有本身的SimpleDateFormat对象。

    /**Map键对应差别的剖析划定规矩字符串, 比方yyyyMMdd*/
    private static Map<String, ThreadLocal<SimpleDateFormat>> tl = new HashMap<>();

回到我们本日的主题, 在Java8中引入了新的日期和时候API, 这也是下面要引见的内容。

新的日期时候类都被 final 润饰, 不存在想上面引见的老版本API的线程不安全题目。

LocalDate、LocalTime和LocalDateTime

LocalDate和LocalTime, LocalDateTime 供应了许多静态工场要领来建立它们的实例对象, 而且这三者之间能够很轻易的相互举行范例转换。

    @Test
    public void test() {
        //静态要领建立对象
        LocalDate ld = LocalDate.of(2019, 10, 3);
        System.out.println(ld.getYear() + "\t" + ld.getMonth() + "\t" + ld.getDayOfMonth() + "\t" + ld.getDayOfWeek() + "\t" + ld.lengthOfMonth() + "\t" + ld.isLeapYear());//result: 2019    OCTOBER    3    THURSDAY    31    false
        LocalDate now = LocalDate.now();
        System.out.println(now.get(ChronoField.YEAR) + "\t" + now.get(ChronoField.MONTH_OF_YEAR) + "\t" + now.get(ChronoField.DAY_OF_MONTH));//result: 2019    11    3

        LocalTime lt = LocalTime.of(20, 44, 12);
        System.out.println(lt.getHour() + "\t" + lt.getMinute() + "\t" + lt.getSecond());//result: 20    44    12
        //剖析字符串
        LocalDate ld2 = LocalDate.parse("2019-10-05");//默许花样: yyyy-MM-dd
        System.out.println(ld2.toString());//result: 2019-10-05
        LocalTime lt2 = LocalTime.parse("20:42:12.828");//默许花样: HH:mm:ss.SSS
        System.out.println(lt2.toString());//result: 20:42:12.828
        //相互举行范例转换
        LocalDateTime ldt = LocalDateTime.of(2019, 10, 5, 21, 12, 10, 888).atZone(ZoneId.of("Asia/Shanghai")).toLocalDateTime();
        LocalDateTime ldt2 = LocalDateTime.of(ld2, lt2);// 2019-10-05T20:42:12.828
        LocalDateTime ldt3 = ld2.atTime(10, 10, 10);// 2019-10-05T10:10:10
        LocalDateTime ldt4 = ld2.atTime(lt2);
        LocalDateTime ldt5 = lt2.atDate(ld2);
        LocalDateTime ldt6 = LocalDateTime.parse("2019/10/05 20:20:20.888", DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss.SSS"));
        LocalDate ld6 = ldt6.toLocalDate();
        LocalTime lt6 = ldt6.toLocalTime();
    }

Instant

Instant对时候的建模体式格局是以UTC时区的1970年1月1日半夜时分最先所阅历的秒数举行盘算,它不包括时区信息。Instant类是为了轻易盘算机处置惩罚日期和时候而设想的。

 Instant.now().toEpochMilli()  能够猎取当前时候的时候戳, 别的, Instant 供应了相似 ofEpochMilli() 的要领依据某个时候戳猎取 Instant 实例, isBefore()和isAfter() 则用来比较两个 Instant 的大小

    @Test
    public void test2() {
        long milli = Instant.now().toEpochMilli();//猎取当前时候戳
        Instant instant = Instant.ofEpochMilli(1572749169937L);//依据某个时候戳猎取Instant实例
        Instant instant2 = instant.minusSeconds(1000L);
        System.out.println(instant.isAfter(instant2));//true
    }

Duration和Period

Duration 对象用秒和纳秒来权衡时候的是非,假如想要对多个时候对象举行日期运算,能够用 Period 类

    @Test
    public void test3() {
        Duration d1 = Duration.between(LocalDateTime.of(2019, 10, 7, 15, 55, 55, 888), LocalDateTime.now());
        Duration d2 = Duration.between(LocalTime.of(17, 55, 10), LocalTime.now());
        Duration d3 = Duration.between(Instant.ofEpochMilli(1570544602000L), Instant.now());
        System.out.println(d3.toHours());// 612
        //Duration对象用秒和纳秒来权衡时候的是非,所以入参不能运用LocalDate范例, 不然抛UnsupportedTemporalTypeException: Unsupported unit: Seconds
        //Duration.between(LocalDate.of(2019, 10, 7), LocalDate.now());

        //假如想要对多个时候对象举行日期运算,能够用Period
        Period p1 = Period.between(LocalDate.of(2018, 8, 30), LocalDate.now());
        System.out.println(p1.getYears() + "\t" + p1.getMonths() + "\t" + p1.getDays());// 1    2    4
        //工场要领引见
        Duration threeMinutes = Duration.ofMinutes(3);
        threeMinutes = Duration.of(3, ChronoUnit.MINUTES);
        Period tenDays = Period.ofDays(10);
        Period threeWeeks = Period.ofWeeks(3);
        Period twoYearsSixMonthsOneDay = Period.of(2, 6, 1);
    }

Temporal接口

LocalDate、LocalTime、LocalDateTime、Instant类都完成了 Temporal 接口,有许多通用的处置惩罚日期和时候的要领,比方plus(), minus(), with()

    @Test
    public void test4() {
        LocalDate ld = LocalDate.of(2019, 10, 7);
        //修正时候对象的某个属性值,返回一个新的对象
        LocalDate ld2 = ld.withDayOfYear(365);//2019-12-31
        LocalDate ld3 = ld.withDayOfMonth(18);//2019-10-18
        LocalDate ld4 = ld.with(ChronoField.MONTH_OF_YEAR, 8);//2019-08-07
        //对时候对象举行加减运算
        LocalDate ld5 = ld.plusWeeks(2L);//2019-10-21
        LocalDate ld6 = ld.minusYears(9L);//2010-10-07
        LocalDate ld7 = ld.plus(Period.ofMonths(2));//2019-12-07
        LocalDate ld8 = ld.plus(2L, ChronoUnit.MONTHS);//2019-12-07

        LocalTime lt = LocalTime.parse("10:10:10.888");
        LocalTime lt1 = lt.plus(Duration.ofHours(2L));//12:10:10.888
        LocalTime lt2 = lt.plus(120L, ChronoUnit.MINUTES);//12:10:10.888
    }

TemporalAdjuster接口

TemporalAdjuster 类供应了更多对日期定制化操纵的功用, 诸如将日期调整到下个工作日、本月的末了的一天、本年的第一天等等。

    @Test
    public void test5() {
        LocalDate ld = LocalDate.of(2019, 10, 7);
        LocalDate ld1 = ld.with(TemporalAdjusters.next(DayOfWeek.FRIDAY));//2019-10-11
        LocalDate ld2 = ld.with(TemporalAdjusters.nextOrSame(DayOfWeek.MONDAY));//2019-10-07
        LocalDate ld3 = ld.with(TemporalAdjusters.firstDayOfNextMonth());//2019-11-01

        //自定义TemporalAdjuster, 来盘算下一个工作日地点的日期
        LocalDate ld4 = LocalDate.of(2019, 10, 11).with(temporal -> {
            DayOfWeek now = DayOfWeek.of(temporal.get(ChronoField.DAY_OF_WEEK));
            long dayToAdd = now.equals(DayOfWeek.FRIDAY) ? 3L : now.equals(DayOfWeek.SATURDAY) ? 2L : 1L;
            return temporal.plus(dayToAdd, ChronoUnit.DAYS);
        });//2019-10-14
        //关于常常复用的雷同操纵,能够将逻辑封装一个类中
        TemporalAdjuster temporalAdjuster = TemporalAdjusters.ofDateAdjuster(temporal -> {
            DayOfWeek now = DayOfWeek.of(temporal.get(ChronoField.DAY_OF_WEEK));
            long dayToAdd = now.equals(DayOfWeek.FRIDAY) ? 3L : now.equals(DayOfWeek.SATURDAY) ? 2L : 1L;
            return temporal.plus(dayToAdd, ChronoUnit.DAYS);
        });
    }

DateTimeFormatter

DateTimeFormatter 用于举行可定制的日期时候花样化, 功用相当于之前的 SimpleDateFormat 类

    @Test
    public void test6() {
        //日期转字符串
        LocalDate ld = LocalDate.of(2019, 10, 7);
        String s1 = ld.format(DateTimeFormatter.BASIC_ISO_DATE);//20191007
        String s2 = ld.format(DateTimeFormatter.ISO_LOCAL_DATE);//2019-10-07
        //字符串转日期
        LocalDateTime ld1 = LocalDateTime.parse("2019-10-07 22:22:22.555", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"));
    }

ZoneId时区

ZoneId 是老版本的 TimeZone 的替代品, ZonedDateTime 代表了相关于指定时区的时候点

    @Test
    public void test7() {
        //LocalDate、LocalDateTime、Instant 转 ZonedDateTime
        ZonedDateTime zdt1 = LocalDate.of(2019, 10, 7).atStartOfDay(ZoneId.systemDefault());
        ZonedDateTime zdt2 = LocalDateTime.of(2019, 10, 7, 15, 55, 55, 888).atZone(ZoneId.of("Asia/Shanghai"));
        ZonedDateTime zdt3 = Instant.now().atZone(ZoneId.of("Asia/Yerevan"));

        //Instant转LocalDateTime
        LocalDateTime ldt1 = LocalDateTime.ofInstant(Instant.now(), ZoneId.systemDefault());
        //下面的两个栗子引见了ZoneOffset,他是应用和 UTC/格林尼治时候的牢固误差盘算时区,但不引荐运用,由于ZoneOffset并未斟酌任何夏令时的影响
        LocalDateTime ldt2 = LocalDateTime.ofInstant(Instant.now(), ZoneOffset.of("+8"));
        //LocalDateTime转Instant
        Instant instant = LocalDateTime.of(2019, 10, 7, 15, 55, 55).toInstant(ZoneOffset.of("+4"));
    }

参考资料

Java8 实战

SimpleDateFormat线程不安全及处置惩罚办法

Java进阶(七)正确理解Thread Local的道理与实用场景

作者:张小凡
出处:https://www.cnblogs.com/qingshanli/
本文版权归作者和博客园共有,迎接转载,但未经作者赞同必需保存此段声明,且在文章页面显著位置给出原文衔接,不然保存追查法律责任的权益。假如以为另有协助的话,能够点一下右下角的【引荐】。

  选择打赏方式
微信赞助

打赏

QQ钱包

打赏

支付宝赞助

打赏

  移步手机端
Java8系列 (六) 新的日期和时候API

1、打开你手机的二维码扫描APP
2、扫描左则的二维码
3、点击扫描获得的网址
4、可以在手机端阅读此文章
未定义标签

本文来源:搜奇网

本文地址:https://www.sou7.cn/282363.html

关注我们:微信搜索“搜奇网”添加我为好友

版权声明: 本文仅代表作者个人观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。请记住本站网址https://www.sou7.cn/搜奇网。

发表评论

选填

必填

必填

选填

请拖动滑块解锁
>>