如今 Java 按照周期发布,每六个月就更新一次。所谓 “小步快跑,快速迭代”java
JDK8 是 Java 语言开发的一个主要版本,2014年 3月发布,是 JDK5 以来最具革命性的版本。数据库
新特性简介:api
速度更快 (HashMap 的数据结构更改)数组
代码更少 (Lambda 表达式)缓存
Stream API数据结构
便于并行app
减小控制针异常 (Optional)dom
Nashorn引擎,容许在 JVM 上运行 JS 应用 (使用 jjs.exe JDK 11中没有了)ide
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
,这个包里面有几个经常使用的函数式接口:
这些接口功能就是,不用你再去建立新接口,按照名字的语义。直接用它的就好了,好比:
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);
除了 Lambda,JDK8 另外一个重要改变就是 StreamAPI 了。
StreamAPI 是 Java8 中处理集合的关键抽象概念,它能够指定你但愿对集合进行的操做。能够进行复杂的查找、过滤、映射等操做。使用 StreamAPI 对集合数据,在 java 层面进行操做,就相似于使用 SQL 执行的数据库查询。StreamAPI 提供了一种高效且易于使用的数据处理方式。
在实际开发中,不少数据来自缓存、数据库。咱们就可使用 StreamAPI 在 Java 层面高效简单地处理这些数据。
之前学习的集合,他们的增删改查,都是数据结构、内存层面的,即数据源是随之改变的。
如今的 Stream 操做,是 CPU 层面的,数据源不会随之改变。
使用的步骤:
// 方式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);
// 这是数据源 下面的都是操做这儿的 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);
// 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);