目录
1 jdk8的特性
- Lambda表达式(主要使用在Stream流中)
- 函数式接口
- 方法引用
- Stream流
- 日期时间类
2 Lambda表达式
2.1 Lambda的由来
public class Test02 {
public static void main(String[] args) {
//开启一个线程 该构造函数需要传递一个Runnable类型的接口参数
Thread thread = new Thread(new MyThread());
thread.start();//开启线程
//匿名内部类
Runnable runnable = new Runnable(){
@Override
public void run() {
System.out.println("通过匿名内部类完成线程任务");
}
};
Thread thread1 = new Thread(runnable);
thread1.start();//开启线程
}
}
class MyThread implements Runnable{
@Override
public void run() {
System.out.println("使用实现类来完成--------->线程任务");
}
}
分析:
- Thread类需要一个Runnable接口作为参数,其中的抽象方法run方法是用来指定线程任务内容的核心。
- 为了指定run方法体,不得不需要Runnable的实现类。
- 为了省去定义一个Runnable 的实现类,不得不使用匿名内部类。
- 必须覆盖重写抽象的run方法,所有的方法名称,方法参数,方法返回值不得不都重写一遍,而且不能出错。
- 而实际上,我们只在乎方法的参数列表及方法体中的代码。我们可以使用lambda表达式来完成上面的功能。
2.2 初体验lambda表达式
//lambda表达式
Runnable runnable1=()->{
System.out.println("使用lambda表达式完成线程任务");
};
Thread thread2 = new Thread(runnable1);
thread2.start();
2.3 lambda表达式的语法
Lambda表达式省去了面向对象的条条框框,Lambda表达式的标准格式由3个部分组成:
(参数列表)->{}
():参数列表。
->:连接符,连接的是参数以及方法体。
{}:方法体。
2.4 Lambda表达式使用的前提
1.方法的参数或局部变量类型必须为接口才能使用Lambda。
2.接口中有且仅有一个抽象方法(@FunctionalInterface)。
2.5 无参无返回值的Lambda表达式
public class Test02 {
public static void main(String[] args) {
//lambda表达式:该接口必须为函数式接口
UserService userService=()->{
System.out.println("lambda表示的show方法");
};
fun(userService);
}
public static void fun(UserService userService){
userService.show();
}
}
//函数式接口--->里面有且仅有一个抽象方法。--->只有这种接口才可以是使用lambda表达式
interface UserService{
public void show();
}
2.6 有参有返回值的Lambda表达式
public class Test03 {
public static void main(String[] args) {
List<Person> personList = new ArrayList<>();
personList.add(new Person("张三丰",65));
personList.add(new Person("任盈盈",23));
personList.add(new Person("令狐冲",32));
personList.add(new Person("林平之",12));
personList.add(new Person("风清扬",88));
//对集合中的元素按照年龄排序,从小到大
System.out.println(personList);
//Collections:集合类工具。--匿名内部类
/* Comparator<Person> comparator = new Comparator<Person>() {
@Override//如果是0表示相同 大于0表示o1>o2
public int compare(Person o1, Person o2) {
return o1.getAge()- o2.getAge();
}
};
Collections.sort(personList,comparator);
System.out.println(personList);*/
//lambda表达式
Comparator<Person> comparator =(o1,o2)->{
//对函数式接口中抽象方法的简写。
return o1.getAge() - o2.getAge();
};
Collections.sort(personList,comparator);
System.out.println(personList);
}
}
class Person{
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
2.7 Lambda表达式的省略写法
在lambda表达式的标准写法基础上,可以使用省略写法的规则为:
- 小括号内的参数类型可以省略。
- 如果小括号内有且仅有一个参数,则小括号可以省略。
- 如果大括号内有且仅有一个语句,可以同时省略大括号,return 关键字及语句分号。
代码演示如下:
public class Test04 {
public static void main(String[] args) {
//匿名内部类对接口中抽象方法的个数没有任何要求。
/*USB u = new USB() {
@Override
public String toUpper(String str) {
return str.toUpperCase();
}
};*/
//lambda表达式
USB u = str->str.toUpperCase();
fun(u);
}
public static void fun(USB usb) {
String s = usb.toUpper("hello world");
System.out.println(s);
}
}
interface USB{
public String toUpper(String str);
}
3. 内置函数式接口
3.1 内置函数式接口的由来
public class Test05 {
public static void main(String[] args) {
Operation o=arr->{
int sum = 0;
for (int s:arr){
sum+=s;
}
return sum;
};
fun(o);
}
public static void fun(Operation operation){
int[] arr={1,2,3,4};
int sum = operation.getSum(arr);
System.out.println("数组的和:"+sum);
}
}
@FunctionalInterface
interface Operation{
public int getSum(int[] arr);
}
分析:
使用Lambda表达式的前提是需要有函数式接口,而Lambda表达式使用时不关心接口名,抽象方法名。只关心抽象方法的参数列表和返回值类型。因此为了让我们使用Lambda表达式更加的方便,在JDK中提供了大量常用的函数式接口. 大多数无需自己再定义函数式接口,而可以直接使用jdk内置的函数式接口。分成四类如下图所示:
3.2 消费型函数式接口---Consumer
适合有参数,没有返回值的。
public class Test06 {
public static void main(String[] args) {
Consumer<Double> c = t->System.out.println("今天吃饭花费:"+t+"元");
fun(c,200.0);
}
public static void fun(Consumer<Double> consumer,Double money){
consumer.accept(money);
}
}
3.3 供给型函数式接口---Supplier
适合无参,有返回值的接口类。
public class Test06 {
public static void main(String[] args) {
//供给型函数式接口
Supplier<Integer> s = ()->new Random().nextInt(10);
fun2(s);
}
public static void fun2(Supplier<Integer> supplier){
Integer a = supplier.get();
System.out.println("这期彩票的中奖号码为:"+a);
}
}
3.4 函数型函数式接口---Function<T,R>
适合有参数,有返回值的。
T:表示参数的泛型。
R:表示返回值的泛型。
public class Test06 {
public static void main(String[] args) {
//函数型函数式接口
Function<int[],Integer> f = arr->{
int sum = 0;
for (int b: arr){
sum+=b;
}
return sum;
};
fun3(f);
}
public static void fun3(Function<int[],Integer> fun){
int[] arr = {1,2,3,4};
Integer sum = fun.apply(arr);
System.out.println("数组的和为:"+sum);
}
}
3.5 断言型函数式接口--Predicate
适合有参数,返回值类型为boolean的接口类。
T:表示参数类型。
boolean:表示返回值类型。
public class Test06 {
public static void main(String[] args) {
//断言型函数式接口
Predicate<Integer> p = age->age>18;
fun4(p,19);
}
public static void fun4(Predicate<Integer> predicate,Integer age){
boolean test = predicate.test(age);
System.out.println("是否成年:"+test);
}
}
4. 方法引用
方法引用是一种特殊的lambda表达式,它是对lambda表达式的一种简写形式。如果Lambda表达式方法体中只是调用一个特定的已经存在的方法,则可以使用方法引用
4.1 方法引用的类型
4.2 静态方法引用
lambda表达式:(args)->类名.静态方法(args)-------->类名::静态方法;
当lambda表达式中方法体,只有一条语句,而这条语句是类名.静态方法。而静态方法的参数和lambda的参数一致时可以用静态方法引用。
public class Test07 {
public static void main(String[] args) {
Consumer<int[]> c = arr->Test07.sum(arr);
fun(c);
//静态方法引用
Consumer<int[]> con = Test07::sum;
fun(con);
}
public static void fun(Consumer<int[]> consumer){
int[] arr = {1,2,3,4};
consumer.accept(arr);
}
//求和方法
public static void sum(int[] arr){
int sum=0;
for(int a:arr){
sum+=a;
}
System.out.println("数组的和:"+sum);
}
}
4.3 实例方法引用
lambda表达式:(args) -> inst.instMethod(args)-------->对象名::实例方法;
实例方法引用,顾名思义就是调用已经存在的实例的方法,与静态方法引用不同的是类要先实例化,静态方法引用类无需实例化,直接用类名去调用。
public class Test08 {
public static void main(String[] args) {
//创建一个类对象
Student student = new Student("杨玉环",18);
//通过内置的函数接口,返回对象的名称
// Supplier<String> supplier=()->student.getName();
Supplier<String> supplier=student::getName;
String s1 = supplier.get();
System.out.println(s1);
//观察:lambda表达式中有且仅有一条语句,方法调用语句。 ---实例方法引用特点:(args)->对象.普通方法(args);
}
}
class Student{
private String name;
private Integer age;
public Student() {
}
public Student(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
4.4 对象方法引用
lambda表达式: (inst,args)->inst.普通方法(args) -------->类名::普通方法;
若Lambda参数列表中的第一个参数是实例方法的参数调用者,而第二个参数是实例方法的参数时,可以使用对象方法引用。
public class Test09 {
public static void main(String[] args) {
//判断两个字符串是否相等
//BiFunction<String,String,Boolean> biFunction=(a,b)->a.equals(b);
//对象方法引用
BiFunction<String,String,Boolean> biFunction=String::equals;
show(biFunction);
}
public static void show(BiFunction<String,String,Boolean> biFunction){
Boolean apply = biFunction.apply("hello", "hello");
System.out.println("两字符串是否相等:"+apply);
}
}
4.5 构造方法引用
lambda表达式:(args) -> new 类名(args)------>类名::new;
public class Test10 {
public static void main(String[] args) {
//Supplier<Student> supplier =()->new Student();
//Student student = supplier.get();
//System.out.println(student);
//观察调用的构造函数
Supplier<Student> supplier=Student::new;
Student student = supplier.get();
System.out.println(student);
//BiFunction<String,Integer,Student> biFunction = (a,b)->new Student(a,b);
BiFunction<String,Integer,Student> biFunction = Student::new;
Student s = biFunction.apply("小龙女", 18);
System.out.println(s);
}
}
class Student{
private String name;
private Integer age;
public Student() {
}
public Student(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
5. Stream流
Java8的两个重大改变,一个是Lambda表达式,另一个就是Stream API表达式。Stream 是Java8中处理集合的关键抽象概念,它可以对集合进行非常复杂的查找、过滤、筛选等操作。
5.1 Stream初体验
一个ArrayList集合中存储有以下数据:张无忌,周芷若,赵敏,张飞,张三丰。
需求:1.拿到所有姓张的 2.拿到名字长度为3个字的 3.打印这些数据
public class Test11 {
public static void main(String[] args) {
//一个ArrayList集合中存储有以下数据:张无忌,周芷若,赵敏,张飞,张三丰
//需求:1.拿到所有姓张的 2.拿到名字长度为3个字的 3.打印这些数据
List<String> list = new ArrayList<>();
list.add("张无忌");
list.add("周芷若");
list.add("赵敏");
list.add("张飞");
list.add("张三丰");
list.stream().filter(t->t.startsWith("张")).filter(t->t.length()==3).forEach(System.out::println);
}
}
5.2 Stream流的原理
Stream流式思想类似于工厂车间的“生产流水线”,Stream流不是一种数据结构,不保存数据,而是对数据进行加工 处理。Stream可以看作是流水线上的一个工序。在流水线上,通过多个工序让一个原材料加工成一个商品。
5.3 步骤
- 获取Stream流对象。
- 中间操作----返回类型还是Stream流对象。
- 终止操作----不再是Stream流对象。
5.4 获取Stream流对象的方式
通过集合对象调用stream()。
通过Arrays获取stream流对象。
通过Stream流里面of方法。
public class Test12 {
public static void main(String[] args) {
List<String> list=new ArrayList<>();
list.add("张无忌");
list.add("周芷若");
list.add("赵敏");
//第一种通过集合调用
Stream<String> stream = list.stream();
stream.forEach(System.out::println);
//第二种通过Arrays工具类
String[] arr={};
Stream<String> stream1 = Arrays.stream(arr);
//第三种:Stream类
Stream<Integer> stream2 = Stream.of(1, 2, 3, 4, 5, 6);
//上面的流都是串行流。并行流如下:
Stream<String> stringStream = list.parallelStream();
stringStream.forEach(System.out::println);
}
}
5.5 Stream流中的api方法
举个例子:
假设有一个Person类和一个Person列表,现在有两个需求:
1)找到年龄大于18岁的人并输出;
2)找出所有中国人的数量。
@Data
class Person {
private String name;
private Integer age;
private String country;
private char sex;
public Person(String name, Integer age, String country, char sex) {
this.name = name;
this.age = age;
this.country = country;
this.sex = sex;
}
}
5.5.1 遍历/过滤/(foreach/filter)
public class Test13 {
public static void main(String[] args) {
List<Person> personList = new ArrayList<>();
personList.add(new Person("欧阳雪",18,"中国",'F'));
personList.add(new Person("Tom",24,"美国",'M'));
personList.add(new Person("Harley",22,"英国",'F'));
personList.add(new Person("仰天笑",20,"中国",'M'));
personList.add(new Person("杨康",22,"中国",'M'));
personList.add(new Person("小梅",20,"中国",'F'));
personList.add(new Person("何雪",21,"中国",'F'));
personList.add(new Person("杨过",22,"中国",'M'));
//1. 年龄大于18 filter:过滤掉不满足条件的元素. forEach:输出元素. ---如果没有终止函数,那么中间函数的代码不会被执行。
personList.stream().filter(t->t.getAge()>18).forEach(System.out::println);
}
}
5.5.2 聚合(count、max、min)
public class Test13 {
public static void main(String[] args) {
List<Person> personList = new ArrayList<>();
personList.add(new Person("欧阳雪",18,"中国",'F'));
personList.add(new Person("Tom",24,"美国",'M'));
personList.add(new Person("Harley",22,"英国",'F'));
personList.add(new Person("仰天笑",20,"中国",'M'));
personList.add(new Person("杨康",22,"中国",'M'));
personList.add(new Person("小梅",20,"中国",'F'));
personList.add(new Person("何雪",21,"中国",'F'));
personList.add(new Person("杨过",22,"中国",'M'));
//2. 找出中国人 并统计个数: count()
long count = personList.stream().filter(t -> t.getCountry().equals("中国")).count();
System.out.println("集合中有"+count+"个中国人");
//找出年龄最大的人
Person maxAge = personList.stream().max(((o1, o2) -> o1.getAge() - o2.getAge())).get();
System.out.println(maxAge);
//找出年龄最小的人
Person minAge = personList.stream().min(((o1, o2) -> o1.getAge() - o2.getAge())).get();
System.out.println(minAge);
}
}
5.5.3 map
可以把集合中的元素转化成另一种类型
获取集合中中国人的姓名和年龄
public class Test13 {
public static void main(String[] args) {
List<Person> personList = new ArrayList<>();
personList.add(new Person("欧阳雪",18,"中国",'F'));
personList.add(new Person("Tom",24,"美国",'M'));
personList.add(new Person("Harley",22,"英国",'F'));
personList.add(new Person("仰天笑",20,"中国",'M'));
personList.add(new Person("杨康",22,"中国",'M'));
personList.add(new Person("小梅",20,"中国",'F'));
personList.add(new Person("何雪",21,"中国",'F'));
personList.add(new Person("杨过",22,"中国",'M'));
//map
personList.stream().filter(t->t.getCountry().equals("中国")).map(item->new People(item.getName(), item.getAge())).forEach(System.out::println);
}
}
(1)整数数组中每个元素+5
(2数组中字符串变为大写
public class Test01 {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 17, 27, 7);
//整数数组每个元素+5
list.stream().map(item->item+5).forEach(System.out::println);
List<String> list2=Arrays.asList("hello","world","java","spring","springboot");
//字符串大写 map(item->item.toUpperCase) 可以简写为:map(String::toUpperCase)
list2.stream().map(String::toUpperCase).forEach(System.out::println);
}
}
5.5.4 collect(收集)
把处理过的集合收集成为新的集合。
需求:把Person-年龄大于20人--里面名称----收集为新的集合。
public class Test1 {
public static void main(String[] args) {
List<Person> personList = new ArrayList<>();
personList.add(new Person("欧阳雪",18,"中国",'F'));
personList.add(new Person("Tom",24,"美国",'M'));
personList.add(new Person("Harley",22,"英国",'F'));
personList.add(new Person("仰天笑",20,"中国",'M'));
personList.add(new Person("杨康",22,"中国",'M'));
personList.add(new Person("小梅",20,"中国",'F'));
personList.add(new Person("何雪",21,"中国",'F'));
personList.add(new Person("杨过",22,"中国",'M'));
//把Person-年龄大于20人--里面名称----收集为新的集合。
//map(Person::getName) == map(item -> item.getName())
List<String> collect = personList.stream().filter(t -> t.getAge() > 20).map(Person::getName).collect(Collectors.toList());
System.out.println(collect);
}
}
5.5.5 sorted(排序)
需求:把集合中的人按照年龄从小到大排序
public class Test1 {
public static void main(String[] args) {
List<Person> personList = new ArrayList<>();
personList.add(new Person("欧阳雪",18,"中国",'F'));
personList.add(new Person("Tom",24,"美国",'M'));
personList.add(new Person("Harley",22,"英国",'F'));
personList.add(new Person("仰天笑",20,"中国",'M'));
personList.add(new Person("杨康",22,"中国",'M'));
personList.add(new Person("小梅",20,"中国",'F'));
personList.add(new Person("何雪",21,"中国",'F'));
personList.add(new Person("杨过",22,"中国",'M'));
//把集合中的人按照年龄从大到小排序
List<Person> collect1 = personList.stream().sorted(((o1, o2) -> o1.getAge() - o2.getAge())).collect(Collectors.toList());
System.out.println(collect1);
}
}
5.5.6 reduce规约
归约,也称缩减,顾名思义,是把一个流缩减成一个值,能实现对集合求和、求乘积和求最值操作。
需求:求整型集合中元素的和、最大值、积
public class Test02 {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1,3,5,7);
//求和
Optional<Integer> sum = list.stream().reduce((t1, t2) -> t1 + t2);
//t1=1,t2=3---->t1=4,t2=5---->t1=9,t2=7
System.out.println("和为:"+sum.get());
//求最值
Optional<Integer> reduce = list.stream().reduce((t1, t2) -> t1 > t2?t1:t2);
System.out.println("最大值为:"+reduce.get());
//求积
Optional<Integer> reduce2 = list.stream().reduce((t1, t2) -> t1 * t2);
System.out.println("积为:"+reduce2.get());
}
}
5.5.7 findFirst(查找第一个元素)
需求:查找出集合中年龄大于6且小于二十的第一条数据
public class Test1 {
public static void main(String[] args) {
List<Person> personList = new ArrayList<>();
personList.add(new Person("欧阳雪",18,"中国",'F'));
personList.add(new Person("Tom",24,"美国",'M'));
personList.add(new Person("Harley",22,"英国",'F'));
personList.add(new Person("仰天笑",20,"中国",'M'));
personList.add(new Person("杨康",22,"中国",'M'));
personList.add(new Person("小梅",20,"中国",'F'));
personList.add(new Person("何雪",21,"中国",'F'));
personList.add(new Person("杨过",22,"中国",'M'));
//查找符合条件的第一个数据
Person first = personList.stream().filter(item -> item.getAge() >= 6 && item.getAge() <= 20).findFirst().get();
System.out.println(first);
}
}
5.5.8 提取、组合
Stream流也可以进行合并、去重、限制、跳过等操作。
public class Test03 {
public static void main(String[] args) {
String[] arr = {"a","b","c","d"};
String[] arr2 = {"c","d","e","f"};
Stream<String> stream1 = Stream.of(arr);
Stream<String> stream2 = Stream.of(arr2);
//concat:合并两个流 distinct: 去重
List<String> list = Stream.concat(stream1, stream2).distinct().collect(Collectors.toList());
System.out.println("合并后的:"+list);
//limit: 获取流中前n个数据
List<Integer> collect = Stream.iterate(1, t -> t + 1).limit(5).collect(Collectors.toList());
System.out.println("limit获取前五个元素:"+collect);
//skip: 跳过前n个数据
List<Integer> collect2 = Stream.iterate(1, t -> t + 1).skip(3).limit(5).collect(Collectors.toList());
System.out.println("skip跳过前三个元素:"+collect2);
}
}