Java16都快上线了,你该不会连Java8的特性都不会用吧?

2021年09月15日 阅读数:1
这篇文章主要向大家介绍Java16都快上线了,你该不会连Java8的特性都不会用吧?,主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。

据说微信搜索《Java鱼仔》会变动强哦!java

本文收录于githubgitee ,里面有我完整的Java系列文章,学习或面试均可以看看哦git

(一)前言

2020年,Java16有了新的消息,预计将在2021年的3月16日正式发布。可是这一消息对于百分之九十的国内程序员来讲都只是一个新闻而已,由于国内的绝大部分公司依然使用着Java8。这款发布于2014年的JDK版本深受各大公司的喜好,最大的缘由取决于它的稳定性。程序员

即便如此,依然有一半以上的程序员对于Java8的特性不了解,因而我用一个周末的时间把JDK8的”新“特性肝了一遍,但愿对你们有所帮助。github

(二)Lambda表达式

Lambda是一个匿名函数,它容许你经过表达式来代替功能接口,使用lambda可让代码变得更加简洁。简单来说,lambda使用更简洁的方式给接口添加实现方法。面试

Lambda只能在函数式接口中使用,函数式接口就是那些只有一个抽象方法的接口,能够用注解@FunctionalInterface修饰,若是一个接口中有多个方法,就不能再使用Lambda了。数据库

咱们经常使用的Runnable、Comparator都是函数式接口:express

Comparator

Runnable

lambda的语法格式以下:数组

(parameters) -> expression
或
(parameters) ->{ statements; }

2.1 无参数、无返回值

lambda实现的是对功能接口代码的简洁化,好比下面这一段:微信

Runnable runnable=new Runnable() {
    @Override
    public void run() {
        System.out.println("run");
    }
};

咱们在建立一个Runnable对象的时候须要经过匿名内部类实现接口中定义的方法,而使用lambda表达式,上面的代码一行就能够搞定:markdown

Runnable runnable2= () -> System.out.println("run");

2.2 有参数,无返回值

咱们先写一个这样的接口:

public interface Student {
    public void getAge(int age);
}

在之前的代码中,咱们须要首先写一个类去继承这个接口,或者是写一个匿名内部类,如:

Student student=new Student() {
    @Override
    public void getAge(int age) {
        System.out.println(age);
    }
};

如今就变得简单了:

Student student2=(age) -> { System.out.println(age); };

若是只有一个参数,小括号能够不写:

Student student3=age -> { System.out.println(age); };

2.3 有参数,有返回值

写一个接口:

public interface Student {
    public int getAge(int age);
}

直接写lambda表达式了:

Student student5=age -> { return age; };
System.out.println(student5.getAge(1));

(三)内置函数式接口

在前面已经介绍了什么是函数式接口,函数式接口就是那些只有一个抽象方法的接口,Java8中内置了四种函数式接口:

Consumer<T> : void accept(T t);
Supplier<T> : T get();
Function<T,R> : R apply(T t);
Predicate<T> : boolean test(T t)

这四种接口和咱们日常写的没有什么区别,能够在Java8中直接调用:

3.1 Consumer

消费型接口,提供了一个参数、无返回值的接口方法,就和消费同样,花出去就没有了。

public static void main(String[] args) {
    consume(100,money -> {
        System.out.println("消费了"+money+"元");
    });
}
public static void consume(double money, Consumer<Double> consumer){
    consumer.accept(money);
}

3.2 Supplier

供给型接口,提供了无参数,有返回值的接口方法,主要起到供给数据的做用,好比实现一个生成随机数的功能:

public static void main(String[] args) {
    System.out.println(getRandomNum(()->(int)(Math.random()*100)));
}
public static int getRandomNum(Supplier<Integer> supplier){
    return supplier.get();
}

3.3 Function

函数型接口,提供带参数和返回值的接口方法,可用来对数据进行处理后再返回,好比实现一个替换字符串给功能:

public static void main(String[] args) {
   //去掉先后空格
   System.out.println(handlerString("\t\tJava鱼仔\t\t",(str)->str.trim()));
}
public static String handlerString(String string, Function<String,String> function){
    return function.apply(string);
}

3.4 Predicate

断言型接口,提供带参数和返回值的接口方法,只不过返回值是boolean类型,可用于进行判断,好比实现一个判断某个字符串是不是整数的需求

public static void main(String[] args) {
     Pattern pattern = Pattern.compile("^[-\\+]?[\\d]*$");
     if (isDigit("111",s -> {
         return pattern.matcher(s).matches();
     })){
         System.out.println("is Digit");
     }
}
public static String handlerString(String string, Function<String,String> function){
    return function.apply(string);
}

(四)Stream API

Java8中两个最重大的改变,一个是Lambda表达式,另一个就是Stream API

Stream是Java8中处理集合的关键抽象概念,它能够指定你但愿对集合进行的操做,能够执行很是复杂的查找、过滤和映射数据等操做。

使用Stream API对集合数据进行操做,就相似于使用SQL执行的数据库查询。也可使用Stream API来并行执行操做。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。

Stream的执行过程分为三步

一、建立stream

二、操做stream(查找、筛选、过滤等等)

三、执行stream

stream的操做属于惰性求值,即全部的操做是在执行stream时一次性执行。

4.1 建立stream

建立流的方式有四种,这里直接把建立方式的注释写进代码里

public static void main(String[] args) {
    //建立Stream
    //一、经过Collection系列集合提供的stream()或parallelStream()
    List<String> list=new ArrayList<>();
    Stream<String> stream = list.stream();

    //二、经过Arrays中的静态方法stream()获取
    Integer[] integers=new Integer[10];
    Stream<Integer> stream1 = Arrays.stream(integers);

    //三、经过Stream类中的静态方法of()
    Stream<Integer> stream2 = Stream.of(integers);

    //四、迭代建立
    Stream<Integer> stream3 = Stream.iterate(0, (x) -> x + 2);
}

4.2 操做stream

stream有多种操做集合的方法,下面一一进行讲解:

为了更方便的演示,新建一个实体类User:

public class User {
    private String name;
    private int age;
    private String sex;
    //这里省略构造方法、get、set、toString方法
}

filter过滤--经过某种条件过滤元素:

filter经过Predicate接口过滤元素,下面这段代码将年龄超过30岁的过滤出来

public static void main(String[] args) {
    List<User> list= Arrays.asList(
            new User("javayz",23,"male"),
            new User("张三",25,"male"),
            new User("李四",30,"female"),
            new User("王五",33,"female")
            new User("王五",33,"female")
    );
    //过滤出年龄大于30岁的人
    Stream<User> userStream = list.stream().filter((e) -> e.getAge() >= 30);
   userStream.forEach((e)-> System.out.println(e));
}

limit截断--使元素不超过给定的数量

//对取出来的数据只展现一条
Stream<User> userStream = list.stream()
        .filter((e) -> e.getAge() >= 30)
        .limit(1);
userStream.forEach((e)-> System.out.println(e));

skip--跳过集合中的前n个数

能够和limit联合使用取出指定的范围的数

Stream<User> userStream = list.stream().skip(2);

distinct--去重,经过元素的hashcode()和equals()去除重复元素

去重是根据hashcode()和equals()方法判断重复元素的,所以实体类了须要重写hashcode()和equals()方法

Stream<User> userStream = list.stream().distinct();

map--经过Function接口对stream中的每一个数据处理后返回新的数据

好比我想把全部的英文改成大写,就能够这样

List<String> list1=Arrays.asList("aa","bb","cc");
Stream<String> stringStream = list1.stream().map((str) -> str.toUpperCase());
stringStream.forEach((e)-> System.out.println(e));

flatmap--经过Function接口把stream的每个值都换成另外个stream,最后再把全部stream链接成一个stream

这个方法的使用可能会比较难懂,经过一个例子,把包含三个字符串的集合中的每一个字符串都分红一个个字符提取出来。好比把一个集合{"aa","bb","cc"}变成{'a','a','b','b','c','c'},使用map就须要这样:

List<String> list1=Arrays.asList("aa","bb","cc");
Stream<Stream<Character>> streamStream = list1.stream().map((str) -> {
    List<Character> characters = new ArrayList<>();
    for (Character ch : str.toCharArray()) {
        characters.add(ch);
    }
    return characters.stream();
});
streamStream.forEach((stream)->{
    stream.forEach((character -> System.out.println(character)));
});

它第一个stream的返回值是stream中套一个字符流,输出的时候也须要先遍历最外层的stream再遍历内层的stream,比较麻烦,因而就可使用flatmap,他会把多个stream合并为一个。

Stream<Character> characterStream = list1.stream().flatMap((str) -> {
    List<Character> characters = new ArrayList<>();
    for (Character ch : str.toCharArray()) {
        characters.add(ch);
    }
    return characters.stream();
});
characterStream.forEach((e)-> System.out.println(e));

sorted--天然排序,按照Comparable方式排序

Stream<String> stringStream = list.stream().sorted();

sorted(Comparator comparator)--定制排序,本身写一个Comparator 实现排序

List<User> list= Arrays.asList(
        new User("javayz",23,"male"),
        new User("张三",25,"male"),
        new User("李四",30,"female"),
        new User("王五",33,"female")
);

Stream<User> sorted = list.stream().sorted((e1, e2) -> {
    return e1.getAge() == e2.getAge() ? 0 : e1.getAge() > e2.getAge() ? 1 : -1;
});
sorted.forEach((e)-> System.out.println(e));

4.3 执行stream

若是只是操做stream,流的数据是不会变化的,接下来介绍执行stream的一系列方法,首先把接下来会用的数据建立进来:

List<User> list= Arrays.asList(
        new User("javayz",23,"male"),
        new User("张三",25,"male"),
        new User("李四",30,"female"),
        new User("王五",33,"female")
);

allMatch--检查是否匹配全部元素

//判断全部的元素的sex是否都是male,这里返回false
boolean male = list.stream().allMatch((e) -> e.getSex().equals("male"));
System.out.println(male);

anyMatch--检查是否至少匹配一个元素

boolean male = list.stream().anyMatch((e) -> e.getSex().equals("male"));
System.out.println(male);//这里返回true

noneMatch--检查是否是没有元素可以匹配指定的规则

boolean male = list.stream().noneMatch((e) -> e.getSex().equals("male"));
System.out.println(male);//这里返回false

findFirst--返回第一个元素

Optional<User> first = list.stream().findFirst();
System.out.println(first.get());

findAny--返回当前流中的任意一个元素

Optional<User> first = list.stream().findAny();
System.out.println(first.get());

count--返回当前流中的元素总个数

long count = list.stream().count();
System.out.println(count);

max--返回当前流中的最大值

返回最大值和最小值依旧须要实现comparator接口方法

Optional<User> max = list.stream().max((e1, e2) -> {
    return Integer.compare(e1.getAge(), e2.getAge());
});
System.out.println(max.get());

min--返回当前流中的最小值

Optional<User> max = list.stream().min((e1, e2) -> {
    return Integer.compare(e1.getAge(), e2.getAge());
});
System.out.println(max.get());

reduce--规约,将流中的元素反复结合起来,获得一个值

List<Integer> list=Arrays.asList(1,2,3,4,5,6,7,8,9);
//从第0个元素开始,对list求和
Integer reduce = list.stream().reduce(0, (x, y) -> x + y);
System.out.println(reduce);

collection--收集,将流中的数据接收成其余的形式

好比咱们把上面User数据的名字收集到List数组中:

List<String> collect = list.stream().map((e) -> e.getName()).collect(Collectors.toList());
System.out.println(collect);

Collectors提供了大量的转化为其余方式的实现,这里不作过多介绍。

(五)接口中的默认方法和静态方法

在1.8以前,接口中的方法只能声明没法实现,在Java8中,接口中能够添加默认方法和静态方法了。

首先介绍默认方法,经过default修饰的方法能够在接口中增长实现。

public interface MyInterface {
    default void defaultMethod(){
        System.out.println("hello");
    }
}

另外是接口中的静态方法:

public interface MyInterface {
    public static void staticMethod(){
        System.out.println("hello");
    }
}

(六)新时间日期API

JAVA8中增长了新的时间日期API,经过代码来混个眼熟:

6.1 LocalDate、LocalTime、LocalDateTime

@Test
public void test1(){
    //获取当前时间
    LocalDateTime localDateTime=LocalDateTime.now();
    System.out.println(localDateTime);
    //增长两年
    System.out.println(localDateTime.plusYears(2));
    //减小两年
    System.out.println(localDateTime.minusYears(2));
    //获取年、月、日、小时、分钟、秒
    System.out.println(localDateTime.getYear());
    System.out.println(localDateTime.getMonthValue());
    System.out.println(localDateTime.getDayOfMonth());
    System.out.println(localDateTime.getHour());
    System.out.println(localDateTime.getMinute());
    System.out.println(localDateTime.getSecond());
}

6.2 Instant :时间戳

@Test
public void test2(){
    Instant instant=Instant.now();
    //获取当前时间戳
    System.out.println(instant.toEpochMilli());
}

6.3 Duration :计算两个时间间隔

@Test
public void test3(){
    Instant instant1=Instant.now();
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    Instant instant2=Instant.now();
    Duration between = Duration.between(instant1, instant2);
    //计算出两个日期相差的毫秒、分钟、小时等数据
    System.out.println(between.toMillis());
    System.out.println(between.toMinutes());
    System.out.println(between.toHours());
}

6.4 Period:计算两个日期间隔

@Test
public void test4(){
    LocalDate localDate1=LocalDate.of(2020,11,1);
    LocalDate localDate2=LocalDate.now();
    Period between = Period.between(localDate1, localDate2);
    //计算相差几年几个月零几天
    System.out.println(between.getYears());
    System.out.println(between.getMonths());
    System.out.println(between.getDays());
}

6.5 TemporalAdjuster:时间矫正器

TemporalAdjuster用于对时间进行操做,咱们能够经过TemporalAdjusters这个类调用大量对时间操做的方法,好比下个周日等等。

@Test
public void test5(){
    LocalDateTime localDateTime=LocalDateTime.now();
    LocalDateTime with = localDateTime.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
    System.out.println(with);
}

6.6 格式化时间

时间格式化类在JAVA8以前用的最多的就是SimpleDateFormat,如今多了一个叫作DateTimeFormatter的格式化类:

@Test
public void test6(){
    DateTimeFormatter dtf= DateTimeFormatter.ofPattern("yyyy-MM-dd");
    LocalDateTime localDateTime=LocalDateTime.now();
    System.out.println(localDateTime.format(dtf));
}

(七)总结

总的来说,Java8的新东西仍是有不少的,虽然如今不少程序员都不习惯使用新的语法,但对这些新语法也不要抗拒,毕竟如今最新的Java版本已经到16了呢!

扫码_搜索联合传播样式白色版 中号 小号.jpg