二—18:Java新特性

一、Lambda表达式

Lambda适用于只有一个抽象方法的接口。

1. Lambda说明
  1. Lambda表达式:
    Lambda表达式基于数学中的λ演算得名,对应java中的lambda抽象,是一个匿名函数,即没有函数名的函数。

  2. Lambda表达式好处:
    使用 Lambda 表达式可使代码变的更加简洁紧凑。并且Lambda表达式可和Stream API等相结合,使代码更加简洁紧凑。Lambda 表达式经常用来替代部分匿名内部类。

  3. Lambda表达式的语法
    (parameters) -> expression或 (parameters) ->{ statements; }
    参数:要重写的方法的形参列表
    -> :lambda运算符
    表达式/语句体:要实现的方法的方法体

  4. Lambda表达式的本质:

    Lambda 表达式是一种匿名函数(不是匿名内部类),简单地说,它是没有声明的方法,也即没有访问修饰符、返回值声明和名字。它实质属于函数式编程的概念。

2. Lambda表达式的使用
2.1 无参,无返回值
  1. 接口
public interface A {
  void test();
}
  1. 实现
public class TestLambda1 {
  public static void main(String[] args) {
    /*1. 没有参数,没有返回值*/
    //1.匿名内部类
    A a = new A() {
      @Override
      public void test() {
        System.out.println("匿名内部类");
      }
    };
    a.test();
    
    //2.使用lambda表达式 实现A接口中的test方法
    /*
     * 匿名内部类和Lambda表达式:
     *   public void test() 相当于 ()
     *   {} 相当于 {}
     * */
    A a1 = () -> {
      System.out.println("使用lambda表达式");
    };
    a1.test();

    //3.Lambada表达式的简化
    //只有一条执行语句,可以省略{}
    A a2 = () -> System.out.println("简化的Lambda");
    a2.test();
  }
}
2.2 单参,无返回值
  1. 接口
public interface B {
  void test(String name);
}
  1. 实现
public class TestLambda2 {
  public static void main(String[] args) {
    /*1. 一个参数,没有返回值*/
    //匿名内部类
    B b = new B() {
      @Override
      public void test(String name) {
        System.out.println(name);
      }
    };
    b.test("zs");

    //Lambda表达式
    B b1 = (String name) -> {
      System.out.println("Lambda " + name);
    };
    b1.test("ls");

    //简化 1 只有一个执行语句
    B b2 = (String name) -> System.out.println(name);
    b2.test("ww");

    //简化 2 参数的类型可以简化,自动的推断类型
    B b3 = (name) -> System.out.println(name);
    b3.test("zl");

    //简化 3 只有一个参数,可以将参数外的() 省略
    B b4 = name -> System.out.println(name);
    b4.test("wq");
  }
}
2.3 多参,无返回值
  1. 接口
public interface C {
  void test(String name, int age);
}
  1. 实现
public class TestLambda3 {
  public static void main(String[] args) {
    /*1. 多个参数, 没有返回值*/
    //匿名内部类
    C c = new C() {
      @Override
      public void test(String name, int age) {
        System.out.println(name + age);
      }
    };
    c.test("zs", 19);
    
    //Lambda表达式
    C c1 = (String name, int age) -> {
      if (age > 18) {
        System.out.println(name + age);
      }
    };
    c1.test("ls", 19);
    
    //简化  注意: 没有参数,多余一个参数,()不能够省略
    //参数名可以和实现方法的名字不一致
    C c2 = (aaa, age) -> System.out.println(aaa + age);
    c2.test("ww", 19);
  }
}
2.4 无惨,有返回值
  1. 接口
public interface D {
  int test();
}
  1. 实现
public class TestLambda4 {
  public static void main(String[] args) {
    /*1. 无参数,有返回值*/
    //匿名内部类
    D d = new D() {
      @Override
      public int test() {
        return 10;
      }
    };
    System.out.println(d.test());

    //Lambda表达式
    D d1 = () -> {
      System.out.println("-----------");
      return 100;
    };
    System.out.println(d1.test());
    
    //简化  只有返回值的代码 可以将{} 和 return都省略
    D d2 = () -> 100;
    System.out.println(d2.test());
  }
}
2.5 单参,有返回值
  1. 接口
public interface E {
  String test(String name);
}
  1. 实现
public class TestLambda5 {
  public static void main(String[] args) {
    /*1. 一个参数,有返回值*/
    //匿名内部类
    E e = new E() {
      @Override
      public String test(String name) {
        return name;
      }
    };
    System.out.println(e.test("zs"));
    
    //使用Lambda表达式
    E e2 = (String name) -> {return name;};
    System.out.println(e2.test("ls"));

    //简化1 参数类型可以省略,只有一个参数()可以省略  只有返回的语句,可以将{} 和 return省略
    E e3 = aa -> aa;
    System.out.println(e3.test("ww"));

    //复杂的逻辑的代码,不能简化
    E e4 = aa -> {
      if(Integer.parseInt(aa) > 10){
        return aa;
      }
      return 0;
    };
  }
}
2.6 多参,有返回值
  1. 接口
public interface F {
  int test(String name, int age);
}
  1. 实现
public class TestLambda6 {
  public static void main(String[] args) {
    /*1. 多个参数,有返回值*/
    //匿名内部类
    F f = new F() {
      @Override
      public int test(String name, int age) {
        System.out.println(name);
        return age;
      }
    };
    System.out.println(f.test("zs", 10));

    //使用Lambda表达式
    F f1 = (name, age) -> {
      /* System.out.println(name);*/
      return age;
    };
    System.out.println(f1.test("ls", 20));

		//简化  只返回结果,无其他操作可以简化
    F f2 = (name, age) -> age;
  }
}
2.7 注意
  1. 有返回值的Lambda表达式,如果方法体只有一条返回语句,可同时省略return和{}。
  2. 虽然使用 Lambda 表达式可以对某些接口进行简单的实现,但并不是所有的接口都可以使用 Lambda 表达式来实现。Lambda 规定接口中只能有一个需要被实现的抽象方法,不是规定接口中只能有一个方法,称为函数式接口。

二、 函数式接口

1. 认识函数式接口

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

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

  1. 演示
@FunctionalInterface
public interface FuncInterface {
  //只有一个抽象方法
  public void  method1();
  //default方法不计
  default  void method2(){
  }
  //static方法不计
  static void method3(){
  }
  //从Object继承的方法不计
  public boolean equals(Object obj);
}
2. 内置的函数式接口

JDK 也提供了大量的内置函数式接口,使得 Lambda 表达式的运用更加方便、高效。在这里特别介绍四个Java内置的函数式接口。

函数式接口 方法名 输入参数 输出参数 作用
消费型接口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的对象进行操作,返回布尔类型结果

消费型接口:

public class TestFunctional1 {
  public static void main(String[] args) {
    List<Integer > list = new ArrayList<>();
    Collections.addAll(list,34,56,89,65,87);
    
    //使用匿名内部类实现
    Consumer consumer = new Consumer<Integer>() {
      @Override
      public void accept(Integer elem) {
        System.out.println(elem);
      }
    };
    list.forEach(consumer);
    
    //使用Lambda表达式
    //list.forEach((elem)->{System.out.println(elem);});
    list.forEach(elem->System.out.println(elem));
  }
}

断言型接口:

public class TestFunctional2 {
  public static void main(String[] args) {
    List<Integer > list = new ArrayList<>();
    Collections.addAll(list,34,56,89,65,87);

    //使用匿名内部类实现
    System.out.println(list);
    Predicate predicate = new Predicate<Integer>(){
      @Override
      public boolean test(Integer i) {
        if(i<60){
          return true;
        }
        return false;
      }
    };
    list.removeIf(predicate);
    System.out.println(list);

    //使用Lambda表达式实现
    list.removeIf((i)->{
      if(i > 80) {
      	return true;
    	}
      return false;
    });
    System.out.println(list);
  }
}

三、 方法引用

有时候,Lambda体中可能仅调用一个方法,而不做任何其它事,对于这种情况,通过一个方法名字来引用这个已存在的方法会更加清晰。方法引用是一个更加紧凑,易读的 Lambda 表达式,注意方法引用是一个 Lambda 表达式,方法引用操作符是双冒号 “::”。

1. 方法引用示例

示例1:

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);
  }
}

示例2:

public class TestMethodRef2 {
  public static void main(String[] args) {
    List<String > list = new ArrayList<>();
    Collections.addAll(list,"Java","MySQL","HTML","JSP","SSM");

    //使用匿名内部类实现
    Consumer<String> consumer = new Consumer<>() {
      @Override
      public void accept(String s) {
        System.out.println(s);
      }
    };
    list.forEach(consumer);

    //使用lambda表达式实现
    list.forEach(a -> System.out.println(a));

    //使用方法引用实现
    list.forEach(System.out::println);
  }
}

示例3:

public class TestMethodRef4 {
  public static void main(String[] args) {
    //使用匿名内部类实现
    Comparator comparator1 = new Comparator<Integer>() {
      public int compare(Integer in1, Integer in2) {
        //return in1.intValue()-in2.intValue();
        return Integer.compare(in1,in2);
      }
    };
    System.out.println(comparator1.compare(12,34));
    
    //使用Lambda表达式实现
    Comparator<Integer> comparator2 = (in1,in2)->{ return Integer.compare(in1,in2);};
    System.out.println(comparator2.compare(12,34));
    
    //使用方法引用实现
    Comparator<Integer> comparator3 =Integer::compare;
    System.out.println(comparator3.compare(12,34));
  }
}
2.总结

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

  1. 对象引用::实例方法名
  2. 类名::静态方法名
  3. 类名::实例方法名
  4. 类名::new (也称为构造方法引用)
  5. 类型[]::new (也称为数组引用)

四、 流式编程

1. 介绍

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

它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势,使用fork/join并行方式来拆分任务和加速处理过程。所以说,Java8中首次出现的 java.util.stream是一个函数式语言+多核时代综合影响的产物。

Stream有如下三个操作步骤:

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

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

​ 三、终止操作:一个终止操作,执行中间操作链,并产生结果。
二—18:Java新特性_第1张图片

  • 当数据源中的数据上了流水线后,这个过程对数据进行的所有操作都称为“中间操作”。中间操作仍然会返回一个流对象,因此多个中间操作可以串连起来形成一个流水线。比如map (mapToInt,flatMap等)、filter、distinct、sorted、peek、limit、skip、parallel、sequential、unordered。
  • 当所有的中间操作完成后,若要将数据从流水线上拿下来,则需要执行终止操作。终止操作将返回一个执行结果,这就是你想要的数据。比如:forEach、toArray、reduce、collect、min、max、count、iterator等。
  • 多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何处理!而在终止操作时一次性全部处理,称作“惰性求值”。
2. 创建Stream串行流
public class TestStream1 {
  public static void main(String[] args) {
    //创建集合Stream
    List<Integer > list = new ArrayList<>();
    Collections.addAll(list,34,56,89,65,87,80,87,95,100,34,45);
    Stream stream = list.stream();
    stream.forEach(System.out::println);
    
    //创建多个元素Stream
    System.out.println("----------------");
    Stream stream3 = Stream.of(34,56,89,65,87,80,87,95,100,34,45);
    stream3.forEach(System.out::println);
    
    //创建数组Stream   使用ntStream 对象名 = Arrays.stream(数组名);
    int [] arr = {34,56,89,65,87,80,87,95,100,34,45};
    IntStream stream4 = Arrays.stream(arr);
    stream4.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 set = stream.collect(Collectors.toSet());
      //获取最大值
      //Optional max = stream.max(Integer::compareTo);
      //System.out.println(max.get());
      //获取最小值
      //Optional min = stream.min(Integer::compareTo);
      //System.out.println(min.get());
      //获取元素个数
      //long count = stream.count();
      //System.out.println(count);
      //变为数组
      //Object[] objects = stream.toArray();
      //获取第一个
      //Optional first = stream.findFirst();
      //System.out.println(first.get());
      //遍历获取每一个元素
      stream.forEach(System.out::println);
  }
}
4. 创建Stream并行流

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

Java 8 中将并行进行了优化,我们可以很容易的对数据进行并行操作。 Stream API 可以声明性地通过 parallel() 与sequential() 在并行流与串行流之间进行切换 。

4.1 获取并行流
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()));
}
4.2 Fork/Join

将一个大任务,进行拆分(fork)成若干个小任务(拆到不可再拆时),再将一个一个小任务的运算结果进行 join 汇总 。二—18:Java新特性_第2张图片

4.3 求和

使用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));
  }
}

五、 Optional

1. 介绍

Optional存在的意义就是简化为了防止空指针而进行的if…else等判断的代码。

提供了全局value属性存储值。

2. 获取Optional对象
  1. 通过Optional.of()方法,传递的参数的值为null,会出现空指针异常。

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

People people = null;
Optional<People> optional = Optional.of(people); //java.lang.NullPointerException
Optional<People> optional1 = Optional.ofNullable(people); //创建Optional对象
3. Optional使用
3.1 参数为null, 使用创建参数类型的对象
  1. 如果传递参数值为null, 可以使用orElse(T)或orElseGet(Supplier)进行参数的实例化。

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

public class Demo {
  public static void main(String[] args) {
    //People people = null;
    People people =new People();
    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();
  }
}
3.2 获取Optional中存储的值

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

People people =new People();
Optional<People> optional = Optional.ofNullable(people);
People people = optional.get();
3.3 判断传递的参数是否为null

ifPresent(Consumer 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());
3.4 filter() 过滤
Optional<People> people1 = Optional.ofNullable(people).filter(x -> {
            if (x.getA() > 60) {
                return true;
            } else {
                return false;
            }
        });
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, 打印结果

六、总结

1、函数式接口 :

接口中只有一个抽象方法,default,static,Object中继承的不算。

2、Lambda表达式

作用:简化实现接口,实现接口中的抽象方法

使用要求:接口必须为函数式接口

简化:
​ 1.参数类型可以省略不写,运行时自动类型推断。
​ 2.如果只有一个参数,()可以省略不写。
​ 3.方法体中只有一条执行语句,{}可以省略不写。
​ 4.如果执行语句就一条return语句,return 和 {}可以 同时省略不写。

3、方法引用

作用:简化Lambda表达式。

使用要求:

1.使用了Lambda表达式

2.方法体中只有一个调用方法的执行语句.

3.函数式接口中的 方法 和 方法体中调用的方法。

​ 1.函数式接口中的 方法的参数 和 方法体中调用的方法的参数 个数必须相同。

​ 2.函数式接口中的方法有返回值,方法体中调用的方法也必须有返回值.

​ 3.函数式接口中 方法的参数类型 和 方法体中调用的方法的类型相同 或为其的子类

​ 4.函数式接口方法的返回值和方法体中调用的方法的返回值类型相同 或 为其参数返回值类型的父类

4、流式编程

1.创建Stream串行流

else {
return false;
}
});


##### 6.3.5 map()

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

六、 总结

1、Lambda表达式

作用:简化实现接口,实现接口中的抽象方法

使用要求:接口必须为函数式接口

简化:
​ 1.参数类型可以省略不写,运行时自动类型推断。
​ 2.如果只有一个参数,()可以省略不写。
​ 3.方法体中只有一条执行语句,{}可以省略不写。
​ 4.如果执行语句就一条return语句,return 和 {}可以同时省略不写。

2、函数式接口 :

接口中只有一个抽象方法,default,static,Object中继承的不算。
作用:
在Java中主要用在Lambda表达式和方法引用(想使用Lambda表达式,接口必须为函数式接口)。

3、方法引用

作用:简化Lambda表达式。

使用要求:

1.使用了Lambda表达式

2.方法体中只有一个调用方法的执行语句.

3.函数式接口中的 方法 和 方法体中调用的方法。

​ 1.函数式接口中的 方法的参数 和 方法体中调用的方法的参数 个数必须相同。

​ 2.函数式接口中的方法有返回值,方法体中调用的方法也必须有返回值.

​ 3.函数式接口中 方法的参数类型 和 方法体中调用的方法的类型相同 或为其的子类

​ 4.函数式接口方法的返回值和方法体中调用的方法的返回值类型相同 或 为其参数返回值类型的父类

4、流式编程

1.创建Stream串行流
Stream stream = 对象名.stream();

2.创建Stream并行流
Stream stream = 对象名.parallelStream();

你可能感兴趣的:(java,开发语言)