Java8新特性
特点:
速度更快:hashMap红黑树
代码更少:lambda表达式
强大的Stream API
便于并行:提高了程序的执行效率
最大化减少空指针异常:Optional
Nashorn引擎,允许在JVM上运行JS应用
Lambda表达式
可以给函数式接口赋值
使用:
举例:
(o1,o2) -> Integer.compare(o1,o2)
格式:
->
:lambda操作符 或 箭头操作符->
左边 : lambda形参列表(其实就是接口中抽象方法的形参列表)->
右边 : lambda体(其实就是重写的抽象方法的方法体)
Lambda表达式的使用:(分为6种情况介绍)
无参,无返回值
Runnable run = new Runnable() { @Override public void run() { System.out.println("gogogo"); } }; run.run(); System.out.println("**********lambda************"); Runnable run1 = () ->{ System.out.println("runrunrun"); }; run1.run();
有参,无返回值
Consumer<Integer> con = new Consumer<Integer>() { @Override public void accept(Integer integer) { System.out.println(integer); } }; con.accept(123); System.out.println("**********lambda************"); Consumer<Integer> con1 = (Integer integer)->{ System.out.println(integer); }; con1.accept(456);
类型推断
System.out.println("**********类型推断************"); Consumer<Integer> con2 = (i)->{ System.out.println(i); }; con2.accept(789);
只有1个参数,参数小括号可以省略
System.out.println("**********1个参数************"); Consumer<Integer> con2 = i->{ System.out.println(i); }; con2.accept(789);
有2个或以上参数,多条执行语句,并且有返回值
Comparator<Integer> comparator = new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { System.out.println(o1); System.out.println(o2); return o1.compareTo(o2); } }; System.out.println(comparator.compare(1,2)); System.out.println("***有2个或以上参数,多条执行语句,并且有返回值***"); Comparator<Integer> comparator1 = (o1,o2)->{ System.out.println(o1); System.out.println(o2); return o1.compareTo(o2); }; System.out.println(comparator1.compare(1,2));
当lambda体只有一条语句时,return与大括号都可以省略
System.out.println("***只有一条语句时,return与大括号都可以省略***"); Comparator<Integer> comparator2 = (o1,o2)-> o1.compareTo(o2); System.out.println(comparator2.compare(1,2));
语法总结
->
左边:lambda形参列表的参数类型可以省略(类型推断);形参只有1个,()省略->
右边:lambda体应该用{}包裹,如果只有一条执行语句,{}省略,return也需要省略Lambda表达式的本质:作为函数式接口的实例
如果一个接口中,只声明了一个抽象方法,则此接口就称为函数式接口
函数式接口
@FunctionalInterface
如果一个接口中,只声明了一个抽象方法,则此接口就称为函数式接口。如:Runable、compartor
可以通过Lambda表达式来创建该接口的对象,即以前用匿名是西安类表示的现在都可以用Lambda表达式来写
java.util.function
提供了大量的函数式接口
接口名 | 返回值 方法 | |
---|---|---|
消费型接口 | Consumer |
void accept(T t) |
供给型接口 | Supplier |
T get() |
函数型接口 | Function<T,R> | R apply(T t) |
断定型接口 | Predicate |
boolean test(T t) |
@Test
public void test1(){
consume(200, new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
System.out.println("一共消费"+integer);
}
});
System.out.println("******lambda*******");
consume(200,i ->{
System.out.println("一共消费"+i);
});
}
public void consume(Integer money, Consumer<Integer> consumer){
consumer.accept(money);
}
@Test
public void test2(){
List<String> list = Arrays.asList("北京","东京","西京","普京","北极");
List<String> l = filterString(list, new Predicate<String>() {
@Override
public boolean test(String s) {
//制定规则,如果字符串中包含京则返回为true
return s.contains("京");
}
});
System.out.println(l);
System.out.println("*********lambda**********");
List<String> l2 = filterString(list, s -> s.contains("京"));
System.out.println(l2);
}
public List<String> filterString(List<String> list,Predicate<String> pre){
List<String> l= new ArrayList<>();
for (String s : list){
//遍历集合,按照某种规则进行判断,为true则添加进集合
if(pre.test(s)){
l.add(s);
}
}
return l;
}
方法引用与构造器引用
在一些特殊情况下,我们可以使用方法引用来替换lambda表达式
- 使用情境:当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用
- 方法引用,本质上就是Lambda表达式,而Lambda表达式作为函数时接口的实例,所以方法引用,也是函数式接口的实例
- 使用格式: 类(或对象) :: 方法名
- 具体有三种情况
- 对象 ::非静态方法
- 类 ::静态方法
- 类::非静态方法
//对象::非静态方法
@Test
public void test1(){
Consumer<String> consumer1 = s -> System.out.println(s);//lambda体中已经有实现的方法println(),参数和返回值一样,就可以使用方法引用
consumer1.accept("abc");
System.out.println("******方法引用******");
PrintStream print = System.out;//对象
Consumer<String> consumer2 = print :: println;//对象::方法
consumer2.accept("def");
}
//类::静态方法
@Test
public void test2(){
Comparator<Integer> comparator1 = (o1,o2) -> Integer.compare(o1,o2);
System.out.println(comparator1.compare(1,2));
System.out.println("******方法引用*********");
Comparator<Integer> comparator2 = Integer::compare;
System.out.println(comparator2.compare(1,2));
}
//类::非静态方法
@Test
public void test3(){
Comparator<Integer> comparator = (o1,o2) -> o1.compareTo(o2);//compareTo是非静态方法
System.out.println(comparator.compare(1,2));
System.out.println("*******方法引用*******");
//compareTo是通过参数Integer o1调用的,::左边使用调用所属的类,右边为方法
Comparator<Integer> comparator1 = Integer::compareTo;
System.out.println(comparator1.compare(2,3));
}
方法引用使用的要求:
函数式接口中的抽象方法的形参列表和返回值类型与方法引用的方法的形参列表和返回值类型相同
(此要求适用于1,2两种情况)
构造器引用
抽象方法的返回值类型即为构造器所属的类的类型
//构造器引用
@Test
public void test4(){
Supplier<Student> supplier = () -> new Student();
System.out.println(supplier.get());
System.out.println("*****方法引用*****");
Supplier<Student> supplier1 = Student::new;
System.out.println(supplier1.get());
}
//有参构造器引用
@Test
public void test5(){
Function<Integer,Student> function = i -> new Student(i);
System.out.println(function.apply(1));
System.out.println("******方法引用*******");
Function<Integer,Student> function1 = Student::new;
System.out.println(function1.apply(2));
System.out.println("******二元构造器*********");
BiFunction<Integer,String,Student> biFunction = (i,s) -> new Student(i,s);
System.out.println(biFunction.apply(3,"xiaoming"));
System.out.println("****方法引用*****");
BiFunction<Integer,String,Student> biFunction1 = Student::new;
System.out.println(biFunction1.apply(4,"xiaohong"));
}
数组引用
可以看作一个特殊的类,写法和构造器一样
//数组引用
@Test
public void test6(){
Function<Integer,String[]> function1 = new Function<Integer, String[]>() {
@Override
public String[] apply(Integer integer) {
return new String[integer];
}
};
System.out.println("****lambda******");
Function<Integer,String[]> function2 = length -> new String[length];
String[] str = function2.apply(10);
System.out.println(Arrays.toString(str));
System.out.println("******方法引用**********");
Function<Integer,String[]> function3 = String[] :: new;
String[] str3 = function3.apply(10);
System.out.println(Arrays.toString(str3));
}
Stream API
java.util.stream
为什么要使用streamAPI:
我们在处理数据时,会设置一些过滤条件,我们可以通过在数据库层面进行过滤操作。如果是redis等Nosql数据库,我们就需要在java层面进行过滤,StreamAPI可以实现这一点。
Stream和Collection集合的区别:
Collection是静态的内存数据结构,用于存储数据,主要面向内存
Stream是有关计算的(不存储数据,和迭代器类似),主要面向CPU
注意:
- Stream不存储数据
- Stream不会改变源对象,相反,它们会返回一个持有结果的新Stream
- Stream操作是延迟执行的。只有进行终止操作,才会执行前面的中间操作
Stream 执行流程
- Stream的实例化
- 一系列的中间操作(过滤、映射)
- 终止操作
说明:
- 一个中间操作链,对数据源的数据进行处理
- (创建、中间操作、中止操作),一旦执行终止操作,就执行中间操作链,并产生结果。之后,不会再被使用。
Stream的实例化
通过集合
default Stream stream() 返回一个顺序流 顺序流在操作是会按照顺序操作 default Stream parallelStream() 返回一个并行流 并行流在操作是会多个一起操作 List<Student> students = StudentData.getStudents(); Stream<Student> stream = students.stream();//顺序流 Stream<Student> parallelStream = students.parallelStream();//并行流
通过数组
调用Arrays类的静态方法 Stream stream(T[] array) 返回一个流 int[] arr = new int[]{1,2,3,4,5,6}; IntStream stream = Arrays.stream(arr); Student[] arr1 = new Student[]{stu1,stu2}; Stream<Student> stream1 = Arrays.stream(arr1);
通过Stream类本身
通过Stream类本身的静态方法 public static Stream of(T… values) 返回一个流 Stream<Integer> stream = Stream.of(1,2,3,4,5);
创建无限流
作用:生成一些东西
public static |
参数:UnaryOperator,一个特殊的function,传入一个类型返回相同类型 | 迭代 |
---|---|---|
public static |
参数:Supplier,供给型接口,不传值生产值 | 生成 |
//迭代
//遍历前10个偶数
Stream.iterate(0,t -> t+2 )//创建流:从0开始,传入一个数就加2
.limit(10)//中间操作:限制数为10
.foreach(System.out::println);//中止操作:遍历(参数:消费者接口)
//生成
Stream.generate(Math::random).limit.foreach(System.out::println);
中间操作
筛选与切片
filter(Predicate p) | 从流中排除某些数据 |
---|---|
limit(n) | 截断流,使其元素不超过元素不超过给定数量 |
skip(n) | 跳过元素,返回一个扔掉了前n个元素的流,若流中元素不足N个,则返回一个空流 |
distinct() | 筛选,通过流所生成元素的hashCode()和equals()去除重复元素 |
stream.filter(e -> e.getAge()>20).foreach(System.out::println);
映射
map(Function f) | 将元素转换为其他形式或提取信息,该函数会被应用到每个元素,并将其映射成新的元素,返回值仍然是一个流 |
---|---|
flatMap(Function f) | 将流中的每个值都换成另一个流,然后把所有的流连成一个流。如集合中的addAll()方法,List.add(list)导致结果中list中有list,addAll就会将内层List拆散再加到list中。flatMap也是相同的道理,将流拆散加到总流里 |
List<String> list = Arrays.asList("aa","bb");
list.stream().map(t->t.toUpperCase()).foreach(System.out::println);//将集合映射成大写
//获取学生姓名长度大于3的员工姓名
List<Student> students = StudentData.getStudents();
Stream<String> nameStream = students.stream(),map(stu->stu.getname());
nameStream.filter(name -> name.length()>3).forEach(System.out::println);
排序
sorted() | 自然排序 |
---|---|
sorted(Compartor com) | 定制排序 |
//自然排序
List<Integer> list = Arrays.asList(1,12,2,45,34,23,12,3,-1);
list.stream().sorted().forEach(System.out::println);
//定制排序
List<Student> students = new ArrayList<>();
students.add(new Student(2,"c"));
students.add(new Student(1,"b"));
students.add(new Student(2,"a"));
students.stream().sorted((o1,o2)->{
int compare = Integer.compare(o1.getId(), o2.getId());
if(compare != 0){
//当id不相等时,进行排序
return compare;
}else{
//当id相等,根据name进行排序
int i = o1.getName().compareTo(o2.getName());
return i;
}
}).forEach(System.out::println);
终止操作
匹配与查找
allMatch(Predicate p) | 检查是否所有元素都符合条件 |
---|---|
anyMatch(Predicate p) | 检查是否至少一个元素符合条件 |
nonMatch(Predicate p) | 检查是否没有元素符合条件 |
findFirst | 返回第一个元素 |
findAny | 返回当前流的任意元素 |
count | 返回流的元素总个数 |
max(Comparator c) | 返回流中最大值 |
min(Comparator c) | 返回流中最小值 |
forEach(Consumer c) | 内部迭代 |
//检查所有元素id都大于1
boolean b = students.stream().allMatch(s -> s.getId() > 1);
//返回id最大的元素
Optional<Student> max = students.stream().max((o1, o2) -> Integer.compare(o1.getId(), o2.getId()));
System.out.println(max);
//内部迭代
//students.stream().forEach(s -> System.out.println(s));
students.stream().forEach(System.out::println);
归约
reduce(T seed,BinaryOperator b) | 可以将流中元素反复结合起来,得到一个值,返回T | 参数:seed:初始值,BinaryOperator 二元函数 |
---|---|---|
reduce(BinaryOperator b) | 可以将流中元素反复结合起来,得到一个值,返回Optional |
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
Optional<Integer> sum = list.stream().reduce(Integer::sum);
//Optional<Integer> sum = list.stream().reduce((i1, i2) -> i1 + i2);
System.out.println(sum);
List<Student> students = new ArrayList<>();
students.add(new Student(2,"c"));
students.add(new Student(1,"b"));
students.add(new Student(2,"a"));
Stream<Integer> idStream = students.stream().map(s -> s.getId());//获得id流
//idStream.reduce(Integer::sum);
Optional<Integer> sum1 = idStream.reduce((i1, i2) -> i1 + i2);
System.out.println(sum1);
一般情况下,归约会和map映射一起使用
收集
Collector:通常用静态方法来调用 | Collectors.toList(),toSet() |
---|---|
collect(Collector c) | 将流转换为其他形式(List,Set) |
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
Stream<Integer> stream = list.stream().filter(e -> e > 5);
List<Integer> list1 = stream.collect(Collectors.toList());
// for (Integer i:list1) {
// System.out.println(i);
// }
list1.forEach(System.out::println);
Optional类
作用:有效的解决空指针异常
它是一个容器类,保存类型为T的值,代表这个值存在。或者保存一个Null,表示值不存在。就是将一个对象boy
,把它的值放在optional类中,我们操作boy其实就是在操作optional,如果boy是Null,则optional类的value值就是Null
创建Optional类对象的方法
Optional.of(T t) | 创建一个实例,t必须非空 |
---|---|
Optional.empty() | 创建一个空的Optional实例 |
Optional.ofNullable(T t) | t可以为Null |
判断Optional容器中是否包含对象
boolean isPresent()
:判断是否包含对象
获取Optional容器的对象
T get() | 获取optional的值,如果值为空则会抛异常 |
---|---|
T orElse(T other) | 有值将其返回,没有则返回参数other对象 |
通常
Optional.orf(T t)和get()一起使用,在保证非空的情况下再获取数据
Optional.ofNullable(T t)和orElse(T other)一起使用,在不确定空不空的情况下获取数据,other有兜底对象
Student student = new Student(1,"正宫");
student = null;
Optional<Student> studentOptional = Optional.ofNullable(student);
//student2一定非空
Student student2 = studentOptional.orElse(new Student(10, "备选对象"));
System.out.println(student2.getName());//备选对象
接口默认方法、静态方法
除了定义全局常量和抽象方法意外,还可以定义静态方法、默认方法
- 静态方法只能通过接口调用
- 通过实现类的对象,可以调用接口中默认方法
- 类优先原则,如果接口的方法和子类方法同名,先调用的是extends子类的方法
- 如果实现了两个接口,其中有同名方法,实现类重写哪个默认方法,就调用的是重写后的方法
可重复注解、新类型注解
可重复注解:
- 在注解类上声明@Repeatable,成员值为MyAnnotations.class
- 在MyAnnotation的元注解和MyAnnotations相同
@Repeatable(MyAnnotations.class)//可重复注解,参数:注解类的数组 @Target({TYPE,FIELD,METHOD,PARAMETER})//能用于修饰哪些程序元素 @Retention(RetentionPolicy.RUNTIME)//修饰的注解生命周期 public @interface MyAnnotation{ String value() default "abc"; } @Target({TYPE,FIELD,METHOD,PARAMETER})//必须和上个注解保持一致 @Retention(RetentionPolicy.RUNTIME)//必须和上个注解保持一致 public @interface MyAnnotations{ MyAnnotation[] value(); } public class Test{ public class Test{ @MyAnnotation("hello") @MyAnnotation("world") public void show(){ } @Test public void test1(){ //通过反射获取注解 Class<TestAnnotation> clazz = Test.class; Method m1 = clazz.getMethod("show"); MyAnnotation[] mas = m1.getAnnotationsByType(MyAnnotation.class);//获取注解数组 for(MyAnnotation myAnnotation : mas){ System.out.println(myAnnotation.value());//hello world } } } }
新类型注解(Target)
- ElementType.TYPE_PARAMETER 表示该注解能写在类型变量的声明语句中
- ElementType.TYPE_USE 标识该注解能写在使用类型的任何语句中