文章目录
Java8的新特性常用讲解
Java8 新特性重点思维导图
Java8的概述
- Java8是 Java 语言的一个重要版本,该版本于2014年3月发布,是自Java5以来最具革命性的版 本,这个版本包含语言、编译器、库、工具和JVM等方面的十多个新特性。
函数式接口
-
函数式接口主要指只包含一个抽象方法的接口,如:java.lang.Runnable、java.util.Comparator 接口等。
-
Java8提供@FunctionalInterface注解来定义函数式接口,若定义的接口不符合函数式的规范便会报错。
-
Java8中增加了java.util.function包,该包包含了常用的函数式接口,具体如下:
接口名称 方法声明 功能介绍 Consumer void accept(T t) 根据指定的参数执行操作 Supplier T get() 得到一个返回值 Function<T,R> R apply(T t) 根据指定的参数执行操作并返回 Predicate boolean test(T t) 判断指定的参数是否满足条件。 -
使用匿名内部类调用方法
-
Runnable 接口中的方法 run 的使用(既没有参数 也没有返回值的)
// 1.匿名内部类的语法格式: 父类/接口类型 引用变量名 = new 父类/接口类型() {}; // 2.既没有参数 也没有返回值的 Runnable 接口中的方法 run Runnable runnable = new Runnable() { @Override public void run() { // 打印结果是 Runnable 接口中的 run 方法 既没有参数 也没有返回值 System.out.println("Runnable 接口中的 run 方法 既没有参数 也没有返回值"); } }; // 2.1 方法的调用 runnable.run();
-
Consume 接口中的方法 accept 的使用(有参数 没有没有返回值的 参数支持泛型)
// 3.有参数 没有没有返回值的 Consume 接口中的方法 accept 支持泛型 Consumer consumer = new Consumer() { @Override public void accept(Object o) { // Consume 接口中的 accept 方法 有参数 没有返回值我是参数 System.out.println("Consume 接口中的 accept 方法 有参数 没有返回值" + o); } }; // 3.1方法的使用 consumer.accept("我是参数");
-
Supplier 接口中的方法 get 的使用 (没有参数 但有返回值的 返回值类型支持泛型)
// 4.没有参数 担有返回值的 Supplier 接口中的方法 get 支持泛型 Supplier supplier = new Supplier() { @Override public Object get() { // Supplier 接口中的 get 方法 无参数 但是有返回值 System.out.println("Supplier 接口中的 get 方法 无参数 但是有返回值"); return "我是返回值"; } }; // 4.1方法的使用 Object o = supplier.get(); System.out.println("返回值是 " + o); // 返回值是 我是返回值
-
Function 接口中的方法 apply的使用 ( 有参数 也有返回值的 参数 和 返回值类型 都支持泛型)
// 5.有参数 也有返回值的 Function 接口中的方法 apply 参数 和 返回值 都支持泛型 Function function = new Function() { @Override public Object apply(Object o) { // Function 接口中的方法 apply 方法 有参数 我是参数 System.out.println("Function 接口中的方法 apply 方法 有参数 " + o); return "我是返回值"; } }; // 5.1 方法的使用 Object o1 = function.apply("我是参数"); System.out.println(o1); // 我是返回值
-
Predicate 接口中的方法 test 的使用( 返回值是布尔值 参数 支持泛型 )
// 6.判断制定参数是否满足条件 返回值是布尔值 参数 和支持泛型 Predicate predicate = new Predicate() { @Override public boolean test(Object o) { return "bye".equals(o); } }; // 6.1方法的使用 boolean flag = predicate.test("bye"); // Predicate 中的 test 方法的返回值是 true System.out.println("Predicate 中的 test 方法的返回值是 " + flag);
-
-
Lambda表达式
-
Lambda 表达式是实例化函数式接口的重要方式,使用 Lambda 表达式可以使代码变的更加简洁 紧凑
-
lambda表达式:参数列表、箭头符号->和方法体组成,而方法体中可以是表达式,也可以是语句 块。
-
语法格式:(参数列表) -> { 方法体; } - 其中()、参数类型、{} 以及return关键字 可以省略。
-
lambda表达式的使用 简写上面代码
-
Runnable 接口中的方法 run 使用 lambda
// lambada 表达式的 语法格式是 () -> {方法体} 如果是 只有一个参数类型/语句时 {} return ;() 都可以省略 Runnable runnable1 = () -> System.out.println("Runnable 接口中的 run 方法 既没有参数 也没有返回值"); runnable1.run();
-
Consume 接口中的方法 accept 的使用 lambda
// 有参数的函数式接口 使用lambda表达式 Consumer consumer1 = o -> System.out.println("Consume 接口中的 accept 方法 有参数 没有返回值" + o); consumer1.accept("我是参数");
-
Supplier 接口中的方法 get 的使用 lambda
// 有参数 没有返回值得方法引用 Supplier supplier1 = () -> { System.out.println("Supplier 接口中的 get 方法 无参数 但是有返回值"); return "我是返回值"; };
-
Function 接口中的方法 apply 的使用 lambda 表达式
Function function1 = o -> { System.out.println("Function 接口中的方法 apply 方法 有参数 " + o); return "我是返回值"; }; Object o2 = function.apply("我是参数"); System.out.println(o2); // 我是返回值
-
方法引用(其实就是lambda 表达式的简化)
-
方法引用主要指通过方法的名字来指向一个方法而不需要为方法引用提供方法体,该方法的调用交 给函数式接口执行。
-
方法引用使用一对冒号 :: 将类或对象与方法名进行连接,通常使用方式如下:
-
对象的非静态方法引用
ObjectName :: MethodName
-
引用方法 对象的非静态方法
-
引用方法 对象的非静态方法 匿名内部类 和 Lambda 表达式的对比(没有参数 和 没有返回值)
// 1.使用匿名内部类的方式通过函数式接口Runnable中的方法实现Person类中show方法的调用 Person person = new Person("张三",20); Runnable runnable = new Runnable() { @Override public void run() { person.show(); } }; runnable.run(); // 没事 你好啊 // 2.使用lambda 表达式实现对person类中show方法的调用 Runnable runnable1 = () -> person.show(); runnable.run(); // 没事 你好啊 // 3.使用方法的引用实现Person 类中方法中 show 方法的调用 Runnable runnable2 = person :: show; // 注意由于run中的方法 和 show中的方法 返回值类型 参数列表相同 所以可以使用方法的引用 runnable.run(); // 没事 你好啊
Person 类中的show方法
public void show() { System.out.println("没事 你好啊"); }
-
引用方法 对象的非静态方法(有参 没有返回值)
// 4.使用方法引用 有参数但是没有返回值类型的实现 Consumer<String> consumer = person :: setName; consumer.accept("李四"); System.out.println(person); // Person{name='李四', age=20}
-
方法引用 (无参 有返回值)
// 5.使用方法引用 无参 有返回值 Supplier<String> get = person::getName; System.out.println(get.get()); // 李四
-
-
-
类的静态方法引用
ClassName :: StaticMethodName
代码演示
// 6.使用匿名内部类的方式通过函数式接口Function 实现 Integer 中的静态方法的parseInt() 调用 Function<String, Integer> function = new Function<String, Integer>() { @Override public Integer apply(String s) { return Integer.parseInt(s); } }; System.out.println(function.apply("123")); // 123 // 7.lambda表达式实现 Function<String, Integer> function1 = s -> Integer.parseInt(s); System.out.println(function.apply("123")); // 123 // 8.使用引用方法实现 Function<String, Integer> function2 = Integer :: parseInt; System.out.println(function.apply("123")); // 123 // 9.使用匿名内部类的方式通过函数式编程Comparator 中的方法 实现 Integer 类中 compare 方法的调用 Comparator<Integer> comparator = new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { // 升序的排列 return Integer.compare(o1, o2); } }; System.out.println(comparator.compare(1, 2)); // -1 // 10.使用lambda 表达式的方式实现 Comparator<Integer> comparator1 = (o1, o2) -> Integer.compare(o1, o2); System.out.println(comparator1.compare(1, 2)); // -1 // 11.使用引用方法的方式 实现 Comparator<Integer> comparator2 = Integer::compare; System.out.println(comparator2.compare(1, 2)); // -1
-
类的非静态方法引用
ClassName :: MethodName
// 9.使用匿名内部类的方式通过函数式编程Comparator 中的方法 实现 Integer 类中 compareto 方法的非静态调用 // 其中一个参数对象作为调用对象来调用方发时 可以用上述方法 Comparator<Integer> comparator3 = new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { // 升序的排列 return o1.compareTo(o2); } }; System.out.println(comparator3.compare(1, 2)); // -1 // 10.使用lambda 表达式的方式实现 Comparator<Integer> comparator4 = (o1, o2) -> o1.compareTo(o2); System.out.println(comparator4.compare(1, 2)); // -1 // 11.使用引用方法的方式 实现 Comparator<Integer> comparator5 = Integer::compareTo; System.out.println(comparator5.compare(1, 2)); // -1
-
构造器的引用
ClassName :: new
-
使用无参方式构造对象
// 12.使用匿名内部类的方式通过Supplier 函数接口的创建无参的person 并返回 Supplier<Person> supplier = new Supplier<Person>() { @Override public Person get() { return new Person(); } }; System.out.println(supplier.get()); // Person{name='null', age=0} // 13.使用lambda表达式实现 Supplier<Person> supplier1 = () -> new Person(); System.out.println(supplier1.get()); // Person{name='null', age=0} // 14.使用引用方法实现 Supplier<Person> supplier2 = Person::new; System.out.println(supplier2.get()); // Person{name='null', age=0}
-
使用2个参数的有参构造创建对象
// 14.使用匿名内部类的方式有参创建对象 并返回 BiFunction<String, Integer, Person> biFunction = new BiFunction<String, Integer, Person>() { @Override public Person apply(String s, Integer integer) { return new Person(s,integer); } }; System.out.println(biFunction.apply("张三", 20)); // Person{name='张三', age=20} // 15.使用lambda 表达式 的方式实现 BiFunction<String, Integer, Person> biFunction1 = (s, i) -> new Person(s,i); System.out.println(biFunction.apply("张三", 20)); // Person{name='张三', age=20} // 16.使用引用方法的的方式实现 BiFunction<String, Integer, Person> biFunction2 = Person::new; System.out.println(biFunction.apply("张三", 20)); // Person{name='张三', age=20}
-
-
数组的引用
TypeName[] :: new
-
使用引用方法创建Person类型的数组
// 17.使用匿名内部类实现的方式通过Function函数式接口创建指定数量的Person类型的对象数组并返回 Function<Integer,Person[]> function3 = new Function<Integer, Person[]>() { @Override public Person[] apply(Integer integer) { return new Person[integer]; } }; Person[] pArr = function3.apply(4); System.out.println(Arrays.toString(pArr)); // [null, null, null, null] // 18.使用Lambda表达式实现上述功能 Function<Integer,Person[]> function4 = integer -> new Person[integer]; Person[] pArr1 = function4.apply(4); System.out.println(Arrays.toString(pArr1)); // [null, null, null, null] // 19.使用引用方法实现上述功能 Function<Integer,Person[]> function5 = Person[] :: new; Person[] pArr2 = function5.apply(4); System.out.println(Arrays.toString(pArr2)); // [null, null, null, null]
-
-
-
方法引用是在特定场景下lambda表达式的一种简化表示,可以进一步简化代码的编写使代码更加 紧凑简洁,从而减少冗余代码。
Stream接口
- 案例题目:
准备一个List集合并放入Person类型的对象,将集合中所有成年人过滤出来放到另外一个集合并打 印出来。
// 1.准备一个List集合 泛型是 Person 的对象 使用引用方法的形式
Supplier<List<Person>> supplier2 = ArrayList::new;
List<Person> personList2 = supplier2.get();
// 2.使用方法的引用创建3个Person类型的对象 并放到数组中
BiFunction<String,Integer,Person> biFunction = Person::new;
// 2.1使用apply的方式创建对象3个Person类型的对象 并放到数组中
Person person1 = biFunction.apply("张三", 18);
Person person2 = biFunction.apply("李四", 20);
Person person3 = biFunction.apply("王五", 17);
Person person4 = biFunction.apply("赵六", 18);
// 2.2往集合中添加person对象
personList2.add(person1);
personList2.add(person2);
personList2.add(person3);
personList2.add(person4);
// 3.准备一个新的集合 用来存放与年龄大于18的person类
List<Person> personList3 = new ArrayList<>();
// 4.遍历personList2中的集合 并将 年龄>=18的 person对象放入到 personList3
for (Person person : personList2) {
if (person.getAge() >= 18){
personList3.add(person);
}
}
// 5.分别打印两个集合中的person类型的数据
// [Person{name='张三', age=18}, Person{name='李四', age=20}, Person{name='王五', age=17}, Person{name='赵六', age=18}]
System.out.println(personList2);
// [Person{name='张三', age=18}, Person{name='李四', age=20}, Person{name='赵六', age=18}]
System.out.println(personList3);
基本概念
java.util.stream.Stream接口是对集合功能的增强,可以对集合元素进行复杂的查找、过滤、筛选 等操作。
Stream接口借助于Lambda 表达式极大的提高编程效率和程序可读性,同时它提供串行和并行两 种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势。
使用步骤
- 创建Stream,通过一个数据源来获取一个流。
- 转换Stream,每次转换返回一个新的Stream对象。
- 对Stream进行聚合操作并产生结果。
创建方式(创建流对象 )
-
方式一:通过调用集合的默认方法来获取流,如:default Stream stream()
代码实现
// 6.1使用方式一 使用集合中的默认方法 .stream 的创建流信息 返回值是Steam 类型的对象 Stream<Person> stream = personList2.stream();
-
方式二:通过数组工具类中的静态方法来获取流,如:static IntStream stream(int[] array)
-
方式三:通过Stream接口的静态方法来获取流,如:static Stream of(T… values)
-
方式四:通过Stream接口的静态方法来获取流,static Stream generate(Supplier<? extends T> s)
中间操作(返回值是Stream对象)
-
筛选与切片的常用方法如下:
方法声明 功能介绍 Stream filter(Predicate<? super T> predicate) 返回一个包含匹配元素的流 Stream distinct() 返回不包含重复元素的流 Stream limit(long maxSize) 返回不超过给定元素数量的流 Stream skip(long n) 返回丢弃前n个元素后的流 -
映射的常用方法如下:
方法声明 功能介绍 Stream map(Function<? super T,? extends R> mapper) 返回每个处理过元素组成的流 Stream flatMap(Function<? super T,? extends Stream<? extends R>> mapper) 返回每个替换过元素组成的流,并将所有流合成一个流流,并 将所有流合成一个流 -
排序的常用方法如下:
方法声明 功能介绍 Stream sorted() 返回经过自然排序后元素组成的流 Stream sorted(Comparator<? super T> comparator) 返回经过比较器排序后元素组成的流
终止操作(返回值不是Stream)
-
匹配与查找的常用方法如下:
方法声明 功能介绍 Optional findFirst() 返回该流的第一个元素 boolean allMatch(Predicate<? super T> predicate) 返回所有元素是否匹配 boolean noneMatch(Predicate<? super T> predicate) 返回没有元素是否匹配 Optional max(Comparator<? super T> comparator) 根据比较器返回最大元素 Optional min(Comparator<? super T> comparator) 根据比较器返回最小元素 long count() 返回元素的个数 void forEach(Consumer<? super T> action) 对流中每个元素执行操作 -
规约的常用方法如下:
方法声明 功能介绍 Optional reduce(BinaryOperator accumulator) 返回结合后的元素值 -
收集的常用方法如下:
方法声明 功能介绍 <R,A> R collect(Collector<? super T,A,R> collector) 使用收集器对元素进行处理
Steam流的方法使用
-
使用创建方式一 案例功能
// 6.使用Steam完成上述的功能操作 // 6.1使用方式一 使用集合中的默认方法 .stream 的创建流信息 返回值是Steam 类型的对象 Stream<Person> stream = personList2.stream(); // 6.2使用stream对象调用里面的过滤方法 filter stream.filter(new Predicate<Person>() { @Override public boolean test(Person person) { return person.getAge() >= 18; } // 使用 forEach 遍历集合 并打印结果 里面是函数式接口 Consumer }).forEach(new Consumer<Person>() { @Override public void accept(Person person) { System.out.println(person); } }); // 使用lambda 表达式 personList2.stream().filter(person -> person.getAge() >= 18).forEach((person) -> System.out.println(person)); // 使用方法引用的的方式 personList2.stream().filter(person -> person.getAge() >= 18).forEach(System.out :: println);
-
实现对集合元素通过流跳过1个元素后再取3个元素后打印
// 7.实现对集合元素通过流跳过1个元素后再取3个元素后打印 返回值是Stream 所以可以连续调用 personList2.stream().skip(1).limit(3).forEach(System.out::println);
-
实现集合中所有元素中年龄的获取
// 8.实现集合中所有元素中年龄的获取 personList2.stream().map(new Function<Person, Integer>() { @Override public Integer apply(Person person) { return person.getAge(); } }).forEach(System.out::println); // 对8的简化 personList2.stream().map(Person::getAge).forEach(System.out::println);
-
实现集合中的自然排序
// 9.实现集合中的自然排序并打印 切记 Person类 需要实现Comparable 接口 // 否则回报类型转换异常 ClassCastException personList2.stream().sorted().forEach(System.out :: println);
-
实现集合中的自定义排序
// 10.实现集合中的自定义排序 personList2.stream().sorted(((o1, o2) -> o1.getAge() -o2.getAge())).forEach(System.out::println);
-
判断年龄是否有大于25岁
// 11.判断年龄是否有大于25岁 boolean b = personList2.stream().noneMatch(person -> person.getAge() > 25); System.out.println(b);
-
判断集合中所有Person类的对象是否都大于18
// 12.判断集合中所有Person类的对象是否都大于18 boolean b1 = personList2.stream().allMatch(person -> person.getAge() > 18); System.out.println(b1); // false
-
找出集合中年龄最大
// 13.找出集合中年龄最大 引用方法 类调用非静态的方法 特例 是相互调用的 Optional<Person> max = personList2.stream().max(Person::compareTo); System.out.println(max);
-
集合中的Person 对象的个数
// 14.集合中的Person 对象的个数 long count = personList2.stream().count(); System.out.println(count);
-
实现将集合中所有元素的年龄映射出来并进行累加后打印
// 15.实现将集合中所有元素的年龄映射出来并进行累加后打印 // 当遇到函数式接口时 先使用匿名内部类 之后在使用lambda表达式 最后看看 能否使用引用方法进行简化 Optional<Integer> reduce = personList2.stream().map(Person::getAge).reduce(new BinaryOperator<Integer>() { @Override public Integer apply(Integer integer, Integer integer2) { return integer + integer2; } }); System.out.println(reduce); // 73 // 对15集合进行简化处理 Optional<Integer> reduce1 = personList2.stream().map(Person::getAge).reduce(Integer::sum); System.out.println(reduce1); // 73
-
实现将集合中所有的元素的姓名映射出来并收集到集合中
// 16.实现将集合中所有的元素的姓名映射出来并收集到集合中 List<String> collect = personList2.stream().map(Person::getName).collect(Collectors.toList()); System.out.println(collect); // [张三, 李四, 王五, 赵六]
Optional类
-
案例题目
判断字符串是否为空,若不为空则打印字符串的长度,否则打印0。
代码实现
// 1.判断字符串是否为空,若不为空则打印字符串的长度,否则打印0。 String str1 = "hello java"; // str1 = null; if (str1 != null){ System.out.println(str1.length()); // 10 }else { System.out.println("该字符串为空"); }
基本概念
- java.util.Optional类可以理解为一个简单的容器,其值可能是null或者不是null,代表一个值存在 或不存在。 该类的引入很好的解决空指针异常,不用显式进行空值检测
- 该类的引入很好的解决空指针异常,不用显式进行空值检测
常用的方法
方法声明 | 功能介绍 |
---|---|
static Optional ofNullable(T value) | 根据参数指定数值来得到Optional类型的对象 |
Optional map(Function<? super T,? extends U> mapper) | 根据参数指定规则的结果来得Optional类型的对象 |
T orElse(T other) 。 | 若该值存在就返回,否则返回other的数值 |
Optional类对空字符串的处理
// 2.java8中使用了Optional类实现了对空值的判断
Optional<String> str11 = Optional.ofNullable(str1);
// 2.1建立映射关系 使用字符串的长度与字符串建立关系
Optional<Integer> integer = str11.map(String::length);
// 3.2若字符串为空则打印0,否则打印字符串的数值
System.out.println(integer.orElse(0)); // 10