原创

Java8几个重要特性

目前java的版本已经更新到15,今天分享一下java8

Java8新增的几个重要特性
java8中新增了非常多的特性,其中需要主要学习的我认为有以下几个
lambda表达式
方法引用
Stream API
Optional类
函数式接口
默认方法
下面我就来着重介绍一下他们。
Lambda表达式
相信有前端 ES6基础的小伙伴都知道 lambda 是啥,没错,java8中也新增了这个特性。

总的来说lambda表达式就是一个语法糖,它可以极大的减少我们的代码量,但同时或许会给后续的维护工作带来一些困扰。

不同于 ES6 中的 => ,在 Java 中 lambda 的语法格式如下

(parameters) -> expression
or
(parameters) ->{ statements; }
Java 复制
我们可以发现,在 Java 中符号变为了 ->,这个也是需要我们注意的一点。
下面我们来看一个例子,首先,声明一个接口,之后我们用 Lambda 表达式来进行使用。

public class LambdaTest {
    public static void main(String[] args) {
        // 之前的隐式实现
        Itest sumBefore = new Itest() {
            @Override
            public Integer calc(int a, int b) {
                return a+b;
            }
        };
        System.out.println("before:"+sumBefore.calc(12,12));
        // 使用lambda表达式
        Itest sum = (a,b)-> a + b;
        System.out.println("now:"+sum.calc(12,12));

    }
    interface Itest{
        Integer calc(int a,int b);
    }
}
但值得注意的是,lambda 表达式只能引用标记了final的外层局部变量,
反之那么就说明,在lambda表达域之内,我们不能修改在域外的局部变量(可以访问),否则会编译错误。

在 Lambda 表达式当中不允许声明一个与局部变量同名的参数或者局部变量。

方法引用
方法引用是指通过一个方法的名字来指向它。

方法引用可以使得结构更紧凑和简洁,据我所知,在 BeetlSQL 这个持久化框架(类似于Mybatis)就大量使用到了方法引用来优雅的处理SQL。

方法引用使用的是 :: 一对冒号。

提供的可用来进行方法引用的例子。


public class MethodReference {
    public static void main(String[] args) {
        ICalc sum = Integer::sum;
        /*ICalc max = Integer::max;*/
        System.out.println(sum.sum(12,15));
    }
    interface ICalc{
        int sum(int a , int b);
        /*int max(int a, int... nums);*/
    }
}
方法引用主要有三类:

指向静态方法的方法引用,比如 Integer::parseInt
指向任意类型实例方法的方法引用,比如 String::length
指向现有对象的实例方法引用 this::hashCode
函数式接口
首先,我们需要知道函数式接口的作用,类似于我们常常使用的注解 @Override ,在我们声明的接口上方,我们需要先声明一个注解 @FunctionalInterface ,那么这个接口就可以被隐式的转换为lambda表达式。

函数式接口有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。
同时,在Java8中,已经内置了几种常用的函数式接口:
消费型接口 Consumer<T>
供给型接口 Supplier<T>
函数型接口 Function<T,R>
断言型接口 Predicate<T>
消费型接口
@FunctionalInterface
public interface Consumer<T> {

    /**
     * Performs this operation on the given argument.
     *
     * @param t the input argument
     */
    void accept(T t);

    /**
     * Returns a composed {@code Consumer} that performs, in sequence, this
     * operation followed by the {@code after} operation. If performing either
     * operation throws an exception, it is relayed to the caller of the
     * composed operation.  If performing this operation throws an exception,
     * the {@code after} operation will not be performed.
     *
     * @param after the operation to perform after this operation
     * @return a composed {@code Consumer} that performs in sequence this
     * operation followed by the {@code after} operation
     * @throws NullPointerException if {@code after} is null
     */
    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}
接收一个参数进行消费,但是无需返回结果。

import java.util.function.Consumer;

public class MethodReference {
    public static void main(String[] args) {
        Consumer<String> consumer = (str)->System.out.println(str+"\t消费成功");
        consumer.accept("女盆友1号");
        consumer.accept("女盆友2号");
        consumer.accept("女盆友3号");
    }

}

女盆友1号   消费成功
女盆友2号   消费成功
女盆友3号   消费成功
供给型接口
@FunctionalInterface
public interface Supplier<T> {

    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();
}

返回一个结果

public static void main(String[] args) {
        Supplier<String> t1Sl = ()->"男盆友";
        Supplier<String> t2Sl = ()->"女盆友";
        System.out.print(t1Sl.get()+"->");
        System.out.println(t2Sl.get());
    }

函数型接口
@FunctionalInterface
public interface Function<T, R> {

    /**
     * Applies this function to the given argument.
     *
     * @param t the function argument
     * @return the function result
     */
    R apply(T t);
    ...
}

同样的,函数型接口需要我们提供一个参数,之后返回一个结果。

public static void main(String[] args) {
        Function<String, Boolean> function = (s) -> {
            System.out.println("当前名字:"+s+"\t执行了工作!");
            return true;
        };
        boolean flag = function.apply("小王");
        System.out.println("执行结果:" + flag);
    }

断言型接口
@FunctionalInterface
public interface Predicate<T> {

    /**
     * Evaluates this predicate on the given argument.
     *
     * @param t the input argument
     * @return {@code true} if the input argument matches the predicate,
     * otherwise {@code false}
     */
    boolean test(T t);
    ...
}

传入一个参数,返回成功与否。
下面我们看一个例子。
public static void main(String[] args) {
        Predicate<String> pre1 = String::isEmpty;
        Predicate<String> pre2 = (str)-> str.equals("测试");

        boolean result1 = pre1.test("");
        System.out.println("执行的结果:"+result1);
        boolean result2 = pre2.test("测试1");
        System.out.println("执行的结果:"+result2);
    }

执行的结果:

执行的结果:true
执行的结果:false
Cmd 复制
默认方法
默认方法是 Java 8 中新增加的特性,简单来说,默认方法就是接口可以有实现方法,而且不需要实现类去实现其中的接口方法。

public interface ITest{
    default void print(){
        System.out.println("测试默认方法");
    }
    // 接口的静态方法
    static void staticPrint(){
        System.out.print("测试的静态方法");
    }
}
Optional 类对象
Optional类是一个可以为null的容器对象,如果存在值那么isPresent()方法会返回true,调用get()方法会返回该对象。

Optional类的引入很好的解决了空指针异常,使得我们在平时的开发中不用再去解决/处理空指针异常。

常用的方法
方法名	描述
static Optional empty()	返回空的Optional实例
boolean equals(Object obj)	判断其他对象是否和Optional中的值相同
T get()	如果在这个Optional中包含这个值,返回值,否则抛出异常:NoSuchElementException
boolean isPresent()	如果值存在则方法会返回true,否则返回 false。
static Optional of(T value)	返回一个指定非null值的Optional。否则抛出空指针异常
static Optional ofNullable(T value)	如果为非空,返回 Optional 描述的指定值,否则返回空的 Optional。
T orElse(T other)	如果存在该值,返回值, 否则返回 other。
表格内容来自菜鸟教程

Stream(超级重要)
Stream是 JAVA 8 中处理集合的抽象概念,它可以让我们以一种声明的方式来处理数据。它用一种类似于SQL语句的方式来操作Java集合、进行集合运算。它是一种对Java集合操作运算的高阶抽象类。

这种操作将要处理的元素集合看作一种流,流在管道中传输,并且可以在管道的节点上进行处理,进行一些集合操作。



特性
不是数据结构,它不会保存数据。
不会修改原来对象的数据,它会将操作后的数据保存到另外一个对象中去。
惰性求值,流在中间处理过程中,只是对操作进行了记录,并不会立即执行,需要等到执行终止的时候才会进行实际的计算。
Pipelining:中间操作都会返回流对象本身,这样多个操作可以串联成一个管道,如同流式风格。这样可以对操作优化,比如延迟执行等。
内部迭代:在之前对集合遍历都是通过迭代器或者foreach的方式来进行的,显示的在集合外部进行迭代,这叫做外部迭代。而Stream提供了内部迭代的方式,通过访问者模式实现。
什么是Stream
Stream(流)是一个来自数据源的元素队列并且支持聚合操作。

元素是特定类型的独享,形成一个队列,Java中的流对象并不会存储元素,而是按需计算。

数据源是流的来源,可以是集合,数组,I/O通道,产生器等等。

聚合操作类似于SQL语句一样,如filter,map,reduce,find,match,sorted。
具体使用
常用的创建方法
使用集合下的 stream() 和 parallelStream() 方法来获取流对象。

public class Stream1 {
   public static void main(String[] args) {
       List<String> strings = Arrays.asList("a","b","c","d","e","f","g");
       Stream<String> stringStream = strings.stream(); //获取一个顺序流
       Stream<String> parallelStream = strings.parallelStream(); //获得一个并行流
       stringStream.forEach(System.out::print);
       System.out.println();
       parallelStream.forEach((s)->{
           System.out.print(s + " ");
       });
   }
}

对于以上代码片段有如下结果

abcdefg
e d a f c g b 
Cmd 复制
我们可以很明显的对比出来,顺序流和并行流的区别。

使用Stream中的静态方法来构建流对象

public static void main(String[] args) {
       Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

       Stream<Integer> stream1 = Stream.iterate(0, (x) -> x + 2).limit(10);
       stream1.forEach((i) -> {
           System.out.print(i + " ");
       }); // 0 2 4 6 8 10 12 14 16 18

       System.out.println();

       Stream<Double> stream2 = Stream.generate(()-> Math.random() * 10 +1).limit(10);
       stream2.forEach((d) -> {
           System.out.print(d.intValue() + " ");
       }); // 10个随机数
   }
流的中间操作
筛选与切片操作

熟悉Python的同学就知道,在Python中可以对字典,集合,元组等数据结构进行一些pythonic风格的操作,其中切片操作最为经典。Java8 也支持类似的操作了。

filter:过滤流中的某些元素

limit(n):获取n个元素

skip(n):跳过n元素

distinct:通过流中元素的hashCode()和equals()去除重复元素

public static void main(String[] args) {
       Stream<Integer> stream = Stream.of(6, 15, 100, 258, 150, 0, 1, 2, 5, 4, 8, 9, 6, 3, 6, 9, 6, 165, 4189, 1, 911, 5614, 9, 1, 9, 4, 98, 4, 156, 16, 1);
       Stream<Integer> newStream = stream.filter(i -> i > 5) //获取大于5的元素流对象
               .distinct();//去重
       newStream.forEach(i-> System.out.print(i + " "));

   }

运行结果

6 15 100 258 150 8 9 165 4189 911 5614 98 156 16 
Cmd 复制
映射

map:接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。

flatMap:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。

public static void main(String[] args) {
       List<String> list = Arrays.asList("a,b,c","1,2,3");

       Stream<String> s1 = list.stream().map(s -> s.replaceAll(",",""));
       s1.forEach(s -> System.out.print(s+" "));

       Stream<String> s3 = list.stream().flatMap(s ->{
          // 将每个元素转换成一个stream
          String[] split = s.split(",");
           return Arrays.stream(split);
       });
       s3.forEach(s->System.out.print(s+" "));
   }
Java 复制
排序

sort:自然排序,流中元素需要实现Comparable接口

sorted(Comparator com):自定义排序

public static void main(String[] args) {
       List<String> list = Arrays.asList("a","v","b");
       list.stream().sorted().forEach(s -> System.out.print(s+" "));

       Student s1 = new Student("张三",18);
       Student s2 = new Student("李四",19);
       Student s3 = new Student("王五",20);
       Student s4 = new Student("陈麻子",17);
       List<Student> students = Arrays.asList(s1,s2,s3,s4);
       students.stream().sorted(Comparator.comparingInt(Student::getAge)).forEach(System.out::println);
   }
消费
peek:接收Consumer表达式,没有返回值。

public static void main(String[] args) {
       List<String> list = Arrays.asList("a","v","b");
       list.stream().sorted().forEach(s -> System.out.print(s+" "));

       Student s1 = new Student("张三",18);
       Student s2 = new Student("李四",19);
       Student s3 = new Student("王五",20);
       Student s4 = new Student("陈麻子",17);
       List<Student> students = Arrays.asList(s1,s2,s3,s4);
       students.stream()
               .peek(o -> o.setAge(o.getAge()+20))
               .sorted(Comparator.comparingInt(Student::getAge).reversed())
               .forEach(System.out::println);
       // 在原有的基础上自增20,并进行倒序
   }
流的终止操作
匹配/聚合

allMatch:接收一个 Predicate 函数,当流中每个元素都符合该断言时才返回true,否则返回false

noneMatch:接收一个 Predicate 函数,当流中每个元素都不符合该断言时才返回true,否则返回false

anyMatch:接收一个 Predicate 函数,只要流中有一个元素满足该断言则返回true,否则返回false

findFirst:返回流中第一个元素

findAny:返回流中的任意元素

count:返回流中元素的总个数

max:返回流中元素最大值

min:返回流中元素最小值

规约(reduce)
从一组值中生成一个新的值,reduce 函数其实用途非常广泛,作用也比较大,我们举一个累加的例子。

public static void main(String[] args) {
       List<Integer> list = Arrays.asList(1,2,3,4,5);
       int sum = list.stream().reduce((before,now)->before+now).get();
       System.out.println(sum);
   }

collect:接收一个Collector实例,将流中元素收集成为另外一个数据结构。
后端
  • 泽泽泽
  • 2020-11-05 10:59:11.679

评论区