主要内容:
一、Lambda表达式
二、函数式接口
三、方法引用与结构器引用
四、Stream API
五、接口中的默认方法与静态方法
六、更新时间日期API
七、其他新特性
优点:速度更快、代码更少(增加了新的语法Lambda表达式)、强大的Stream API、便于并行、最大化减少空指针异常Optional
一、Lambda表达式: 简化开发
1、简化匿名内部类的写法,简化接口的实现。
lambda表达式的基础语法:Java8中引入了一个新的操作符 "->" 箭头操作符或lambda操作符,箭头操作符将lambda表达式拆分成两部分。
左侧:对应lambda表达式的参数列表。 ( 接口的抽象方法的参数列表)
右侧:对应lambda表达式中所需要执行的功能。即Lambda体 (对抽象方法的实现)
语法格式一:接口无参数,无返回值。实现代码只有一行的时候{}可以省略
()-> {实现功能的代码}
()->实现功能的代码
语法格式二:接口一个参数,无返回值。一个参数的时候小括号可以不写,实现代码只有一行的时候{}可以省略
(e)-> {实现功能的代码}
e->{实现功能的代码}
e->实现功能的代码
语法格式三:接口一个参数,有返回值。一个参数的时候小括号可以不写,实现代码只有一行的时候{}可以省略
(e)-> {return 实现功能的代码}
e->{return 实现功能的代码}
e->实现功能的代码
语法格式四:接口有多个参数,有返回值。实现代码只有一行的时候 return 和 {} 都可以省略
(x,y)-> {return 实现功能的代码}
(x,y)-> 实现功能的代码
语法格式五:Lambda表达式的参数列表的数据类型可以省略不写,因为JVM编译器会通过上下文推断出数据类型,即“类型推断”
二、Lambda表达式需要“函数式接口”的支持
函数式接口:接口中只有一个抽象方法的接口,称为函数式接口。可以使用注解@FunctionalInterface修饰 检查是不是函数式接口。有这个注解的接口,只能有一个抽象方法,否则会报错。
函数式接口我们不用全部都写,java8已经定义了常用的函数式接口,除非有特殊的情况下才需要我们自己写。
Java8内置的四大核心函数式接口:
Consumer<T> :(传一个参数,没有返回值,只对参数进行一些处理)
消费型接口 void accept(T t);
public static void main(String[] args) {
happy(100D,(e)->{
//实现功能,进行消费,无返回值
System.out.println("消费了"+e+"元,用于happy");
});
}
public static void happy(Double money, Consumer<Double> consumer){
consumer.accept(money);
}
Supplier<T>: (调用方法,返回对象)
供给型型接口 T get();
public static void main(String[] args) {
getNumIntegerList(10,()->{
//供给方法,无参数,有返回值,实现功能,
// 返回值如何产生
return (int)(Math.random()*100);
});
}
public static List<Integer> getNumIntegerList(int num, Supplier<Integer> supplier){
List<Integer> integerList = new ArrayList<>();
for (int i =0;i<num;i++){
Integer integer = supplier.get();
integerList.add(integer);
}
return integerList;
}
Function<T,R>: (传入一个参数,经过处理,返回一个对象)
函数型接口 R apply(T t);
public static void main(String[] args) {
String string = strHandler("我-是-被-处-理-的-字-符-串", (str) -> str.replace("-", ""));
}
public static String strHandler(String string, Function<String,String> function){
return function.apply(string);
}
Predicate<T>:(传入一个参数,判断是否符合某种条件)
断言型接口 boolean test(T t);
public static void main(String[] args) {
List<String> stringList = Arrays.asList("ass","fasfgas","asgfaaasg","gdfvb222df","fsadvsdgdsgsd","asgf","gasgvdasfas","gdfgert");
List<String> list = filterStr(stringList,
//断言型,功能:获取字符串大于7的字符串
(str) -> str.length() > 7);
System.out.println(list);
}
public static List<String> filterStr(List<String> stringList, Predicate<String> predicate){
List<String> newStringList = new ArrayList<>();
stringList.forEach(item->{
if(predicate.test(item)){
newStringList.add(item);
}
});
return newStringList;
}
三、方法引用和构造器引用:
若Lambda体中的内容有方法已经实现了,我们可以使用”方法引用“(可以理解为方法引用时Lambda表达式的另外一种表现形式)
使用的注意事项:Lambda体中调用方法的参数列表和返回值类型,要与函数式接口中抽象方法的函数列表和返回值类型保持一致。
主要有三种语法格式:
对象::实例方法名
Consumer<String> com = (string)->System.out.println(string);
com.accept("这是普通Lambda写法");
PrintStream ps1 = System.out;
Consumer<String> com1 = ps1::print;
com1.accept("这是方法引用的写法");
类::静态方法名
Comparator<Integer> comparable = (x, y)->Integer.compare(x,y);
Comparator<Integer> comparable1 = Integer::compareTo;
类::实例方法名 (有前提条件,第一个参数的实例方法的参数是第二个参数的时候才能用)
BiPredicate<String,String> bp = (x,y)->x.equals(y);
BiPredicate<String,String> bp1 = String::equals;
构造器引用
构造器引用取决于函数式接口的实现方法有几个参数,并且参数类型也要保持一致。
四、Stream API
Stream是Java8中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找,过滤和映射数据等操作。使用Stream API 对集合数据进行操作,就类似使用SQL执行数据库查询。也可以使用Stream API 来并行执行操作。简而言之,Stream API 提供了一种搞笑且易于使用的处理数据的方式。
stream:是数据渠道,用于操作数据源
注意:
1、Stream自己不会存储元素
2、Stream不会改变源对象。相反,他们会返回一个持有结果的新Stream
3、Stream操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
1、Stream的创建
//通过Collection系列集合提供的stream()或parallelStream()
List<String> list = new ArrayList<>();
Stream<String> stream = list.stream();
//通过Arrays中的静态方法stream()获取数组流
Integer[] integers = new Integer[10];
Stream<Integer> stream1 = Arrays.stream(integers);
//通过Stream类中的静态方法 of()
Stream<String> stream2 = Stream.of("aaaaa","bbbbbb","cccccc","ddddd");
//创建无限流,迭代
Stream<Integer> iterate = Stream.iterate(0, (x) -> x + 2);
//生成长度为5 数据为随机数的流
Stream.generate(()->Math.random()).limit(5).forEach(System.out::println);
2、中间操作 (只有中间操作是不会执行的,需要有终止操作才会执行),即“惰性求值”。
*筛选与切片
filter——接收Lambda,从流中排除不满足条件的元素
limit——截断流,使其元素不超过给定数量。达到了指定数量后就不会执行了
skip(n)——跳过元素,返回一个扔掉了前n个元素的流,若流中的元素不足n个,则返回一个空流,与limit互补。
distinct——筛选,通过流所生成元素的hashCode()和equals去除重复元素。
*映射
map——接收Lambda,将元素转换成其他形式或者提取信息,接收一个函数作为参数,该函数会被应用到每个元素上,并映射成一个新的元素。
flatMap——接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。
*排序
sorted()——自然排序(Comparable)
sorted(Comparator com)——定制排序(Comparator)
3、终止操作
*查找与匹配
allMatch——检查所有元素是否都符合条件(方法参数是一个Predicate函数接口,条件写进去)
anyMatch——检查是否存在一个或一个以上的元素符合条件(方法参数是一个Predicate函数接口,条件写进去)
noneMatch——检查是否所有元素都不符合条件(方法参数是一个Predicate函数接口,条件写进去)
findFirst——返回第一个元素
findAny——返回当前流中的任意元素
count——返回流中的总个数
max——返回流中最大值
min——返回流中最小值
*归约
reduce(T indentity, BinaryOperator) / reduce(BinaryOperator)——可以将流中元素反复结合起来,得到一个值。 (indentity 运算的初始值 BinaryOperator定义如何进行结合)
*收集
collect——将流转换成其他形式,接收一个Collector接口的实现,用于做stream中元素的汇总。
collect方法里的Collectors能进行其他计算,包括总数,平均值、总和、最大值、最小值、分组、分区等等.....
总数:Collectors.counting(arg)
平均值:Collectors..averagingDouble(arg)
总和:Collectors.summingDouble(arg)
最大值:Collectors.maxBy(arg)
最小值:Collectors.minBy(arg)
分组:Collectors.groupingBy(arg)
多级分组:Collectors.groupingBy(arg,Collectors.groupingBy(arg))
分区:true,false分区Collectors.partitioningBy(arg)
根据参数某个字段获取到总数,平均值、总和、最大值、最小值等数据的封装体对象Collectors.summarizingDouble(arg)
4、并行流与顺序流
并行流就是把内容分成多个数据块,并用不同的线程分别处理每个数据块的流。
Java8中将并行进行了优化,我们可以很容易的对数据进行并行操作,Stream API 可以声明性的地通过parallel() 与 sequential() 在并行流与顺序流之间进行切换。
5、Optional类
Optional<T>类(java.util.Optional) 是一个容器类,代表一个值存在或不存在,原来用null表示一个值不存在,现在 Optional 可以更好的表达这个概念。并且可以避免空指针异常。
常用方法:
Optional.of(T t):创建一个Optional实例
Optional.empty():创建一个空的Optional实例
Optional.ofNullable(T t):若t不为null,创建Optional实例,否则创建空实例
isPresent():判断是否包含值
orElse(T t) :如果调用对象包含值,否则返回t
orElseGet(Supplier s):如果调用对象包含值,返回该值,否则返回s获取的值
map(Function f) :如果有值对其处理,并返回处理后的Optional,否则返回Optional.empty()
flatMap(Function mapper):与map类似,要求返回值必须是Optional
五、接口中的默认方法与静态方法
在java8中运行接口中有default修饰的默认方法。
六、新时间日期API
//1、LocalDate获取日期、
// LocalTime获取时间、
// LocalDateTime获取日期时间,日期与时间之间 T 分隔
//三个的使用方式类似,这里用LocalDateTime测试
LocalDateTime localDateTime = LocalDateTime.now(); //获取当前日期时间
LocalDateTime.of(2021,10,21,15,28,16);//获取指定年月日时分秒的时间
//plusYears加年 plusMonths加月.....
LocalDateTime localDateTime1 = localDateTime.plusSeconds(10);//加10秒,获取到新实例
//minus... 减获取年月日时分秒...
//get... 获取年月日时分秒...
//2、Instant:时间戳(以Unix元年:1970年1月1日 00:00:00 到某个时间的毫秒值)
Instant now = Instant.now();//获取到目前时间,默认获取以UTC时期为基础,不是电脑系统的当前时间
OffsetDateTime offsetDateTime = now.atOffset(ZoneOffset.ofHours(8));//操作偏移量,偏移8个小时
now.toEpochMilli();//获取毫秒值
Instant.ofEpochSecond(60);//加60秒
//3、计算日期时间之间的操作
//Duration:计算两个 时间 之间的间隔
//Period:计算两个 日期 之间的间隔
Instant now1 = Instant.now();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Instant now2 = Instant.now();
Duration.between(now1,now2);
大多是看文档使用的东西,引用一手别人的文章,使用的时候翻翻。
Java8日期时间API详解_78KgMiao的博客-优快云博客
七、重复注解与类型注解
@Repeatable(MyAnnotations.class) //指定容器类
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String value() default "Java 8";
}
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotations {
MyAnnotation[] value();
}
public class Test01 {
//重复注解
@Test
@MyAnnotation("Hello")
@MyAnnotation("World")
public void test01() throws NoSuchMethodException {
Class<Test01> clazz = Test01.class;
Method test01 = clazz.getMethod("test01");
MyAnnotation[] mas = test01.getAnnotationsByType(MyAnnotation.class);
for (MyAnnotation ma : mas) {
System.out.println(ma.value());
}
}
}