1、lambda表达式
lambda表达式:特殊的匿名内部类,语法更简洁;
lambda表达式允许把函数作为一个方法的函数(函数作为方法的参数传递),将代码像数据一样传递;
语法:
<函数式接口><变量名>=(参数1,参数2)->{};
注:函数式接口:接口中只有一个抽象方法
(参数1,参数2):抽象方法的参数
->:分隔符
{}:表示抽象方法的实现
我们创建线程任务时,之前使用的有两种方式,
①自定义任务接口类
public class Test01 {
public static void main(String[] args) {
//使用自定义任务接口类
Task task = new Task();
Thread thread = new Thread(task);
thread.start();
}
}
class Task implements Runnable{
@Override
public void run() {
System.out.println("只是自定义任务接口类");
}
}
②匿名内部类
public class Test02 {
public static void main(String[] args) {
//使用匿名内部类
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("这是匿名内部类");
}
});
thread.start();
}
}
从上面两种方式可以发现我们真正需要的只是run方法中的方法体
从JDK8之后加入了lambda表达式,我们创建线程任务的方式也有了第三种方式;
③使用lambda表达式
public class Test03 {
public static void main(String[] args) {
//使用lambda表达式
Runnable task = ()->{
System.out.println("这是lambda表达式中的方法体");
};
Thread thread = new Thread(task);
thread.start();
}
}
(1)lambda表达式无参无返回值的使用
/*无参无返回值的lambda表达式的使用*/
public class Test01 {
public static void main(String[] args) {
Singable singable = new Singable() {
@Override
public void singing() {
System.out.println("匿名内部类在唱歌");
}
};
//使用匿名内部类
fun(singable);
//使用lambda表达式,方法作为方法的参数进行传递
fun(()->{
System.out.println("lambda表达式在唱歌");
});
}
public static void fun(Singable singable){
singable.singing();
}
}
@FunctionalInterface
interface Singable{
void singing();
}
(1)lambda表达式有参有返回值的使用
/*有参有返回值*/
public class Test02 {
public static void main(String[] args) {
List<People> peopleList = new ArrayList<>();
peopleList.add(new People("高景霞",21,167));
peopleList.add(new People("张嘉伟",23,180));
peopleList.add(new People("杨坦蓉",25,178));
peopleList.add(new People("何现成",22,185));
//返回值大于0的话按照年龄从大到小排序,
Comparator<People> comparator = (People o1,People o2)->{
return o2.getAge()-o1.getAge();
};
Collections.sort(peopleList,(o1,o2)->
o2.getAge()-o1.getAge()
);
for (People p : peopleList) {
System.out.println(p);
}
}
}
class People{
private String name;
private Integer age;
private Integer tall;
@Override
public String toString() {
return "People{" +
"name='" + name + '\'' +
", age=" + age +
", tall=" + tall +
'}';
}
public People() {
}
public People(String name, Integer age, Integer tall) {
this.name = name;
this.age = age;
this.tall = tall;
}
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;
}
public Integer getTall() {
return tall;
}
public void setTall(Integer tall) {
this.tall = tall;
}
}
(3)lambda表达式的特性
- 形参列表的数据类型会自动推断
- 如果形参列表为空,只需保留()
- 如果形参只有一个,()可以省略,只需要参数的名称即可
- 如果执行语句只有一句,且无返回值,{}可以很省略,若有返回值,则若想省去{}。则必须同时省略return,且执行语句也保证只有一句
- lambda不会生成一个单独的内部类文件
2、函数式接口
如果一个接口只有一个抽象方法,则该接口称之为函数式接口,函数式接口可以使用lambda表达式,lambda表达式会被匹配到这个抽象方法上;
@FunctionalIntegerface注解检测接口是否符合函数式接口
(1)消费型接口Consumer<T>
例子:出去玩花费多少钱?
public class Test01 {
public static void main(String[] args) {
/*使用JDK定义的消费型函数式接口有参无返回值*/
fun(money->{
System.out.println("吃喝玩乐用了"+money);
},200);
}
/*例子:出去玩花费多少钱*/
public static void fun(Consumer<Integer> consumer,Integer money){
consumer.accept(money);
}
}
(2)供给型接口Supplier<T>
例子:点名册
public class Test02 {
public static void main(String[] args) {
/*使用JDK定义的供给型函数式接口无参有返回值*/
fun(()->new Random().nextInt(66)+1);
}
/*点名册*/
public static void fun(Supplier<Integer> supplier){
System.out.println("点到名的人学号为"+supplier.get());
}
}
(3)函数型接口Function<T,R>
例子:传入字符串,返回字符串的长度
public class Test03 {
public static void main(String[] args) {
/*使用JDK定义的函数型函数式接口有参有返回值*/
fun(param->param.length(),"Welcome to China!");
}
/*传入字符串,返回字符串的长度*/
public static void fun(Function<String,Integer> function,String msg){
System.out.println("字符串的长度为"+function.apply(msg));
}
}
(4)断言型接口Predicate<T>
例子:判断名字是否过长
public class Test04 {
public static void main(String[] args) {
/*使用JDK定义的断言型函数式接口有参返回值为布尔类型*/
fun(param-> param.length()>3,"诸葛孔明");
}
/*判断名字是否过长*/
public static void fun(Predicate<String> predicate,String name){
System.out.println("这个名字是否过长:"+predicate.test(name));
}
}
3、lambda表达式的冗余
public class TestRongYu {
public static void main(String[] args) {
fun(integers -> {
int sum=0;
for (int i:integers) {
sum+=i;
}
System.out.println(sum);
});
}
public static void fun(Consumer<Integer[]> consumer){
Integer[] integers = new Integer[]{1,2,3,4,5};
consumer.accept(integers);
}
public static void sum(Integer[] integers){
int sum=0;
for (Integer i:integers) {
sum+=i;
}
System.out.println("结果为:"+sum);
}
}
我们观察上述代码,我们会发现一个问题,我们要实现数组求和的功能,我们使用lambda表达式和jdk自带的函数式接口书写了这一段业务逻辑代码,但是我们也定义了一个求和的方法,只不过没有用上,这就是lambda表达式的代码冗余现象,因此引入了方法引用。
4、方法引用
方法引用时lambda表达式的一种简写方式。如果lambda表达式方法体中只是调用一个特定的已经存在的方法,则可以使用方法引用。
常见形式:
类型 | 语法 | 对应的LAMBDA表达式 |
---|---|---|
静态方法引用 | 类名::staticMethod | (args) -> 类名.staticMethod(args) |
实例方法引用 | inst::instMethod | (args) -> inst.instMethod(args) |
对象方法引用 | 类名::instMethod | (inst,args) -> inst.instMethod(args) |
构建方法引用 | 类名::new | (args) -> new 类名(args) |
(1)静态方法的引用
类名::staticMethod (args) -> 类名.staticMethod(args)
public class TestStatic {
public static void main(String[] args) {
People[] people = {new People("饺子皮",21),new People("饺子馅",22),new People("擀面杖",23)};
//引用类中的静态方法
Arrays.sort(people,People::compareByAge);
for (People p:people) {
System.out.println(p);
}
}
}
public class People {
private String name;
private Integer age;
public static int compareByAge(People a,People b){
return b.getAge()-a.getAge();
}
@Override
public String toString() {
return "People{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public People() {
}
public People(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;
}
}
(2)实例方法的引用
inst::instMethod (args) -> inst.instMethod(args)
public class TestInst {
public static void main(String[] args) {
People people = new People("饺子皮",21);
//实例方法引用
//Supplier<String> supplier = ()->people.getName();
Supplier<String> supplier = people::getName;
String s = supplier.get();
System.out.println("名字是"+s);
}
}
(3)对象方法的引用
类名::instMethod (inst,args) -> inst.instMethod(args)
public class TestObj {
public static void main(String[] args) {
//对象方法引用 (inst) -> inst.instMethod()
/*Function<String,Integer> function = (str)->{
return str.length();
};*/
Function<String,Integer> function = String::length;
Integer hello = function.apply("hello");
System.out.println(hello);
//(inst,args) -> inst.instMethod(args)
//比较两个字符串的内容
/*BiFunction<String,String,Boolean> biFunction = (str,str2)->{
return str.equals(str2);
};*/
BiFunction<String,String,Boolean> biFunction = String::equals;
Boolean apply = biFunction.apply("hello", "world");
System.out.println(apply);
}
}
(4)构造方法引用
类名::new (args) -> new 类名(args)
public class TestNew {
public static void main(String[] args) {
//构造方法引用
//BiFunction<String,Integer,People> biFunction = (name,age)->new People(name,age);
BiFunction<String,Integer,People> biFunction = People::new;
System.out.println(biFunction.apply("饺子皮",21));
}
}
5、Stream流
java8的两个重大改变,一个是lambda表达式,一个是Stream流,Stream流是Java8中处理集合的关键抽象概念,它可以对集合进行非常复杂的查找、过滤、筛选等操作。
(1)为什么使用Stream流?
当我们需要对集合中的元素进行操作的时候,除了必需的添加、删除、获取外,最典型的就是集合遍历。集合操作数据的弊端:每当我们需要对集合中的元素进行操作的时候,总是需要进行循环、循环、再循环。循环 是做事情的方式,而不是目的。每个需求都要循环一次,还要搞一个新集合来装数据,如果希望再次遍历,只能再使 用另一个循环从头开始。
Stream为我们带来了更加优雅的写法:
public class TestStream {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "饺子皮", "饺子馅", "擀面杖", "猪肉大葱", "猪肉芹菜","韭菜鸡蛋","饺子大师");
//查询以饺子开头的,长度小于等于3的并且输出
list.stream()
.filter(item->item.startsWith("饺子"))
.filter(item->item.length()<=3)
.forEach(item-> System.out.println(item));
}
}
(2)Stream流的原理
整个流操作就是一条流水线,将元素放在流水线上一个个地进行处理。所以可以理解为叫做流
。
Stream不存在数据,只对数据进行加工处理。
(3)获取流对象
- 通过Collection对象的stream()(串行流)或parallelStream()方法(并行流)
- 通过Arrays类的stream()方法
- 通过Stream接口的of()、iterate()、generate()方法
- 通过IntStream、LongStream、DoubleStream接口中的rangeClosed方法
public class TestGetStream {
public static void main(String[] args) {
//通过Collection对象的stream()(串行流)或parallelStream()方法(并行流)
List<String> list = new ArrayList<>();
Collections.addAll(list,"饺子皮","饺子馅","猪肉大葱","猪肉芹菜");
Stream<String> stream = list.stream();
Stream<String> stringStream = list.parallelStream();
//通过Arrays类的stream()方法
int[] intAry = new int[]{1,23,4,5,6};
IntStream stream1 = Arrays.stream(intAry);
//通过Stream接口的of()、iterate()、generate()方法
Stream<String> stream2 = Stream.of("饺子皮", "饺子馅", "擀面杖");
//通过IntStream、LongStream、DoubleStream接口中的rangeClosed方法
IntStream stream3 = IntStream.rangeClosed(1, 10);
}
}
(4)Stream流中常用的api
中间操作api:一个操作的中间链,对数据源的数据进行操作。而这种操作的返回类型还是一个Stream对象。
终止操作api:一个终止操作,执行中间操作链,并产生结果,返回类型不再是Stream流对象。
stream
不存储数据,而是按照特定的规则对数据进行计算,一般会输出结果;
stream
不会改变数据源,通常情况下会产生一个新的集合;
stream
具有延迟执行特性,只有调用终端操作时,中间操作才会执行。
@Data
@AllArgsConstructor
@NoArgsConstructor
class Person {
private String name;
private Integer age;
private String country;
private char sex;
}
1.遍历forEach()
public class TestApi {
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.forEach
//personList.stream().forEach(System.out::println);
}
}
2.发现Find()
public class TestApi {
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.find
Optional<Person> first = personList.stream().filter(item -> item.getAge() > 20).findFirst();
System.out.println(first.get());
//findAny()在并行流中使用
Optional<Person> any = personList.parallelStream().filter(item -> item.getAge() > 20).findAny();
System.out.println(any.get());
}
}
3.匹配Match
public class TestApi {
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'));
//3.match
boolean allMatch = personList.stream().allMatch(item -> item.getAge() > 15);
System.out.println(allMatch);
boolean anyMatch = personList.stream().anyMatch(item -> item.getAge() > 15);
System.out.println(anyMatch);
boolean noneMatch = personList.stream().noneMatch(item -> item.getAge() > 15);
System.out.println(noneMatch);
}
}
4.filter() count()
public class TestApi {
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'));
//4.统计性别为女的人数
long count = personList.stream().filter(item -> item.getSex() == 'F').count();
System.out.println(count);
}
}
5.sort()
public class TestApi {
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'));
//5.根据年龄排序
personList.stream().sorted((p1,p2)->p1.getAge()- p2.getAge()).forEach(System.out::println);
}
}
6.map()
public class TestApi {
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'));
//6.获取对象中的姓名,年龄,存入map
personList.stream().map(item->{
Map<String,String> map = new HashMap<>();
map.put("name",item.getName());
map.put("age",item.getAge().toString());
return map;
}).forEach(System.out::println);
}
}
7.max() min()
public class TestApi {
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'));
//7.查找年龄最大的人
Optional<Person> max = personList.stream().max((p1, p2) -> p1.getAge() - p2.getAge());
System.out.println(max.get());
//查询年里最小的人
Optional<Person> min = personList.stream().min((p1, p2) -> p1.getAge() - p2.getAge());
System.out.println(min.get());
}
}
8.规约reduce
public class TestApi {
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'));
//8.规约reduce
//求所有年龄的和
Optional<Integer> reduce = personList.stream().map(item -> item.getAge()).reduce((age1, age2) -> age1 + age2);
System.out.println(reduce.get());
//求名字最长的员工
Optional<Person> reduce1 = personList.stream().reduce((p1, p2) -> {
if (p1.getName().length() > p2.getName().length()) {
return p1;
}
return p2;
});
System.out.println(reduce1.get());
}
}
9.collect
public class TestApi {
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'));
//9.collect
List<Person> collect = personList.stream().filter(item -> item.getAge() > 20).collect(Collectors.toList());
System.out.println(collect);
}
}
10.limit
public class TestApi {
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'));
//10.limit
//取出两名女性
personList.stream().filter(item->item.getSex()=='F').limit(2).forEach(System.out::println);
}
}
11.skip
public class TestApi {
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'));
//11.skip
//从第二个女性开始取出所有的女性
personList.stream().filter(item->item.getSex()=='F').skip(1).forEach(System.out::println);
}
}
12.distinct
public class TestApi {
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'));
//12.distinct
personList.stream().distinct().forEach(System.out::println);
}
}
6、新增日期时间类
旧的日期时间的缺点:
- 设计比较乱,Date日期在java.util和java.sql也有,而且时间格式转换类在java.text包。
- 线程不安全(主要原因)
新添加的日期时间类
- LocalDate: 表示日期类。yyyy-MM-dd
- LocalTime: 表示时间类。 HH:mm:ss
- LocalDateTime: 表示日期时间类 yyyy-MM-dd t HH:mm:ss sss
- DatetimeFormatter:日期时间格式转换类。
- Instant: 时间戳类。
- Duration: 用于计算两个日期类
public class TestDate {
public static void main(String[] args) {
//LocalDate: 表示日期类。yyyy-MM-dd
LocalDate localDate = LocalDate.now();
System.out.println("现在的日期:"+localDate);
LocalDate localDate1 = LocalDate.of(2022, 5, 1);
System.out.println("自定义的日期:"+localDate1);
//LocalTime: 表示时间类。 HH:mm:ss
LocalTime localTime = LocalTime.now();
System.out.println("现在的时间:"+localTime);
LocalTime localTime1 = LocalTime.of(21, 25, 59);
System.out.println("自定义的时间:"+localTime1);
//LocalDateTime: 表示日期时间类 yyyy-MM-dd t HH:mm:ss sss
LocalDateTime localDateTime = LocalDateTime.now();
System.out.println("现在的日期时间:"+localDateTime);
LocalDateTime localDateTime1 = LocalDateTime.of(2021, 7, 20, 21, 26, 12);
System.out.println("自定义的日期时间:"+localDateTime1);
//DatetimeFormatter:日期时间格式转换类。
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");
String formatLocalDate = localDate.format(dateTimeFormatter);
System.out.println("将日期类型格式化成字符串:"+formatLocalDate);
String date="2021/07/20";
LocalDate parse = LocalDate.parse(date, dateTimeFormatter);
System.out.println("由字符串格式化的日期:"+parse);
//Instant: 时间戳类。
Instant instant = Instant.now();
System.out.println("现在的时间戳:"+instant);
//Duration: 用于计算两个日期类
Duration between = Duration.between(localDateTime1, localDateTime);
System.out.println("两个日期时间之间间隔的天数:"+between.toDays());
System.out.println("两个日期时间之间间隔的小时:"+between.toHours());
}
}