JDK8新特性

一.Lambda表达式

引入:以Thread类为例

public static void main(String[] args) {
    new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("----" + Thread.currentThread());
        }
    }).start();

    // 改造
    new Thread(() -> {System.out.println("--------" + Thread.currentThread()); }).start();
}

lambda表达式的优点:简化匿名内部类的使用,语法更加简洁

匿名内部类语法冗余,体验了Lambda表达式之后,发现Lambda表达式是简化匿名内部类的一种方式

Lambda表达式的规则:

  1. 所包含的一个参数类型是一个接口 并且此接口只有一个抽象方法
  2. 由 括号(())、箭头( ->)、花括号( {})组成

练习:有参数又有返回值

public static void main(String[] args) {
        ArrayList<Person> personArrayList = new ArrayList<>();
        personArrayList.add(new Person("张三",33,"男"));
        personArrayList.add(new Person("李四",13,"女"));
        personArrayList.add(new Person("王五",36,"男"));
        personArrayList.add(new Person("田七",25,"男"));
//        Collections.sort(personArrayList, new Comparator<Person>() {
//            @Override
//            public int compare(Person o1, Person o2) {
//                return o1.getAge()-o2.getAge();
//            }
//        });
        Collections.sort(personArrayList, (Person o1, Person o2) ->{return o1.getAge()-o2.getAge(); });
        System.out.println(personArrayList);
    }

对于自定义的接口当作参数的注意事项:

  • 必须保证此接口只有一个抽象方法
  • 可以在接口类上加上 @FunctionalInterface 注解 用于检查此接口只有一个抽象方法

Lambda表达式的省略写法

​ 在lambda表达式的标准写法基础上

  • 小括号内的参数类型可以省略
  • 如果小括号内如果仅有一个参数,则小括号可以省略
  • 如果大括号内有且仅有一个语句,可以同时省略大括号,return,分号;

例如:

m1(new IUserService() {
    @Override
    public void show(String desc) {

    }
});
m1((String desc)->{
    System.out.println("我是一个标准的Lambda");
});
m1(desc->System.out.println("我是一个简化的Lambda"));
new Thread(()->add(dest,"a")).start();

Lambda的使用前提

​ Lambda表达式的语法是非常简洁的,但是Lambda不是随意使用的,使用时需要注意几个条件

  1. 方法的参数或局部变量类型必须为接口才能使用lambda表达式
  2. 接口中有且仅有一个抽象方法(@FunctionalInterfaces)

Lambda和匿名内部类的区别

  1. 所需参数类型不一样
    • 匿名内部类的类型可以是 类、抽象类、接口
    • Lambda表达式所需要的类型必须是接口
  2. 抽象方法的数量不一样
    • 匿名内部类所需的接口中的抽象方法的数量是随意的
    • Lambda表达式所需要的接口中只能有一个抽象方法
  3. 实现原理不一样
    • 匿名内部类是在编译后生成一个class
    • Lambda表达式是在程序运行的时候动态生成class

二.接口中新增的方法

1.默认方法

为什么要使用默认方法:一个接口需要实现类实现才可以使用,如果需要对接口进行扩展,比如说增加一个方法,那么就需要在所有的实现类中重写此方法,造成代码的冗余。

解决:在接口中使用默认方法

格式:

interface A{
	defalut void 方法名(){};
}

特点:

  • 实现类直接调用接口的默认方法
  • 实现类重写接口中的默认方法
2.静态方法

作用: 同样是为了扩展

格式:

interface A{
	static void 方法名(){};
}

特点:

​ 接口中静态方法在实现类中是不能被重写的,调用的话只能通过接口名来实现:接口名.静态方法名();

3.默认方法与静态方法的区别
  1. 默认方法通过对象调用,静态方法通过接口名调用
  2. 默认方法可以被继承,实现类可以调用或重写接口里的默认方法
  3. 静态方法不可被继承,实现类不能调用和重写接口里的静态方法,只能通过接口名调用

三.函数式接口

​ 我们知道使用Lambda表达式的前提是需要有函数式接口,而Lambda表达式使用时不关心接口名,抽象方法名。只关心抽象方法的参数列表。因此为了让我们使用Lambda表达式更好的方法,在JDK中提供了大量的常用的函数式接口。

@FunctionalInterface
public interface Operate {
    Integer sum();
}
public class FunctionInterfaceTest {
    public static void main(String[] args) {
        sum(()->{
            int[] nums = {1,2,3,4,5,6,7,8,9};
            int sum = nums[0];
            for (int i = 1; i < nums.length; i++) {
                sum+= nums[i];
            }
            return sum;
        });
    }
    public static void sum(Operate operate){
        System.out.println(operate.sum());
    }
}

​ 在 JDK 中有函数式接口,主要在java.util.function包中

Supplier:无参数有返回值,用于生产数据

public static void main(String[] args) {
    max(()->{
        int[] nums = {1,2,3,4,5};
        Arrays.sort(nums);
        return nums[nums.length-1];
    });
}

public static void max(Supplier<Integer> supplier){
    Integer max =  supplier.get();
    System.out.println("最大值"+max);

}

Consumer:有参数无返回值,用于消费数据

public static void main(String[] args) {
    sqrt((Integer num)->{
        System.out.println(Math.sqrt(num));
    });
}
public static void sqrt (Consumer<Integer> consumer){
    consumer.accept(3);
}

andThen 方法:

 public static void consumer(Consumer<Integer> c1,Consumer<Integer> c2){
//        c1.accept(3);
//        c2.accept(3);
        c1.andThen(c2).accept(2);
        // 先调用 c1 在 调用 c2
        
    }

Function:有参数有返回值

public static void main(String[] args) {
    estimate((Integer)->{
       return 3 > 0?true:false;
    });
}
public static void estimate(Function<Integer,Boolean> function){
    System.out.println(function.apply(3));
}

Predicate:有参数返回值为bool值

public static void main(String[] args) {
    equalsFive(num->{return num == 5;});
}
public static void equalsFive(Predicate<Integer> predicate){
    System.out.println(predicate.test(5));
}

四.方法引用

1为什么要有方法引用

方法引用是为了更好的使用 Lambda 表达式

例如:

public class MethodReference {
    public static void main(String[] args) {
        Integer total = sum((nums) -> {
            int sum = nums[0];
            for (int i = 1; i < nums.length; i++) {
                sum += nums[i];
            }
            return sum;
        });
        System.out.println(total);
    }
    
    public static Integer sum(Function<int[],Integer> function){
        int[] nums = {1,2,3,4,5};
        return function.apply(nums);
    }
    public static Integer sumMethod(int[] nums){
        int sum = nums[0];
        for (int i = 1; i < nums.length; i++) {
            sum += nums[i];
        }
        return sum;
    }
}

当使用一个 Lambda 表达式时,发现已经有一个写好的方法,这时候就可以用方法引用。

public class MethodReference {
    public static void main(String[] args) {
        Integer total = sum(MethodReference::sumMethod);
        System.out.println(total);
    }

    public static Integer sum(Function<int[],Integer> function){
        int[] nums = {1,2,3,4,5};
        return function.apply(nums);
    }
    public static Integer sumMethod(int[] nums){
        int sum = nums[0];
        for (int i = 1; i < nums.length; i++) {
            sum += nums[i];
        }
        return sum;
    }
}
2方法引用的格式

符号表示:::

符号说明: 双冒号为方法引用运算符,而他所在的表达式被称为方法引用

应用场景:如果Lambda 表达式所要实现的方案,已经有其他方法存在相同的方案

常见的引用方式:

方法引用在JDK8 中使用相当灵活,有以下几种使用形式:

InstanceName::methodName 对象::方法名

ClassName:: staticMethodName 类名:: 静态方法名

ClassName::methodName 类名:: 普通方法名

ClassName::new 类名::new 调用的构造器

TypeName[]::new String[]:new 调用数组的构造方法

  1. InstanceName::methodName

    public static void main(String[] args) {
    
        Date date = new Date();
        // Lambda 表达式
        Supplier<Long> supplier = ()->date.getTime();
        System.out.println(supplier.get());
        // 方法引用
        Supplier<Long> supplier1 = date::getTime;
        System.out.println(supplier1.get());
    }
    
  2. ClassName:: staticMethodName

    public static void main(String[] args) {
        Supplier<Long> s1 = ()->System.currentTimeMillis();
        Supplier<Long> s2 = System::currentTimeMillis;
        System.out.println(s1.get());
        System.out.println(s2.get());
    }
    
  3. ClassName::methodName

    同上

  4. ClassName::new : 返回一个指定类型的对象

    public static void main(String[] args) {
        BiFunction<String,Integer,Person> f1 = (name,age)->new Person(name,age);
        Person p1 = f1.apply("张三",13);
        System.out.println(p1);
    
        BiFunction<String,Integer,Person> f2 = Person::new;
        Person p2 = f2.apply("李四", 14);
    
        System.out.println(p2);
    }
    
  5. ClassName[]::new. : 返回值一个指定长度的数组

    public static void main(String[] args) {
        Function<Integer,String[]> f1 = len->new String[len];
        String[] a1 = f1.apply(3);
        System.out.println(a1.length);
        Function<Integer,String[]> f2 = String[]::new;
        String[] a2 = f2.apply(4);
        System.out.println(a2.length);
    }
    
    
    小结:

    方法引用是对 Lambda表达式特定情况下的一种缩写方式,他使得我们的Lambda表达式更加精简,也可以理解为Lambda表达式的缩写形式, 不过要注意的是方法引用只能引用已存在的方法

五.StreamAPI

先看一个例子:

public static void main(String[] args) {
    List<String> list = Arrays.asList("张三","张三丰","李四","王五");
    List<String> new1 = new ArrayList<>();
    for (String s : list) {
        if(s.startsWith("张")){
            new1.add(s);
        }
    }
    ArrayList<String> new2 = new ArrayList<>();
    for (String s : new1) {
        if (s.length() == 2) {
            new2.add(s);
        }
    }
    System.out.println(new2);

    
}

为了减少for循环的使用,引入了 StreamAPI

public static void main(String[] args){
	List<String> list = Arrays.asList("张三","张三丰","李四","王五");
	list.stream().filter(s->s.startsWith("张"))
            .filter(s->s.length() == 2)
            .forEach(System.out::println);
}

一.Stream流式思想的概述

注意: Stream 和 IO流(InputStream/OutputStream)没有任何关系,请暂时忘记对传统IO流的固有印象

Stream流式思想类似工厂车间的“生产流水线”,Stream不是一种数据结构,不保存数据,而是对数据进行加工处理。Stream可以看作是流水线上的一个工序。在流水线上,通过多个工序让一个原材料加工成一个商品。

StreamAPI可以快速完成许多复杂的操作,如筛选,切片,映射,查找,去除重复,统计,匹配,和规约。

二.流的获取方式

1.根据Collection获取

​ 首先,java.util.Collection接口中加入了default方法 stream,也就是说Collection接口下的所有实现都可以通过stream方法来获取Stream流

public static void main(String[] args) {
    List<String> list = Arrays.asList("1","2");
    Set<String> set = new HashSet<>();
    Stream<String> listS = list.stream();
    Stream<String> setS = set.stream();
    listS.forEach(System.out::println);
    setS.forEach(System.out::println);
}

​ 但是Map接口没有实现Collection接口,? 我们可以根据Map获取对应的key value集合。

Map<String,Integer> map = new HashMap<>();
Set<String> strings = map.keySet();
Collection<Integer> values = map.values();
Set<Map.Entry<String, Integer>> entries = map.entrySet();
// 然后在获取流
2.通过Stream的of方法获取

Stream 提供了对于数组的 直接操作方法

Stream<Integer> stream = Stream.of(1, 2, 3, 4);
stream.forEach(System.out::println);

三.Stream中的常用方法介绍

常用方法

方法名方法作用返回值类型方法种类
count统计个数long终结
forEach逐一处理void终结
filter过滤Stream函数拼接
limit取用前几个Stream函数拼接
skip跳过前几个Stream函数拼接
map映射Stream函数拼接
concat组合Stream函数拼接

终结方法:返回值类型不再是Stream类型的方法,不再支持链式调用。

非终结方法:返回值类型仍然是 Stream 类的方法,支持链式调用

Stream注意事项(重要)

1.Stream 智能操作一次

2.Stream 方法返回的是新的流

3.Stream不调用终结方法,中间的操作不会执行

常用方法介绍:

1.foreach

void forEach(Consumer<? super T> action);

2.count

long count();

3.filter

Stream<T> filter(Predicate<? super T> predicate);

4.skip

Stream<T> skip(long n);

5.limit

Stream<T> limit(long maxSize);
Stream.of("a1", "a2", "b1", "b2", "b3").filter(s -> s.startsWith("b"))
        .skip(1)
        .limit(2)
        .forEach(System.out::println);

6.map

<R> Stream<R> map(Function<? super T, ? extends R> mapper);
        Stream.of("1","2","3")
//                .map(str->Integer.parseInt(str))
                .map(Integer::parseInt)
                .forEach(System.out::println);

7.sorted

Stream<T> sorted();
Stream<T> sorted(Comparator<? super T> comparator);
        Stream.of("1","2","3","6")
//                .map(str->Integer.parseInt(str))
                .map(Integer::parseInt)
                .sorted((o1,o2)->o2-o1)
                .forEach(System.out::println);

8.distinct

Stream<T> distinct();

基本数据类型可以直接去重,对于引用类型来说需要 重写 equals 和 hashcode 方法

      Stream.of("1","3","3","6")
//                .map(str->Integer.parseInt(str))
                .map(Integer::parseInt)
                .sorted((o1,o2)->o2-o1)
                .distinct()
                .forEach(System.out::println);

9.match:终结方法

10.find:终结方法

11.max min:终结方法

12reduce:求和 最大值 最小值。终结方法

13mapToInt:代表方法,因为包装类使用时浪费内存,用于转换为对应的基本数据类型

14concat: 静态方法,用于将流 进行 合并

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值