Java新特性

一、Java新特性

主要内容

  • Lambda表达式

  • 函数式接口

  • 方法引用

  • Stream流式编程

二、函数式接口

函数式接口:接口中只能有一个抽象方法,其他的可以有default、static、Object里继承的方法等

作用:在Java中主要用在Lambda表达式和方法引用(想使用Lambda表达式,接口必须为函数式接口)。

JDK8专门提供了@FunctionalInterface注解,用来进行编译检查。

已经使用过的函数式接口,比如Comparator等,多线程阶段学习的函数式接口有Runnable、Callable等。

注意:Comparable并没有被标记为函数式接口

内置的函数式接口

JDK 也提供了大量的内置函数式接口,使得 Lambda 表达式的运用更加方便、高效。这些内置的函数式接口已经可以解决我们开发过程中绝大部分的问题,只有一小部分比较特殊得情况需要我们自己去定义函数式接口。在这里特别介绍四个函数式接口。

  1. Consumer<T>:消费型接口(void accept(T t))。有参数,无返回值。

  2. Supplier<T>:供给型接口(T get())。只有返回值,没有入参。

  3. Function<T, R>:函数型接口(R apply(T t))。一个输入参数,一个输出参数,两种类型不可不同、可以一致。

  4. Predicate<T>:断言型接口(boolean test(T t))。输入一个参数,输出一个boolean类型得返回值。

函数式接口方法名输入参数输出参数作用
消费型接口Consumer void accept(T t) T void 对类型为T的对象进行操作
供给型接口Supplier T get() void T 返回类型为T的对象
函数型接口Function R apply(T t) T R 对类型为T的对象进行操作,返回类型为R的对象
断言型接口Predicate boolean test(T t) T boolean 对类型为T的对象进行操作,返回布尔类型结果

三、Lambda表达式

Lambda表达式适用于 函数式接口

注意:

  1. 有返回值的Lambda表达式,如果方法体只有一条返回语句,可同时省略return和{}。

  2. 虽然使用 Lambda 表达式可以对某些接口进行简单的实现,但并不是所有的接口都可以使用 Lambda 表达式来实现。

  3. Lambda 规定接口中只能有一个需要被实现的抽象方法,不是规定接口中只能有一个方法,称为函数式接口。

总结

->左面: 参数类型可以省略不写,自动类型推断。

               ​ 如果只有一个参数,()可以省略不写。

->右侧: {}将方法体的具体内容包裹起来。 

               ​ 方法体中只有一条执行语句,{}可以省略不写。

                如果执行语句就一条return语句,return 和 {}可以同时省略不写。

四、方法引用

Lambda体中仅仅调用一个方法,而没有其他的执行语句,可以使用方法引用

操作符: “::”双冒号

复制代码
public class TestMethodRef1 {
  public static void main(String[] args) {
    //使用匿名内部类实现
    Consumer consumer = new Consumer<Integer>() {
      @Override
      public void accept(Integer i) {
        System.out.println(i);
      }
    };
    consumer.accept(56);
    
    //使用lambda表达式实现
    Consumer<Integer> consumer1 = (i)->System.out.println(i);
    consumer1.accept(56);
    
    //使用方法引用
    Consumer<Integer> consumer2 = System.out::println;
    consumer2.accept(56);
  }
}
复制代码

在本示例中,println()的参数类型、返回值类型正好和Consumer接口的accept方法的参数类型、返回值类型相同,此时可以采用方法引用来简化语法。

语法为对象名::实例方法名。

方法引用有下面几种方式:

  1. 对象引用::实例方法名

  2. 类名::静态方法名

  3. 类名::实例方法名

  4. 类名::new (也称为构造方法引用)

  5. 类型[]::new (也称为数组引用)

 使用方法引用的要求:

   1.方法体中只有一个调用方法的执行语句。
2.函数式接口中方法的参数 和 引用方法的参数个数、顺序必须相同
3.函数式接口中方法有返回值,引用的方法也必须有返回值
4.函数式接口中方法的参数类和引用方法的类型相同 或 为引用方法参数类型的子类。
5.函数式接口中方法的返回值和引用方法的返回值类型相同 或 为引用方法参数返回值类型的父类。

四、流式编程

1.介绍

Stream作为Java8的一大亮点,它与java.io包里的InputStream和OutputStream是完全不同的概念。

它是对容器对象功能的增强,它专注于对容器对象进行各种非常便利、高效的聚合操作或者大批量数据操作。

Stream API借助于同样新出现的Lambda表达式,极大的提高编程效率和程序可读性。

同时,它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势,使用fork/join并行方式来拆分任务和加速处理过程。

所以说,Java8中首次出现的 java.util.stream是一个函数式语言+多核时代综合影响的产物。

Stream有如下三个操作步骤:

  ① 创建Stream:从一个数据源,如集合、数组中获取流。

  ② 中间操作:一个操作的中间链,对数据源的数据进行操作。

  ③ 终止操作:一个终止操作,执行中间操作链,并产生结果。

 

当数据源中的数据上了流水线后,这个过程对数据进行的所有操作都称为“中间操作”。

中间操作仍然会返回一个流对象,因此多个中间操作可以串连起来形成一个流水线。

 

当所有的中间操作完成后,若要将数据从流水线上拿下来,则需要执行终止操作。

终止操作将返回一个执行结果,这就是你想要的数据。

 

多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何处理!而在终止操作时一次性全部处理,称作“惰性求值”。

2.创建Stream串行流

public class TestStream {
    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<>();
        Collections.addAll(list, 2,3,4,1,6,5);
        Stream<Integer> stream = list.stream();//获取stream流对象
        stream.forEach(System.out::println);
    }
}

3.中间操作和终止操作

复制代码
public class TestStream2 {
  public static void main(String[] args) {
      ArrayList<Integer> list = new ArrayList<>();
      Collections.addAll(list, 5, 4, 4, 3, 2, 1);
      /* 中间操作 */
      Stream<Integer> stream = list.stream()
          .filter(t -> {   //过滤, 获取符合条件元素
              if (t > 1) {
                  return true;
              }
              return false;
          })
          .map(t -> t + 1) //将元素进行相关操作
          .distinct() //去重
          .sorted() //排序
          .skip(1) //跳过1个
          .limit(2); //返回2个
      /* 终止操作 */
      //将数据收集到Set集合中
      //Set<Integer> set = stream.collect(Collectors.toSet());
      //获取最大值
      //Optional<Integer> max = stream.max(Integer::compareTo);
      //System.out.println(max.get());
      //获取最小值
      //Optional<Integer> min = stream.min(Integer::compareTo);
      //System.out.println(min.get());
      //获取元素个数
      //long count = stream.count();
      //System.out.println(count);
      //变为数组
      //Object[] objects = stream.toArray();
      //获取第一个
      //Optional<Integer> first = stream.findFirst();
      //System.out.println(first.get());
      //遍历获取每一个元素
      stream.forEach(System.out::println);
  }
}
复制代码

4.创建Stream串行流

简单来说,并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流。

Java 8 中将并行进行了优化,我们可以很容易的对数据进行并行操作。

Stream API 可以声明性地通过 parallel() 与sequential() 在并行流与串行流之间进行切换 。

 例:

  使用ForkJoin和普通for实现1-1000000000000l求和效率对比。

复制代码
public class SumTask extends RecursiveTask<Long> {
  private long start;
  private long end;
  private final int step = 2000000;//自定义步长

  public SumTask(long start, long end) {
    this.start = start;
    this.end = end;
  }

  @Override //在这个方法中定义我们自己计算的规则
  protected Long compute() {
    long sum = 0;
    //小于步长, 不再进行任务的拆分
    if (end - start < step) {
      for (long i = start; i <= end; i++) {
        sum += i;
      }
    } else {
      long mid = (end + start) / 2;
      SumTask leftTask = new SumTask(start, mid);
      SumTask rightTask = new SumTask(mid + 1, end);
      //执行子任务
      leftTask.fork();
      rightTask.fork();
      //子任务,执行完,得到执行结果
      long leftSum = leftTask.join();
      long rightSum = rightTask.join();
      //System.out.println("join结果"+leftSum+"---"+rightSum);
      sum = leftSum + rightSum;
    }
    return sum;
  }

  public static void main(String[] args) throws ExecutionException, InterruptedException {
    long sum = 0;
    long l = System.currentTimeMillis();
    for (long i = 1; i <= 100000000l; i++) {
      sum += i;
    }
    long l2 = System.currentTimeMillis();
    System.out.println(sum);
    System.out.println("forx循环执行时间:" + (l2 - l));

    //使用ForkJoin框架解决
    //创建一个ForkJoin池
    ForkJoinPool pool = new ForkJoinPool();
    //定义一个任务
    SumTask sumTask = new SumTask(1, 100000000l);
    long l3 = System.currentTimeMillis();
    //将任务交给线程池
    Future<Long> future = pool.submit(sumTask);
    long l4 = System.currentTimeMillis();
    //得到结果并输出
    Long result = future.get();
    System.out.println(result);
    System.out.println("ForkJoin执行时间:" + (l4 - l3));
  }
}
复制代码

4.2 获取并行流

public static void main(String[] args) throws ExecutionException, InterruptedException {
  List<Integer > list = new ArrayList<>();
  Collections.addAll(list,34,56,89,65,87,80,87,95,100,34,45);
  //创建Stream方:并行流,底层采用ForkJoin框架,结果并不按照集合原有顺序输出
  Stream stream2 = list.parallelStream();//
  stream2.forEach((x)->System.out.println(x+"---"+Thread.currentThread().getName()));
}

5.Optional

5.1 介绍

Optional存在的意义就是简化 为了防止空指针异常的if-else 的代码

提供了全局value属性存储值

5.2 获取Optional对象

 ① 通过Optional.of()方法,传递的参数的值为null,会出现空指针异常。

 ② 通过Optional.ofNullable()方法,创建Optional对象。


People people = null;
Optional<People> optional = Optional.of(people); //java.lang.NullPointerException
Optional<People> optional1 = Optional.ofNullable(people); //创建Optional对象

5.3 使用

5.3.1 参数为null

复制代码
public class Demo {
  public static void main(String[] args) {
    //People people = null; 参数为null,两种方法都会执行
    People people =new People();//参数不为null,只执行orElse()
    People t1 = Optional.ofNullable(people).orElse(getPeople("orElse"));
    People t2 = Optional.ofNullable(people).orElseGet(()->getPeople("orElseGet"));
  }
  public static People getPeople(String str){
    System.out.println(str);
    return new People();
  }
}
复制代码
  1. 如果传递参数值为null, 可以使用orElse(T)或orElseGet(Supplier)进行参数的实例化。

  2. 如果传递参数值不为null, orElse()也会执行, orElseGet()不会执行。

5.3.2 获取Optional中存储的值

可以通过get()获取到全局value对应的值。

 People people =new People();Optional<People> optional = Optional.ofNullable(people);People people = optional.get();

5.3.3 判断传递的参数是否为null

ifPresent(Consumer<? super T> consumer) 可以判断是否为null

  1. 为null, 不会执行Consumer的实现。

  2. 不为null, 执行Consumer的实现。

People people =new People();
Optional.ofNullable(people).ifPresent(peo -> peo.setName("zs"));
System.out.println(people.getName());

5.3.4 filter()过滤

Optional<People> people1 = Optional.ofNullable(people).filter(x -> {
            if (x.getA() > 60) {
                return true;
            } else {
                return false;
            }
        });

5.3.5 map()

People people = null;
//  People people =new People("zs", 10);
Optional.ofNullable(people).map(u -> u.getAge()).ifPresent(t -> System.out.println(t)); //对象不为null, 打印结果

 

 

 



posted @   ygdgg  阅读(373)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
点击右上角即可分享
微信分享提示