JDK8 新特性

Java 8

如今 Java 按照周期发布,每六个月就更新一次。所谓 “小步快跑,快速迭代”java

JDK8 是 Java 语言开发的一个主要版本,2014年 3月发布,是 JDK5 以来最具革命性的版本。数据库

新特性简介:api

  1. 速度更快 (HashMap 的数据结构更改)数组

  2. 代码更少 (Lambda 表达式)缓存

  3. Stream API数据结构

  4. 便于并行app

  5. 减小控制针异常 (Optional)dom

  6. Nashorn引擎,容许在 JVM 上运行 JS 应用 (使用 jjs.exe JDK 11中没有了)ide




Lambda 表达式

Lambda 表达式是对象,而不是函数。函数

// 之前建立接口实现类的对象

public interface MyInterface {
    void testMethod();
}


class MyClass implements MyInterface{

    @Override
    public void testMethod() {
        System.out.println("重写接口");
    }
}


class Demo {
    public static void main(String[] args) {
        MyClass mc = new MyClass();
        mc.testMethod();
    }
}
// 使用匿名内部类建立对象

public interface MyInterface {
    void testMethod();
}

class Demo {
    public static void main(String[] args) {
        MyInterface ms = new MyInterface() {
            @Override
            public void testMethod() {
                System.out.println("Test");
            }
        };

        ms.testMethod();
    }
}
// 利用 Lambda 表达式

public interface MyInterface {
    void testMethod();
}

class Demo {
    public static void main(String[] args) {
        MyInterface ms = () -> System.out.println("Lambda");
        ms.testMethod();
    }
}

Lambda 本质就是 Java 中接口的一个实例,即接口的实现类的具体的对象。


语法:
-> :         箭头操做符
-> 左侧:      Lambda 形参列表 对应接口中抽象方法的形参列表
-> 右侧:      重写方法的,方法体的具体内容
// 无参,无返回值. 方法体中只有一条语句, 花括号能够省略
MyInterface my = () -> {System.out.println("PS");};
my.testMethod();

// 有一个参数, 无返回值
MyInterface my = (int x) -> System.out.println("PS" + x);
my.testMethod(12);

// 类型推断, 能够去掉参数类型
MyInterface my = (x) -> System.out.println("ps" + x);
my.testMethod(12);

// 一个参数的时候 小括号也能不写
MyInterface my = x -> System.out.println("ps" + x);
my.testMethod(12);
// 多个参数且带返回值
public interface MyInterface {
    String testMethod(int age, int num);
}

class Demo {
    public static void main(String[] args) {

        MyInterface my = (x,y) -> {
            System.out.println("Test");
            return "Hello" + "ps" + x;
        };
        System.out.println( my.testMethod(12,17));
    }
}
// 若是只剩一个 return 的时候
MyInterface my = (x,y) -> {
    return "Hello" + "ps" + x;
};


// 能够直接这样
MyInterface my = (x,y) -> "Hello" + "ps" + x;




函数式接口

Lambda 必须依赖一个接口,只有一个抽象方法的接口,称为函数式接口

@FunctionalInterface 此注解在接口中有多个抽象方法时,便会爆红。不过除了 Object 类中的抽象方法。

JDK8 中新加入了一个包 java.util.function ,这个包里面有几个经常使用的函数式接口:

  1. Consumer 消费型接口
  2. Function 函数型接口
  3. Predicate 判定型接口
  4. Supplier 供给型接口

这些接口功能就是,不用你再去建立新接口,按照名字的语义。直接用它的就好了,好比:

public static void main(String[] args) {

    Consumer<Double> c = new Consumer<Double>() {
        @Override
        public void accept(Double o) {
            System.out.println("accept");
        }
    };

    c.accept(1.00);
}


// 简洁一下
Consumer<Double> c = o -> System.out.println("accept");
public static void main(String[] args) {

    List<String> list = Arrays.asList("asss", "aaaa", "vvvv", "vvv");

// 简化前
//        List<String> result = filterString(list, new Predicate<String>() {
//            @Override
//            public boolean test(String o) {
//                return o.length() < 4;
//            }
//        });


// 简化后
    List<String> result = filterString(list, o -> o.length() < 4);

    System.out.println(list);
    System.out.println(result);
}

public static List<String> filterString(List<String> waitTest, Predicate<String> predicate) {
    List<String> filterList = new ArrayList<>();
    for (String item : waitTest) {
        if (predicate.test(item)) {
            filterList.add(item);
        }
    }
    return filterList;
}




方法引用

当要传递给 Lambda 体的操做,已经有实现的方法了,可使用方法引用。

使用操做符 :: 将类(或者对象)与方法名分开。

以下三种状况使用:

// 1. 对象::实例方法名 (非静态方法)

class Student {
    String name;
    Integer age;

    public Student(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() { return name; }

    public Integer getAge() { return age; }
}

class Demo {
    public static void main(String[] args) {
        Student student = new Student("jc", 18);

        // 第一种写法
        Supplier<String> s1 = () -> {
            return student.getName();
        };
        System.out.println(s1.get());

        // 第二种写法
        Supplier<String> s2 = () -> student.getName();
        System.out.println(s2.get());

        // 方法引用的写法
        Supplier<String> s3 = student::getName;
        System.out.println(s3.get());
    }
}

只要接口的抽象方法和具体的实现方法,参数同样、返回值同样,就能够这样用。上面代码中,student.getName()Supplier 中的 get() 就知足此条件。

// 2. 类::静态方法名

// 写法一
Comparator<Integer> c1 = (x, y) -> Integer.compare(x, y);
System.out.println(c1.compare(12, 20));

// 写法二
Comparator<Integer> c2 = Integer::compareTo;
System.out.println(c2.compare(12, 20));
// 3. 类::实例方法名 (非静态方法)

// 写法一
BiPredicate<String, String> bp = (x, y) -> x.equals(y);
System.out.println(bp.test("abc", "abc"));

// 写法二
BiPredicate<String, String> bp2 = String::equals;
System.out.println(bp2.test("abc", "abc"));

这个就有点奇怪了,显然 equals() 的参数列表,与下方 test() 的明显不一样。与上面的说明并不符啊。

其实 (x, y) -> ... 中第一个参数表示:调用方法的调用者

第二个参数就是调用方法的实际传入参数。只要是这种第一个参数用来调用方法的,第二个参数是传入参数的,就能使用 类::实例方法名 的方式




构造器引用

利用供给型接口,返回一个对象

class Student {
    String name;

    public Student() { }

    public Student(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                '}';
    }
}

class Demo {
    public static void main(String[] args) {
        // 第一种写法
        Supplier supp = () -> new Student();
        Object stuent = supp.get();
        System.out.println(stuent);
        
        // 第二种写法
        Supplier supp = Student::new;
        Object stuent = supp.get();
        System.out.println(stuent);
        
        // 试一个能够传参的 能够本身看下 Function 注释
        Function<String, Student> f = Student::new;
        Object o = f.apply("zhouxuan");
        System.out.println(o);
    }
}

不过须要注意的是,实现的方法的参数,和接口的方法的参数必须一至。为啥上面调用的是空构造器,由于 get() 没有参数,那构造函数也调用的是无参的。

// 最后再看个建立数组的例子

// Lambda 表达式
Function<Integer, String[]> f1 = (x) -> new String[x];
String[] r1 = f1.apply(8);
System.out.println(r1.length);

// 数组引用
Function<Integer, String[]> f2 = String[]::new;
String[] r2 = f2.apply(5);
System.out.println(r2.length);




StreamAPI

除了 Lambda,JDK8 另外一个重要改变就是 StreamAPI 了。

StreamAPI 是 Java8 中处理集合的关键抽象概念,它能够指定你但愿对集合进行的操做。能够进行复杂的查找、过滤、映射等操做。使用 StreamAPI 对集合数据,在 java 层面进行操做,就相似于使用 SQL 执行的数据库查询。StreamAPI 提供了一种高效且易于使用的数据处理方式。

在实际开发中,不少数据来自缓存、数据库。咱们就可使用 StreamAPI 在 Java 层面高效简单地处理这些数据。

之前学习的集合,他们的增删改查,都是数据结构、内存层面的,即数据源是随之改变的。

如今的 Stream 操做,是 CPU 层面的,数据源不会随之改变。

使用的步骤:

  1. 先产生一个流 Stream (一个数据源获取一个流)
  2. 中间链式操做 (对数据源的数据进行处理)
  3. 产一个新流 (进行终止操做,此时去执行上一步的中间操做链,那就至关因而晚一步延迟执行)




建立 Stream 的几种方式:

// 方式1:Collection 接口的方法
        Collection<String> col = new ArrayList<>();
        // 获取串行流
        Stream<String> s1 = col.stream();
        // 获取并行流
        Stream<String> s2 = col.parallelStream();


        // 方式2:Arrays 中的 Stream 方法
        IntStream s3 = Arrays.stream(new int[]{1, 2, 4});


        // 方式3:Stream 中的 of 方法
        Stream<String> s4 = Stream.of("aa", "cc", "dd");


        // 方式4:建立无限流 (就是每次 +2 无限加, 很神奇 执行一下就知道了)
        Stream<Integer> s5 = Stream.iterate(2, (x) -> x + 2);
        s5.forEach(System.out::println);

        // 也是建立无限流,产生无限多个
        Stream<Double> s6 = Stream.generate(() -> Math.random());
        s6.forEach(System.out::println);




StreamAPI 的中间操做

// 这是数据源 下面的都是操做这儿的        
    List<Student> list = Arrays.asList(
                new Student("lili", 19),
                new Student("ffff", 20),
                new Student("lll", 21),
                new Student("tttt", 22),
                new Student("aaaa", 23),
                new Student("cccc", 24)
        );
// filter 过滤

        // 之前这么写
        Iterator<Student> it = list.iterator();
        while (it.hasNext()) {
            Student item = it.next();
            if (item.getAge() > 21) {
                System.out.println(item);
            }
        }


        // 这是如今
        // 1. 建立 Stream
        Stream<Student> s = list.stream();

        // 2. 中间操做
        Stream<Student> s1 = s.filter((x) -> x.getAge() > 21);

        // 3. 终止操做
        s1.forEach(System.out::println);


        // 再体验下链式操做
        list.stream()
            .filter((x) -> x.getAge() > 21)
            .forEach(System.out::println);
// limit, 截断(短路)  
        list.stream()
                .filter((x) -> {
                    System.out.println("正在进行 " + x.getName());
                    return x.getAge() > 21;
                })
                // 只要找到两个 知足了条件 后面就无论了
                .limit(2)
                .forEach(System.out::println);
// skip, 这个和 limit 相反        
        list.stream()
                .filter((x) -> {
                    System.out.println("正在进行 " + x.getName());
                    return x.getAge() > 21;
                })
                // 跳过前两个知足条件的
                .skip(2)
                .forEach(System.out::println);
// distinct, 去重, 可是须要重写 hashCode() 和 equals()         
        list.stream()
                .distinct()
                .forEach(System.out::println);
// map, 映射   把原先的 Student 都映射成单个的名字     
        list.stream()
                // .map((x) -> x.getName())
                .map(Student::getName)
                .forEach(System.out::println);
// 例子: 输出 age > 21 的名字
        list.stream()
                .map((x) -> {
                    if (x.getAge() > 21) {
                        return x.getName();
                    }
                    return null;
                })
                .filter((x) -> x != null)
                .forEach(System.out::println);
// sorted 排序, 这里暂时新建一个 list
        List<Integer> list = Arrays.asList(1, 3, 6, 2, 8, 11);

        list.stream()
                .sorted()
                .forEach(System.out::println);
// 例子: 按照学生年龄排序
        list.stream()
                .sorted((x, y) -> x.getAge() - y.getAge())
                .forEach(System.out::println);




StreamAPI 的终止操做

// allMatch, 是否是全部 Student 的 age 都知足条件
        boolean result = list.stream()
                .allMatch((x) -> x.getAge() > 20);
        System.out.println(result);
// anyMatch, 有一个知足条件就返回 true
        boolean result = list.stream()
                .anyMatch((x) -> x.getAge() > 21);
        System.out.println(result);
// noneMatch, 都不知足条件就返回 true        
        boolean result = list.stream()
                .noneMatch((x) -> x.getAge() > 100);
        System.out.println(result);
// 还有一些关于查找和匹配的
        findFirst() 返回第一个
        findAny() 返回任意一个    
        count() 获取此流的元素数
        max() 获取此流中的最大元素,须要传入 Comparator
        min() 获取此流中的最小元素,须要传入 Comparator
        forEach() 对此流的每一个元素执行操做


接着是规约操做,下面的代码的意思是:reduce(0,.. 这里的 0 做为初始值,初始值赋值给 (x, y) ... 中的 x, y 是每次遍历获得的 age,这样每次将 y 加进 x 中,就能获得年龄的总和了。最后使用方法引用,语法更简便。

Integer sumAge = list.stream()
                .map(Student::getAge)
                // .reduce(0, (x, y) -> x + y);
                .reduce(0, Integer::sum);
        System.out.println(sumAge);


最后一个终止操做,收集操做。用于给 Stream 中的元素进行汇总。下面是手机全部 Student 的 name,返回一个 集合。

// List, 简单的 Demo 献给你们
        List<String> allName = list.stream()
                .map(Student::getName)
                .collect(Collectors.toList());
        System.out.println(allName);

// Set, 固然可使用 Set, 固然 Set 是惟一的哦
        Set<String> allName = list.stream()
                .map(Student::getName)
                .collect(Collectors.toSet());
        System.out.println(allName);

// Map, 最后是 Map
        Map<String, Integer> map = list.stream()
                .collect(Collectors.toMap((k) -> k.getName(), (v) -> v.getAge()));
        System.out.println(map);

// count, 获得学生总数
        Long sum = list.stream()
                .collect(Collectors.counting());
        System.out.println(sum);

// averaging, 获得平均年龄
        Double d = list.stream()
                .collect(Collectors.averagingDouble((x) -> x.getAge()));
        System.out.println(d);

// sum, 年龄总和
        Double d = list.stream()
                .collect(Collectors.summingDouble((x) -> x.getAge()));
        System.out.println(d);

// max, 获取年龄最大的, 固然也有 minBy()
        Optional<Student> op = list.stream()
                .collect(Collectors.maxBy((x, y) -> Double.compare(x.getAge(), y.getAge())));
        System.out.println(op.get());
// 分组, 按年龄分组        
        Map<Integer, List<Student>> map = list.stream()
                .collect(Collectors.groupingBy((x) -> x.getAge()));
        Set<Map.Entry<Integer, List<Student>>> entries = map.entrySet();
        for (Map.Entry<Integer, List<Student>> e : entries) {
            System.out.println(e.getKey() + "--" + e.getValue());
        }


// 分区, 知足条件的一个区 不知足条件的一个区
        Map<Boolean, List<Student>> map = list.stream()
                .collect(Collectors.partitioningBy((x) -> x.getAge() > 20));
        Set<Map.Entry<Boolean, List<Student>>> entries = map.entrySet();
        for (Map.Entry<Boolean, List<Student>> e : entries) {
            System.out.println(e.getKey() + "--" + e.getValue());
        }

// 拼接, 将全部的名字进行拼接
        String str = list.stream()
                .map((x) -> x.getName())
                .collect(Collectors.joining());
        System.out.println(str);
相关文章
相关标签/搜索