【Java基础】Java8 新特性

时间:2022-09-22 11:42:31

Java8 新特性

Lambda 表达式

Lambda 是一个匿名函数,我们可以把 Lambda 表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。使用它可以写出更简洁、更灵活的代码。

Lambda 表达式的本质是作为函数式接口的实例。

->, 该操作符被称为 Lambda 操作符或箭头操作符。它将 Lambda 分为两个部分:

  • 左侧:指定了 Lambda 表达式需要的参数列表
  • 右侧:指定了 Lambda 体,是抽象方法的实现逻辑,也即 Lambda 表达式要执行的功能。
package parzulpan.com.java;

import org.junit.Test;

import java.util.Comparator;
import java.util.function.Consumer; /**
* @Author : parzulpan
* @Time : 2020-11-29
* @Desc : Lambda 表达式
*/ public class LambdaTest { // Lambda 表达式 - 使用举例
@Test
public void test1() {
// 匿名内部类
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("hello");
}
};
r1.run(); // 使用 Lambda 表达式
Runnable r2 = () -> System.out.println("hello Lambda");
r2.run(); // 使用匿名内部类作为参数传递
Comparator<Integer> com1 = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1, o2);
}
};
int compare = com1.compare(12, 21);
System.out.println(compare); // 使用 Lambda 表达式
Comparator<Integer> com2 = (o1, o2) ->Integer.compare(o1, o2);
int compare1 = com2.compare(12, 21);
System.out.println(compare1); // 使用 方法引用
Comparator<Integer> com3 = Integer::compare;
int compare2 = com3.compare(12, 21);
System.out.println(compare2); } // 语法格式
@Test
public void test2() {
// 格式一:无参,无返回值
Runnable r1 = () -> {
System.out.println("格式一:无参,无返回值");
};
r1.run(); // 格式二:Lambda 需要一个参数,但是没有返回值
Consumer<String> con = (String str) -> {
System.out.println(str);
};
con.accept("格式二:Lambda 需要一个参数,但是没有返回值"); // 格式三:数据类型可以省略,因为可由编译器推断得出,称为"类型推断"
Consumer<String> con1 = (str) -> {
System.out.println(str);
};
con1.accept("格式三:数据类型可以省略,因为可由编译器推断得出,称为\"类型推断\""); // 格式四:Lambda 若只需要一个参数时,参数的小括号可以省略
Consumer<String> con2 = str -> {
System.out.println(str);
};
con1.accept("格式四:Lambda 若只需要一个参数时,参数的小括号可以省略"); // 格式五:Lambda 需要两个或以上的参数,多条执行语句,并且可以有返回值
Comparator<Integer> com = (x, y) -> {
System.out.println("格式五:Lambda 需要两个或以上的参数,多条执行语句,并且可以有返回值");
return Integer.compare(x, y);
};
int compare = com.compare(10, 20);
System.out.println(compare); // 格式六:当 Lambda 体只有一条语句时,return 与大括号若有,都可以省略
Comparator<Integer> com1 = (x, y) -> Integer.compare(x, y);
int compare1 = com1.compare(20, 10);
System.out.println(compare1); } }

总结六种语法格式情况:

  • 左边:Lambda 形参列表的参数类型可以省略(类型推断);如果形参列表只有一个参数,其一对 () 也可以省略;
  • 右边:Lambda 体应该使用一对 {} 包裹;如果 Lambda 体只有一条执行语句(可能是 return 语句)那么可以省略这一对 {} 和 return 关键字;

函数式接口

在 Java8 中,Lambda 表达式就是一个函数式接口的实例。这就是

Lambda 表达式和函数式接口的关系。也就是说,只要一个对象是函数式接口

的实例,那么该对象就可以用 Lambda 表达式来表示

所以以前用匿名实现类表示的现在都可以用 Lambda 表达式来写。

作为参数传递 Lambda 表达式:为了将 Lambda 表达式作为参数传递,接收 Lambda 表达式的参数类型必须是与该 Lambda 表达式兼容的函数式接口的类型。

Java 内置四大核心函数式接口:

  • Consumer<T> 消费型接口。对类型为 T 的对象应用操作,包含方法:

    void accept(T t)
  • Supplier<T> 供给型接口。返回类型为T的对象,包含方法:T get()
  • Function<T, R> 函数型接口。对类型为 T 的对象应用操作,并返回结果。结果是 R 类型的对象。包含方法:R apply(T t)
  • Predicate<T> 断定型接口。确定类型为 T 的对象是否满足某约束,并返回 boolean 值。包含方法:boolean test(T t)

方法引用&构造器引用

对于方法引用,

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

方法引用可以看做是 Lambda 表达式深层次的表达。换句话说,方法引用就

是 Lambda 表达式,也就是函数式接口的一个实例,通过方法的名字来指向

一个方法,可以认为是 Lambda 表达式的一个
语法糖

要求:实现接口的抽象方法的参数列表和返回值类型,必须与方法引用的

方法的参数列表和返回值类型保持一致。

格式:使用操作符 :: 将类(或对象)与方法名分隔开来。

主要使用情况:

  • 情况 1,对象::实例方法名
  • 情况 2,类::静态方法名
  • 情况 3,类::实例方法名

针对于情况 1 和 2,要求接口中的抽象方法的形参列表和返回值类型与方法引用的形参列表和返回值类型相同。

针对于情况 3,要求当函数式接口方法的第一个参数是需要引用方法的调用者,并且第二个参数是需要引用方法的参数(或无参数)时。ClassName::methodName

BiPredicate<String, String> bp = (x, y) -> x.equals(y);

// 等同于
BiPredicate<String, String> bp1 = String::equals;
boolean flag = bp1.test("hello", "hi);

对于构造器引用,

与函数式接口相结合,自动与函数式接口中方法兼容。可以把构造器引用赋值给定义的方法,要求:构造器参数列表要与接口中抽象方法的参数列表一致!且方法的返回值即为构造器对应类的对象。格式ClassName::new

Function<Integer, MyClass> fun = (n) -> new MyClass(n);

// 等同于
Function<Integer, MyClass> fun1 = MyClass::new;

对于数组引用,格式type[] :: new

Function<Integer, Integer[]> fun = (n) -> new Integer[n];

// 等同于
Function<Integer, Integer[]> fun1 = Integer[]::new;

StreamAPI

Stream API java.util.stream)把真正的函数式编程风格引入到 Java 中。使用 Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。

Stream 和 Collection 集合的区别

  • Collection 是一种静态的内存数据结构,而 Stream 是有关计算的。
  • 前者是主要面向内存,存储在内存中,后者主要是面向 CPU,通过 CPU 实现计算。

Stream 通俗的讲,是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。

注意

  • Stream 自己不会存储元素。
  • Stream 不会改变源对象。相反,他们会返回一个持有结果的新 Stream。
  • Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。

Stream 的操作三个步骤

  • 创建 Stream,一个数据源(如:集合、数组),获取一个流;
  • 中间操作,一个中间操作链,对数据源的数据进行处理;
  • 终止操作,一旦执行终止操作,就执行中间操作链,并产生结果。之后,不会再被使用;
package parzulpan.com.java;

import org.junit.Test;

import java.util.Arrays;
import java.util.List;
import java.util.stream.IntStream;
import java.util.stream.Stream; /**
* @Author : parzulpan
* @Time : 2020-11-29
* @Desc : 创建 Stream
*/ public class StreamAPITest { // 创建 Stream方式一:通过集合
@Test
public void test1() {
List<Employee> employees = EmployeeData.getEmployees();
Stream<Employee> stream = employees.stream();
Stream<Employee> employeeStream = employees.parallelStream();
} // 创建 Stream方式二:通过数组
@Test
public void test2() {
int[] ints = {1, 3, 43, 5, 463};
IntStream stream = Arrays.stream(ints); Employee ppa = new Employee(1001, "PPA");
Employee ooa = new Employee(1002, "OOA");
Employee[] employees = {ppa, ooa};
Stream<Employee> stream1 = Arrays.stream(employees); } // 创建 Stream方式三:通过Stream的of()
@Test
public void test3() {
Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5, 6); } // 创建 Stream方式四:创建无限流
@Test
public void test4() {
// 1. 迭代
// 遍历前 10 个偶数
Stream<Integer> iterate = Stream.iterate(0, x -> x + 2);
iterate.limit(10).forEach(System.out::println); // 2. 生成
Stream<Double> generate = Stream.generate(Math::random);
generate.limit(10).forEach(System.out::println);
}
}
package parzulpan.com.java;

import org.junit.Test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream; /**
* @Author : parzulpan
* @Time : 2020-11-29
* @Desc : Stream 的中间操作
*/ public class StreamAPITest1 { // 筛选和切片
@Test
public void test1() {
List<Employee> employees = EmployeeData.getEmployees(); // filter(Predicate p) 接收 Lambda,从流中排除某些元素
Stream<Employee> stream = employees.stream();
stream.filter(e -> e.getSalary() > 7000).forEach(System.out::println); // 查询员工表中薪金大于 7000 的员工信息 System.out.println(); // limit(long maxSize) 截断流,使其元素不超过给定数量
employees.stream().limit(3).forEach(System.out::println); System.out.println(); // skip(long n) 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补
employees.stream().skip(3).forEach(System.out::println); System.out.println(); // distinct() 筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素
employees.add(new Employee(1010, "刘强东", 40, 80000));
employees.add(new Employee(1010, "刘强东", 40, 80000));
employees.add(new Employee(1010, "刘强东", 40, 80000));
employees.add(new Employee(1010, "刘强东", 40, 80000));
employees.add(new Employee(1010, "刘强东", 40, 80000));
employees.add(new Employee(1010, "刘强东", 40, 80000));
employees.add(new Employee(1010, "刘强东", 40, 80000));
employees.stream().distinct().forEach(System.out::println);
} // 映射
@Test
public void test2() {
// map(Function f) 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
List<String> strings = Arrays.asList("AA", "BB", "fE", "fkk", "Ae");
strings.stream().map(String::toUpperCase).forEach(System.out::println); // 获取员工姓名长度大于 3 的员工姓名
List<Employee> employees = EmployeeData.getEmployees();
Stream<String> stringStream = employees.stream().map(Employee::getName);
stringStream.filter(name -> name.length() > 3).forEach(System.out::println); // flatMap(Function f) 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
// 注意这两个的区别,有点类似 add() 和 addAll()
Stream<Stream<Character>> streamStream = strings.stream().map(StreamAPITest1::fromStringToStream);
streamStream.forEach(s -> {
s.forEach(System.out::println);
});
Stream<Character> characterStream = strings.stream().flatMap(StreamAPITest1::fromStringToStream);
characterStream.forEach(System.out::println);
} // 将字符串中的多个字符构成的集合转换为对应的 Stream 的实例
public static Stream<Character> fromStringToStream(String str) {
ArrayList<Character> arrayList = new ArrayList<>();
for (Character c : str.toCharArray()) {
arrayList.add(c);
} return arrayList.stream();
} // 排序
@Test
public void test3() {
// sorted() 产生一个新流,其中按自然顺序排序
List<Integer> integers = Arrays.asList(11, 24, 453, 463, 1, 325, -134);
integers.stream().sorted().forEach(System.out::println); // sorted(Comparator com) 产生一个新流,其中按比较器顺序排序
EmployeeData.getEmployees().stream().sorted(
(o1, o2) -> Integer.compare(o1.getAge(), o2.getAge())
).forEach(System.out::println);
}
}
package parzulpan.com.java;

import org.junit.Test;

import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors; /**
* @Author : parzulpan
* @Time : 2020-11-29
* @Desc : Stream 的终止操作
*/ public class StreamAPITest2 { // 匹配与查找
@Test
public void test1() {
List<Employee> employees = EmployeeData.getEmployees(); // allMatch(Predicate p) 检查是否匹配所有元素
System.out.println(employees.stream().allMatch(e -> e.getAge() > 18)); // 是否所有的员工的年龄都大于 18 // anyMatch(Predicate p) 检查是否至少匹配一个元素
System.out.println(employees.stream().anyMatch(e -> e.getSalary() > 8000)); // 是否存在员工的工资大于 8000 // noneMatch(Predicate p) 检查是否没有匹配所有元素
System.out.println(employees.stream().noneMatch(e -> e.getName().startsWith("马"))); // 是否存在员工姓 "马" // findFirst() 返回第一个元素
Optional<Employee> first = employees.stream().findFirst();
System.out.println(first); // findAny() 返回当前流中的任意元素
Optional<Employee> any = employees.parallelStream().findAny();
System.out.println(any); // count() 返回流中元素总数
long count = employees.stream().filter(e -> e.getSalary() > 8000).count(); // 员工的工资大于 8000 的人数
System.out.println(count); //max(Comparator c) 返回流中最大值
Optional<Double> max = employees.stream().map(Employee::getSalary).max(Double::compare); // 员工的最高工资
System.out.println(max); //min(Comparator c) 返回流中最小值 Optional<Employee> min = employees.stream().min(
(o1, o2) -> Double.compare(o1.getSalary(), o2.getSalary())
); // 最低工资的员工
System.out.println(min); //forEach(Consumer c) 内部迭代(使用 Collection 接口需要用户去做迭代,称为外部迭代。相反,Stream API 使用内部迭代
employees.stream().forEach(System.out::println); // 内部迭代
employees.forEach(System.out::println); // 外部迭代
} // 归约
@Test
public void test2() { // reduce(T identity, BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回 T
List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Integer sum = integers.stream().reduce(10, Integer::sum); // 计算 1-10 的和 + 10 的结果
System.out.println(sum); //reduce(BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回 Optional<T>
List<Employee> employees = EmployeeData.getEmployees();
Optional<Double> reduce = employees.stream().map(Employee::getSalary).reduce(Double::sum); // 计算员工工资总和
System.out.println(reduce); // map 和 reduce 的连接通常称为 map-reduce 模式,因 Google 用它来进行网络搜索而出名。
} // 收集
@Test
public void test3() {
// collect(Collector c) 将流转换为其他形式。接收一个 Collector 接口的实现,用于给 Stream 中元素做汇总的方法
// Collector 接口中方法的实现决定了如何对流执行收集的操作(如收集到 List、Set、Map)
// Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例
// 查找员工的工资大于 8000 的员工,结果返回为一个 List 或 Set
List<Employee> employees = EmployeeData.getEmployees();
List<Employee> collect = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toList());
System.out.println(collect);
collect.forEach(System.out::println); Set<Employee> collect1 = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toSet());
System.out.println(collect1);
collect1.forEach(System.out::println);
}
}

Optional 类

空指针异常是导致 Java 应用程序失败的最常见原因。Google 著名的 Guava 项目引入了 Optional 类,Guava 通过使用检查空值的方式来防止代码污染,它鼓励程序员写更干净的代码。

Optional<T> 类是一个容器类,它可以保存类型 T 的值,代表这个值存在。或者仅仅保存 null,表示这个值不存在。原来用 null 表示一个值不存在,现在 Optional 可以更好的表达这个概念。并且可以避免空指针异常。

如果值存在则 isPresent() 方法会返回 true,调用 get() 方法会返回该对象。

package parzulpan.com.java1;

/**
* @Author : parzulpan
* @Time : 2020-11-29
* @Desc :
*/ public class Boy {
private Girl girl; public Boy() {
} public Boy(Girl girl) {
this.girl = girl;
} public Girl getGirl() {
return girl;
} public void setGirl(Girl girl) {
this.girl = girl;
} @Override
public String toString() {
return "Boy{" +
"girl=" + girl +
'}';
}
}
package parzulpan.com.java1;

/**
* @Author : parzulpan
* @Time : 2020-11-29
* @Desc :
*/ public class Girl {
private String name; public Girl() {
} public Girl(String name) {
this.name = name;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} @Override
public String toString() {
return "Girl{" +
"name='" + name + '\'' +
'}';
}
}
package parzulpan.com.java1;

import org.junit.Test;

import java.util.Optional;

/**
* @Author : parzulpan
* @Time : 2020-11-29
* @Desc : Optional 类
*/ public class OptionalTest { // 创建Optional类对象的方法
@Test
public void test1() {
// Optional.of(T t) 创建一个 Optional 实例,t必须非空;
Girl girl = new Girl();
Optional<Girl> girl1 = Optional.of(girl);
System.out.println(girl1); // Optional.empty() 创建一个空的 Optional 实例
Optional<Object> empty = Optional.empty();
System.out.println(empty); // Optional.ofNullable(T t) t 可以为 null
girl = null;
Optional<Girl> girl2 = Optional.ofNullable(girl);
System.out.println(girl2); // Optional 的应用
Boy boy = null;
String girlName1 = getGirlName(boy);
System.out.println(girlName1); // AAA boy = new Boy();
String girlName2 = getGirlName(boy);
System.out.println(girlName2); // BBB boy = new Boy(new Girl("CCC"));
String girlName3 = getGirlName(boy);
System.out.println(girlName3); // CCC
} // 使用 Optional 优化
public String getGirlName(Boy boy) {
// 避免 Boy 和 Girl 为 null
Optional<Boy> boyOptional = Optional.ofNullable(boy);
// T orElse(T other) 如果有值则将其返回,否则返回指定的other对象
Boy boy1 = boyOptional.orElse(new Boy(new Girl("AAA"))); Girl girl = boy1.getGirl(); Optional<Girl> girlOptional = Optional.ofNullable(girl);
Girl girl1 = girlOptional.orElse(new Girl("BBB")); return girl1.getName();
}
}

练习和总结

【Java基础】Java8 新特性的更多相关文章

  1. Java基础-Java8新特性

    一.Lambda表达式 在了解 Lambda 之前,首先回顾以下Java的方法. Java的方法分为实例方法,例如:Integer的equals()方法: public final class Int ...

  2. java基础---java8 新特性

    1. 函数式接口 函数式接口主要指只包含一个抽象方法的接口,如:java.lang.Runnable(java1.0).java.util.Comparator接口(java1.4)等. Java8提 ...

  3. 【Java】Java8新特性

    文章目录 Java8新特性 Lambda表达式的使用 语法格式一:无参,无返回值 语法格式二:Lambda 需要一个参数,但是没有返回值. 语法格式三:数据类型可以省略,因为可由编译器推断得出,称为& ...

  4. 漫漫人生路-学点Jakarta基础-Java8新特性 Stream&sol;Lambda

    背景 Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利.高效的聚合操作(aggregate operation),或者大批量数据操作 (bulk dat ...

  5. Java基础20:Java8新特性终极指南

    更多内容请关注微信公众号[Java技术江湖] 这是一位阿里 Java 工程师的技术小站,作者黄小斜,专注 Java 相关技术:SSM.SpringBoot.MySQL.分布式.中间件.集群.Linux ...

  6. 夯实Java基础系列21:Java8新特性终极指南

    本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial 喜欢的话麻烦点下 ...

  7. Java基础之java8新特性(1)Lambda

    一.接口的默认方法.static方法.default方法. 1.接口的默认方法 在Java8之前,Java中接口里面的默认方法都是public abstract 修饰的抽象方法,抽象方法并没有方法实体 ...

  8. 【Java学习笔记之二十八】深入了解Java8新特性

    前言: Java8 已经发布很久了,很多报道表明java8 是一次重大的版本升级.在Java Code Geeks上已经有很多介绍Java 8新特性的文章,例如Playing with Java 8 ...

  9. Java学习之&equals;&equals;&gt&semi;Java8 新特性详解

    一.简介 Java 8 已经发布很久了,很多报道表明Java 8 是一次重大的版本升级.Java 8是 Java 自 Java 5(发布于2004年)之后的最重要的版本.这个版本包含语言.编译器.库. ...

随机推荐

  1. memcache(一)概述

    概述 memcache是一种支持分布式的缓存系统,基于网络连接(当然它也可以使用localhost)方式完成服务,本身它是一个独立于应用的程序或守护进程(Daemon方式). 本地缓存 memcach ...

  2. C&num; int&period;Parse&lpar;&rpar;与int&period;TryParse&lpar;&rpar;

    int i = -1;bool b = int.TryParse(null, out i);执行完毕后,b等于false,i等于0,而不是等于-1,切记. int i = -1;bool b = in ...

  3. java获取系统指定时间年月日

    java获取系统指定时间年月日 private String setDateTime(String falg) { Calendar c = Calendar.getInstance(); c.set ...

  4. 【转】FIO使用指南

    原文地址:http://blog.csdn.net/yuesichiu/article/details/8722417 Fio压测工具和io队列深度理解和误区 这个文档是对fio-2.0.9 HOWT ...

  5. shell的数值计算,小数计算

    shell脚本中,可以进行数值计算, 如加减乘除,通过expr.let.(())等完成,文章介绍:http://blog.csdn.net/longshenlmj/article/details/14 ...

  6. nvm的使用

    nvm能下载并按照指定 的版本,还能切换已安装好的版本,相当的好用

  7. 5&period;6版本GTID复制异常处理一例(转)

    http://imysql.com/2014/07/31/mysql-faq-exception-replication-with-gtid.shtml 昨天处理了一个MySQL 5.6版本下开启GT ...

  8. ubuntu下安装 java环境

    步骤1:下载jdk 我选择的jdk版本文件: jdk-8u201-linux-x64.tar 官网下载链接 步骤2:创建单独的目录 sudo mkdir /usr/local/java 步骤3:将下载 ...

  9. IIS服务器的安全保护措施

    转载自:https://www.williamlong.info/archives/118.html 部分内容做了修改. 通过标注Web服务器安全级别以及可用性的安全策略,网络管理员将能够从容地在不同 ...

  10. C&plus;&plus; 文件和流

    到目前为止,我们已经使用了 iostream 标准库,它提供了 cin 和 cout 方法分别用于从标准输入读取流和向标准输出写入流. 本教程介绍如何从文件读取流和向文件写入流.这就需要用到 C++ ...