美文网首页
JDK1.8新特性小总结

JDK1.8新特性小总结

作者: 鼻涕不停的小眼睛 | 来源:发表于2019-03-27 15:40 被阅读0次

    JDK1.8新特性

    一、接口的默认方法

    Java 8允许我们给接口添加一个非抽象的方法实现,只需要使用 default关键字即可,这个特征又叫做扩展方法

    interface Formula {
        double calculate(int a);
        default double sqrt(int a) {
            return Math.sqrt(a);
        }
    }
    Formula formula = new Formula() {
        @Override
        public double calculate(int a) {
            return sqrt(a * 100);
        }
    };
    formula.calculate(100);     // 100.0
    formula.sqrt(16);           // 4.0
    

    二、Lambda 表达式

    在老版本的Java中排列字符串

    List<String> names = Arrays.asList("peter", "anna", "mike", "xenia");
    Collections.sort(names, new Comparator<String>() {
        @Override
        public int compare(String a, String b) {
            return b.compareTo(a);
        }
    });
    

    在Java 8 中就不用使用这种传统的匿名对象的方式,Java 8提供了更简洁的语法,lambda表达式:

    Collections.sort(names, (String a, String b) -> {
        return b.compareTo(a);
    });
    

    对于函数体只有一行代码的,可以去掉大括号{}以及return关键字,参数也可以不写类型:

    Collections.sort(names, (a, b) -> b.compareTo(a));
    

    三、函数式接口

    可以将lambda表达式当作任意只包含一个抽象方法的接口类型,确保你的接口一定达到这个要求,你只需要给你的接口添加 @FunctionalInterface 注解,编译器如果发现你标注了这个注解的接口有多于一个抽象方法的时候会报错的(不加也对)。

    @FunctionalInterface
    interface Converter<F, T> {
        T convert(F from);
    }
    Converter<String, Integer> converter = (from) -> Integer.valueOf(from);
    Integer converted = converter.convert("123");
    System.out.println(converted);    // 123
    

    四、方法与构造函数引用

    上面中的代码还可以通过静态方法引用来表示:

    Converter<String, Integer> converter = Integer::valueOf;
    Integer converted = converter.convert("123");
    System.out.println(converted);   // 123
    

    也可以引用一个对象的方法

    converter = something::startsWith;
    String converted = converter.convert("Java");
    System.out.println(converted);    // "J"
    

    构造函数使用::关键字来引用

    class Person {
        String firstName;
        String lastName;
        Person() {}
        Person(String firstName, String lastName) {
            this.firstName = firstName;
            this.lastName = lastName;
        }
    }
    interface PersonFactory<P extends Person> {
        P create(String firstName, String lastName);
    }
    PersonFactory<Person> personFactory = Person::new;//相当于用带两个参数的构造方法实现接口方法
    Person person = personFactory.create("Peter", "Parker");
    

    五、一些接口配合lambda使用

    Predicate接口<br />Predicate 接口只有一个参数,返回boolean类型。该接口包含多种默认方法来将Predicate组合成其他复杂的逻辑(比如: 与,或,非):

    Predicate<String> predicate = (s) -> s.length() > 0;
    predicate.test("foo");              // true
    predicate.negate().test("foo");     // false
    Predicate<Boolean> nonNull = Objects::nonNull;
    Predicate<Boolean> isNull = Objects::isNull;
    Predicate<String> isEmpty = String::isEmpty;
    Predicate<String> isNotEmpty = isEmpty.negate();
    

    Function 接口<br />Function 接口有一个参数并且返回一个结果,并附带了一些可以和其他函数组合的默认方法(compose, andThen):

    Function<String, Integer> toInteger = Integer::valueOf;
    Function<String, String> backToString = toInteger.andThen(String::valueOf);
    backToString.apply("123");     // "123"
    

    Supplier 接口
    <br />Supplier 接口返回一个任意范型的值,和Function接口不同的是该接口没有任何参数

    Supplier<Person> personSupplier = Person::new;
    personSupplier.get();   // new Person
    

    Consumer 接口<br />Consumer 接口表示执行在单个参数上的操作

    Consumer<Person> greeter = (p) -> System.out.println("Hello, " + p.firstName);
    greeter.accept(new Person("Luke", "Skywalker"));//Hello, Luke
    

    Comparator 接口<br />添加了许多默认的方法

    Comparator<Person> comparator = (p1, p2) -> p1.firstName.compareTo(p2.firstName);
    Person p1 = new Person("John", "Doe");
    Person p2 = new Person("Alice", "Wonderland");
    comparator.compare(p1, p2);             // > 0
    comparator.reversed().compare(p1, p2);  // < 0
    

    Optional 接口<br />Optional 被定义为一个简单的容器,其值可能是null或者不是null。在Java 8之前一般某个函数应该返回非空对象但是偶尔却可能返回了null,而在Java 8中,不推荐你返回null而是返回Optional。

    Optional<String> optional = Optional.of("bam");
    optional.isPresent();           // true
    optional.get();                 // "bam"
    optional.orElse("fallback");    // "bam"
    optional.ifPresent((s) -> System.out.println(s.charAt(0)));     // "b"
    

    Stream 接口<br />java.util.Stream 表示能应用在一组元素上一次执行的操作序列。Stream 操作分为中间操作或者最终操作两种,最终操作返回一特定类型的计算结果,而中间操作返回Stream本身,这样你就可以将多个操作依次串起来。Stream 的创建需要指定一个数据源,比如 java.util.Collection的子类,List或者Set, Map不支持。Stream的操作可以串行执行或者并行执行。

    List<String> stringCollection = new ArrayList<>();
    stringCollection.add("ddd2");
    stringCollection.add("aaa2");
    stringCollection.add("bbb1");
    stringCollection.add("aaa1");
    stringCollection.add("bbb3");
    stringCollection.add("ccc");
    stringCollection.add("bbb2");
    stringCollection.add("ddd1");
    

    Filter 过滤
    过滤通过一个predicate接口来过滤并只保留符合条件的元素,该操作属于中间操作,所以我们可以在过滤后的结果来应用其他Stream操作(比如forEach)。forEach需要一个函数来对过滤后的元素依次执行。forEach是一个最终操作,所以我们不能在forEach之后来执行其他Stream操作。

    stringCollection
       .stream()
       .filter((s) -> s.startsWith("a"))
       .forEach(System.out::println);
    // "aaa2", "aaa1"
    

    Sort 排序
    排序是一个中间操作,返回的是排序好后的Stream。如果你不指定一个自定义的Comparator则会使用默认排序,需要注意的是,排序只创建了一个排列好后的Stream,而不会影响原有的数据源,排序之后原数据stringCollection是不会被修改的

    stringCollection
       .stream()
       .sorted()
       .filter((s) -> s.startsWith("a"))
       .forEach(System.out::println);
    // "aaa1", "aaa2"
    

    Map 映射
    中间操作map会将元素根据指定的Function接口来依次将元素转成另外的对象,map返回的Stream类型是根据你map传递进去的函数的返回值决定的。

    stringCollection
       .stream()
       .map(String::toUpperCase)
       .sorted((a, b) -> b.compareTo(a))
       .forEach(System.out::println);
    // "DDD2", "DDD1", "CCC", "BBB3", "BBB2", "AAA2", "AAA1"
    

    Match 匹配
    Stream提供了多种匹配操作,允许检测指定的Predicate是否匹配整个Stream。所有的匹配操作都是最终操作,并返回一个boolean类型的值。

    boolean anyStartsWithA = 
       stringCollection
           .stream()
           .anyMatch((s) -> s.startsWith("a"));
    System.out.println(anyStartsWithA);      // true
    boolean allStartsWithA = 
       stringCollection
           .stream()
           .allMatch((s) -> s.startsWith("a"));
    System.out.println(allStartsWithA);      // false
    boolean noneStartsWithZ = 
       stringCollection
           .stream()
           .noneMatch((s) -> s.startsWith("z"));
    System.out.println(noneStartsWithZ);      // true
    

    limit 线段流,使其不超过指定数量

    stringCollection.stream()
    .sorted()
    .limit(2)
    .forEach(s-> System.out.println(s));//"aaa1","aaa2"
    

    skip(n)—— 跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空流。与limit互补

    stringCollection.stream()
    .sorted()
    .skip(2)
    .forEach(s-> System.out.println(s));
    

    distinct——筛选,通过硫所生成元素的hashCode()和equals去除重复元素

    stringCollection.stream()
    .sorted()
    .distinct()
    .forEach(s-> System.out.println(s));
    

    Count 计数
    计数是一个最终操作,返回Stream中元素的个数,返回值类型是long。

    long startsWithB = 
       stringCollection
           .stream()
           .filter((s) -> s.startsWith("b"))
           .count();
    System.out.println(startsWithB);    // 3
    

    Reduce 规约
    这是一个最终操作,允许通过指定的函数来讲stream中的多个元素规约为一个元素,规越后的结果是通过Optional接口表示的:

    Optional<String> reduced =
       stringCollection
           .stream()
           .sorted()
           .reduce((s1, s2) -> s1 + "#" + s2);
    reduced.ifPresent(System.out::println);
    // "aaa1#aaa2#bbb1#bbb2#bbb3#ccc#ddd1#ddd2"
    

    查找与匹配
    allMatch——检查是否匹配所有元素
    anyMatch——检查是否至少匹配一个元素
    noneMatch——检查是否没有匹配所有元素
    findFoirst——返回第一个元素
    findAny——返回流中的任意元素
    count——返回流中元素的总个数
    max——返回流中元素的最大值
    min——返回流中元素的最小值

    并行Streams
    前面提到过Stream有串行和并行两种,串行Stream上的操作是在一个线程中依次完成,而并行Stream则是在多个线程上同时执行

    int max = 1000000;
    List<String> values = new ArrayList<>(max);
    for (int i = 0; i < max; i++) {
       UUID uuid = UUID.randomUUID();
       values.add(uuid.toString());
    }
    long t0 = System.nanoTime();
    long count = values.stream().sorted().count();//串行
    System.out.println(count);
    long t1 = System.nanoTime();
    long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);
    System.out.println(String.format("sequential sort took: %d ms", millis));
    // 串行耗时: 899 ms
    long t0 = System.nanoTime();
    long count = values.parallelStream().sorted().count();//并行
    System.out.println(count);
    long t1 = System.nanoTime();
    long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);
    System.out.println(String.format("parallel sort took: %d ms", millis));
    // 并行排序耗时: 472 ms
    

    Map<br />Map类型不支持stream,不过Map提供了一些新的有用的方法来处理一些日常任务。

    Map<Integer, String> map = new HashMap<>();
    for (int i = 0; i < 10; i++) {
        map.putIfAbsent(i, "val" + i);
    }
    map.forEach((id, val) -> System.out.println(val));
    map.computeIfPresent(3, (num, val) -> val + num);
    map.get(3);             // val33
    map.computeIfPresent(9, (num, val) -> null);
    map.containsKey(9);     // false
    map.computeIfAbsent(23, num -> "val" + num);
    map.containsKey(23);    // true
    map.computeIfAbsent(3, num -> "bam");
    map.get(3);             // val33
    map.getOrDefault(42, "not found");  // not found
    //Merge做的事情是如果键名不存在则插入,否则则对原键对应的值做合并操作并重新插入到map中。
    map.merge(9, "val9", (value, newValue) -> value.concat(newValue));
    map.get(9);             // val9
    map.merge(9, "concat", (value, newValue) -> value.concat(newValue));
    map.get(9);             // val9concat
    

    六、Optional类

    Optional 类(java.util.Optional) 是一个容器类,代表一个值存在或不存在,原来用null 表示一个值不存在,现在Optional 可以更好的表达这个概念。并且可以避免空指针异常。<br />常用方法:

    • Optional.of(T t) : 创建一个Optional 实例
    • Optional.empty() : 创建一个空的Optional 实例
    • Optional.ofNullable(T t):若t 不为null,创建Optional 实例,否则创建空实例
    • isPresent() : 判断是否包含值
    • orElse(T t) : 如果调用对象包含值,返回该值,否则返回t
    • orElseGet(Supplier s) :如果调用对象包含值,返回该值,否则返回s 获取的值
    • map(Function f): 如果有值对其处理,并返回处理后的Optional,否则返回Optional.empty()
    • flatMap(Function mapper):与map 类似,要求返回值必须是Optional

    七、新的日期时间API

    LocalDate,该类的实例是一个不可变对象,它只提供了简单的日期,并不含当天的时间信息。另外,它也不附带任何与时区相关的信息。
    LocalDate例子:

                    LocalDate date = LocalDate.of(2019, 1, 20);
            int year = date.getYear(); //2019
            Month month = date.getMonth();//JANUARY
            int dayOfMonth = date.getDayOfMonth();//20
            DayOfWeek dayOfWeek = date.getDayOfWeek();//SUNDAY
            int length = date.lengthOfMonth();//31
            boolean leapYear = date.isLeapYear();//false
            LocalDate now = LocalDate.now();//2019-03-06
            int y = date.get(ChronoField.YEAR);//2019
            int m = date.get(ChronoField.MONTH_OF_YEAR);//1
            int d = date.get(ChronoField.DAY_OF_MONTH);//20
    
                    LocalDate date1 = LocalDate.of(2014, 3, 18); //2014-03-18
                LocalDate date2 = date1.withYear(2011);  //2011-03-18
                LocalDate date3 = date2.withDayOfMonth(25);   //2011-03-25
                LocalDate date4 = date3.with(ChronoField.MONTH_OF_YEAR, 9);   //2011-09-25
                    
                    LocalDate date1 = LocalDate.of(2014, 3, 18);
                    LocalDate date2 = date1.plusWeeks(1);  //2014-03-25
                LocalDate date3 = date2.minusYears(3);  //2011-03-25
                LocalDate date4 = date3.plus(6, ChronoUnit.MONTHS);  //2011-09-25
    

    LocalTime,该类用于一天中的时间,比如13:45:20

                    LocalTime time = LocalTime.of(3,10,20);
            int hour = time.getHour();//3
            int minute = time.getMinute();//10
            int second = time.getSecond();//20
    

    LocalDateTime,是LocalDate和LocalTime的合体。它同时表示了日期和时间,但不带有时区信息,你可以直接创建,也可以通过合并日期和时间对象构造,如下所示。

                    // 2014-03-18T13:45:20
            LocalDate date = LocalDate.now();
            LocalTime time = LocalTime.now();
            LocalDateTime dt1 = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45, 20); 
            LocalDateTime dt2 = LocalDateTime.of(date, time);
            LocalDateTime dt3 = date.atTime(13, 45, 20);
            LocalDateTime dt4 = date.atTime(time);
            LocalDateTime dt5 = time.atDate(date);
                    LocalDate date1 = dt1.toLocalDate();
                    LocalTime time1 = dt1.toLocalTime();
    

    Instant,上面三个有关时间的类都有个一个.toInstant()可转换为Instant()类,可用Date.from(instant)方法转换为Date类。

                    Instant instant1 = Instant.ofEpochSecond(3);
            System.out.println(instant1);//1970-01-01T00:00:03Z
            //第一个参数是秒,第二个是纳秒参数,纳秒的存储范围是0至999,999,999
            Instant instant2 = Instant.ofEpochSecond(3,0);
            System.out.println(instant2);//1970-01-01T00:00:03Z
            //2s之后的在加上100万纳秒(1s)
            Instant instant3 = Instant.ofEpochSecond(2,1000000000);
            System.out.println(instant3); //1970-01-01T00:00:03Z
            Instant instant4 = Instant.ofEpochSecond(4,-1000000000);
            System.out.println(instant4); //1970-01-01T00:00:03Z
    

    上面这些类都实现了Temporal接口,所以有一些通用的方法如下:

    image.png

    Duration类的静态工厂方法between就是需要创建两个Temporal对象,计算之间的秒数。LocalDate不能使用。

        Duration d1 = Duration.between(time1, time2);
        Duration d1 = Duration.between(dateTime1, dateTime2);
        Duration d2 = Duration.between(instant1, instant2);
    

    Period类是以年、月或者日的方式对多个时间单位建模。

            Period period = Period.between(localDate1,localDate2);
            System.out.println(period.getYears()); //获取相隔的年份差 0
            System.out.println(period.getMonths()); //获取相隔的月份差 11
            System.out.println(period.getDays()); //获取相隔的日子差 4
    
    image.png

    TemporalAdjuster,有时候需要进行一些更加
    复杂的操作,比如,将日期调整到下个周日、下个工作日,或者是本月的最后一天,这时,可以使用重载Temporal中的with方法,向其传递一个提供了更多定制化选择的TemporalAdjuster对象,
    更加灵活地处理日期。

                    LocalDate date = LocalDate.of(2014,3,18);//2014-03-18
            LocalDate with = date.with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY));//2014-03-23
            LocalDate with1 = date.with(TemporalAdjusters.lastDayOfMonth());//2014-03-31
    
    image.png

    DateTimeFormatter,格式化以及解析日期时间对象

    LocalDate date = LocalDate.of(2014, 3, 18);
    String s1 = date.format(DateTimeFormatter.BASIC_ISO_DATE); //20140318
    String s2 = date.format(DateTimeFormatter.ISO_LOCAL_DATE);//2014-03-18
    
    LocalDate date1 = LocalDate.parse("20140318",DateTimeFormatter.BASIC_ISO_DATE);
    LocalDate date2 = LocalDate.parse("2014-03-18",DateTimeFormatter.ISO_LOCAL_DATE);
    
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
    LocalDate date1 = LocalDate.of(2014, 3, 18);
    String formattedDate = date1.format(formatter);// 18/3/2014
    LocalDate date2 = LocalDate.parse(formattedDate, formatter);
    

    ZoneId,是用来处理时区问题的

            ZoneId romeZone = ZoneId.of("Europe/Rome");
            LocalDate date = LocalDate.of(2014, Month.MARCH, 18); 
        ZonedDateTime zdt1 = date.atStartOfDay(romeZone);
            LocalDateTime dateTime = LocalDateTime.of(2014,Month.MARCH, 18,13,25);
            ZonedDateTime zdt3 = dateTime.atZone(romeZone);
            Instant instant = Instant.now();
        ZonedDateTime zdt3 = instant.atZone(romeZone);
    

    下图为理解LocaleDate、
    LocalTime、LocalDateTime以及ZoneId之间的差异。

    image.png

    ZoneOffset,利用当前时间和伦敦格林尼治子午线时间的差异:

    ZoneOffset newYorkOffset = ZoneOffset.of("-05:00");
    LocalDateTime dateTime = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45); 
    OffsetDateTime dateTimeInNewYork = OffsetDateTime.of(date, newYorkOffset);
    

    相关文章

      网友评论

          本文标题:JDK1.8新特性小总结

          本文链接:https://www.haomeiwen.com/subject/wgycpqtx.html