😀😀😀创作不易,各位看官点赞收藏.
Java之jdk8新特性
文章目录
Java8为Java语言、编译器、类库、开发工具与JVM带来了大量的新特性。例如函数式接口、Lambda表达式、Stream、接口的静态方法和默认方法、集合中的新特性、Optional类、新日期类等。
1、Lambda 表达式
1.1、函数式接口
函数式接口:只包含一个抽象方法的接口,但是可以拥有静态方法和默认方法,称为函数式接口。在
java.util.function
包下定义了Java 8丰富的函数式接口。
// 这个注解是用来检查这个接口是否为函数式接口,可以不使用这个注解
@FunctionalInterface
public interface MyInterface {
// 只有一个抽象方法
void method1();
// 静态方法
static void method2(){
System.out.println("我是函数式接口中的静态方法");
}
// 默认方法
default void method3(){
System.out.println("我是函数式接口中的默认方法");
}
}
@FunctionalInterface注解:使用在接口上,检查该接口是否为函数式接口,同时在Javadoc中也会包含函数接口这条声明。
在Java中内置了很多的函数式接口,核心的4大函数式接口:
public static void main(String[] args) {
// 消费性接口:需要一个参数,没有返回值
Consumer<String> consumer = s -> {
System.out.println("我是消费型接口==>"+s);
};
consumer.accept("Hello,World");
// 供给型接口:不需要参数,有返回值
Supplier<String> supplier = () -> {
int a = 100;
return a+"分";
};
System.out.println(supplier.get());
// 函数式接口:有一个参数,返回值是另一个类型的参数
Function<String,Integer> function = s ->{ // <>泛型第一个是参数类型,第二个是返回值类型
System.out.println("我是参数式接口===>"+s);
return 100;
};
System.out.println(function.apply("Hello,World"));
// 判定型接口,有一个参数,返回值是boolean
Predicate<Integer> predicate = num ->{
return num>0;
};
System.out.println(predicate.test(-10)); // false
}
1.2、使用Lambda
Lambda表达式:是一个匿名函数,一段可以传递的代码。使用它可以写出简洁、灵活的代码,一种新的语法格式。它可以创建一个函数式接口的实现类,针对函数式接口使用Lambda表达式创建一个实现类。
public static void main(String[] args) {
// Comparator接口是一个函数式接口
// 不使用Lambda表达式,创建一个接口匿名子类对象
Comparator<Integer> comparator1 = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1,o2);
}
};
System.out.println(comparator1.compare(29, 30));
// 使用Lambda表达式来创建一个接口匿名子类,因为接口中只有一个方法,直接可以省略方法名,只写方法体就可以
Comparator<Integer> comparator2 = (o1,o2) -> {
return Integer.compare(o1,o2);
};
System.out.println(comparator2.compare(32, 12));
}
Lambda形式:
- -> :lambda操作符或者叫箭头操作符。
- 箭头左边(o1,o2):lambda的形参列表(本质是接口抽象方法的形参列表)。
- 箭头右边:lambda体(重写接口抽象方法的方法体)。
// (o1,o2) 是形参列表,如果没有参数直接是一个括号,可以表明参数的类型(Integer o1,Integer o2),也可以不用标明的直接通过类型推断就可以知道
// {方法体} 重写接口方法的方法体
Comparator<Integer> comparable = (o1, o2) -> {
return Integer.compare(o1,o2);
};
// 如果参数列表只有一个参数,可以省略小括号,但是没有参数时必须加上小括号
Consumer<String> consumer = s -> {
System.out.println(s);
};
// 当lambda体只有一条语句时,可以把大括号省略,如果有返回值,也可以将return省略
Comparator<Integer> comparable1 = (o1, o2) -> Integer.compare(o1,o2);
Lambda表达式必须依赖于函数式接口,只能创建函数式接口的实现类。
1.3、方法引用
方法引用:当传递给Lambda方法体中,已经有实现方法了,就可以使用方法引用了。要求:实现接口的抽象方法的参数列表和返回值类型必须和方法引用的参数列表和返回值类型一样。方法引用的操作符是
::
,方法引用本质上是一个Lambda,Lambda是一个函数式接口的实例,那么方法引用也是一个函数式接口的实例对象。
方法引用的三种情况:
- 对象 :: 非静态方法名
- 类 :: 静态方法名
- 类 :: 非静态方法名
// 对象 :: 非静态方法
public static void main(String[] args) {
// 不使用引用方法
Consumer<String> consumer1 = s -> {
System.out.println(s);
};
consumer1.accept("Hello,World");
// 因为Consumer接口的accept方法与System.out对象的println()都是一个String参数没有返回值
// 这样就可以使用方法引用,使用方法引用来替换Lambda表达式
Consumer<String> consumer2 = System.out::println;
consumer2.accept("Hello,World");
}
// 类::静态方法
public static void main(String[] args) {
// 不使用方法引用
Comparator<Integer> comparator1 = (o1,o2) -> {
return Integer.compare(o1,o2);
};
System.out.println(comparator1.compare(10, 2));
// Comparator的compare方法和Integer.compare静态方法的参数列表和返回值相同,可以使用方法引用
Comparator<Integer> comparator2 = Integer::compare;
System.out.println(comparator2.compare(3, 10));
}
// 类::非静态方法
public static void main(String[] args) {
// 不使用方法引用
Comparator<String> comparator1 = (s1,s2) -> {
return s1.compareTo(s2);
};
System.out.println(comparator1.compare("adb", "adt"));
// 使用方法引用,类::非静态方法
/* 要求:
如果接口有两个参数,第一个参数需要作为方法的调用者;
如果接口有一个参数,参数需要作为方法的调用者
*/
Comparator<String> comparator2 = String::compareTo; // s1.compareTo(s2),第一个参数作为了方法的调用者
System.out.println(comparator2.compare("abc", "abcd"));
}
构造器引用:
// 构造器引用
public static void main(String[] args) {
// 不使用构造器引用
Function<Integer,User> function1 = (id) -> {
return new User(id);
};
System.out.println(function1.apply(1001));
// 使用构造器引用,构造器的参数列表和函数式抽象方法得参数列表要相同
// 这个相当于是调用了某个类的构造器,创建了一个对象
Function<Integer,User> function2 = User::new;
System.out.println(function2.apply(1002));
}
数组引用:
// 数组引用:可以看成一个构造器引用
public static void main(String[] args) {
// 不使用数组引用
Function<Integer,String[]> function1 = integer -> {
return new String[integer];
};
System.out.println(Arrays.toString(function1.apply(10)));
// 使用数组引用
Function<Integer,String[]> function2 = String[]::new;
System.out.println(Arrays.toString(function2.apply(5)));
}
2、Stream API
Stream:把真正的函数式编程风格引入Java中,是目前Java类库最好的补充,它可以让程序更加简洁、高效。使用Stream对集合中数据进行计算操作,类似于SQL执行的数据查询,是一种高效且容易的一种数据处理方式。
Stream特点:
- 数一种数据渠道,操作数据源(集合、数组)所在的元素序列。
- 它不会自己存放数据。
- 它不会改变数据源,会返回一个新持有结构的Stream。
- 它是延迟操作的,只有它需要结果的时候才会进行计算数据。
Stream的三个步骤:
- 创建Stream:通过一个数据源获取一个Stream流。
- 中间操作:对数据源的处理。
- 终止操作:一旦执行终止操作,就会执行中间操作,并产生对应的结果。
2.1、Stream实例化
集合方式实例化Stream流:
// 集合实例化Stream流
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("hello");
list.add("world");
list.add("你好");
// 实例化一个Stream流
Stream<String> stream1 = list.stream(); // 实例化集合的一个顺序流
Stream<String> stream2 = list.parallelStream(); // 实例化集合的一个并行流
}
- 顺序流:在操作集合数据时,选择数据是按照集合中的顺序一个一个操作。
- 并行流:同时会有对个线程对多个数据并行进行操作。
数组方式实例化Stream流:
// 数组方式实例化Stream流
public static void main(String[] args) {
String[] array = new String[]{"张三","李四","王五"};
// 通过Arrays的stream()方法,参数是一个数组
Stream<String> stream = Arrays.stream(array);
}
Stream类实例化:
public static void main(String[] args) {
// 参数是一个可变参数,这些元素作为数据源
Stream<String> stream = Stream.of("张三", "李四", "王五");
}
2.2、中间操作
多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行。而且在触发终止操作时,会一次性将中间操作全部执行完成,称为惰性求值。
2.2.1、筛选和切片
// Stream流中的筛选和切片
public static void main(String[] args) {
List<Integer> array = Arrays.asList(29, 34, 34, 35, 12, 23, 67, 12, 1, 2, 3);
Stream<Integer> stream = array.stream();
// 筛选
// filter()参数是一个Predicate的判定型接口,forEach()是一个遍历的终止操作参数是一个消费函数式接口
// 这样才会执行中间操作才会有结果
stream.filter(num -> num>20).forEach(System.out::println);
System.out.println();
stream = array.stream();
// limit()只会选择前n个元素,n作为参数
stream.limit(10).forEach(System.out::println);
System.out.println();
stream = array.stream();
// 这会根据元素的重写的hashcode()和equals()方法去掉重复数据
stream.distinct().forEach(System.out::println);
System.out.println();
stream = array.stream();
// 这会跳过前n个元素,n是参数,如果元素个数小于n就会返回一个空的stream流
stream.skip(3).forEach(System.out::println);
}
2.2.2、映射
映射:将每一个元素通过一系列操作,返回一个新的数据。类似函数中自变量每一个x会对应一个y值。
// 映射
public static void main(String[] args) {
List<User> list = new ArrayList<>();
list.add(new User(1001,"张三1"));
list.add(new User(1002,"张三2"));
list.add(new User(1003,"张三3"));
list.add(new User(1004,"张三4"));
list.add(new User(1005,"张三5"));
// 将集合中user的名称提取出来
Stream<User> stream = list.stream();
// map()参数是一个函数式接口,存在一个参数返回一个新的数据
// 参数是一个user对象,返回值是一个user名称字符串
stream.map(User::getName).forEach(System.out::println);
stream = list.stream();
// flatMap():每一个元素将返回的数值当成一个新的stream,然后将每一个stream流拼接起来
stream.flatMap(user->{
String[] array = new String[]{user.getName()}; // 将name加入到一个String数组
return Arrays.stream(array); // 返回一个stream流
}).forEach(System.out::println);
}
2.2.3、排序
// 排序
public static void main(String[] args) {
List<Integer> list = Arrays.asList(14, 34, 2, -90, 23, 43, -3);
Stream<Integer> stream = list.stream();
// sorted():会根据元素中的实现的comparable进行排序,如果没有实现接口就会报错
stream.sorted().forEach(System.out::println); // 从小到大
System.out.println();
stream = list.stream();
// sorted(CompareTo c):会根据自定的CompareTo接口进行排序
stream.sorted((e1,e2)->-(e1-e2)).forEach(System.out::println); // 从大到小
}
2.3、终止操作
终止操作:终止操作会执行对应的中间操作,其结果可以是任何不是流的值,例如List、Integer、void等。流进行了终止操作后,就不能在继续操作流了不然会报错。
2.3.1、匹配和查询
// 匹配
public static void main(String[] args) {
List<Integer> list = Arrays.asList(23, 4, 1, -25, 0, 19, 65);
Stream<Integer> stream = list.stream();
// allMatch() 参数是一个判断性函数式接口,如果所有元素都满足条件就返回true
boolean match1 = stream.allMatch(e -> e >= 0);
System.out.println(match1); // false
stream = list.stream();
// anyMatch() 参数是判断型函数接口,如果有一个元素满足条件就返回true
boolean match2 = stream.anyMatch(e -> e >= 100);
System.out.println(match2); // false
stream = list.stream();
// noneMatch() 参数是判断型接口,没有任何元素匹配就返回true
boolean match3 = stream.noneMatch(e -> e >= 100);
System.out.println(match3); // true
stream = list.stream();
// 返回流中的第一个元素
Optional<Integer> first = stream.findFirst();
System.out.println(first);
stream = list.parallelStream();
// 返回流中任意一个元素,对于顺序流一般是第一个元素,如果是并行流是随机一个元素
Optional<Integer> any = stream.findAny();
System.out.println(any);
}
// 查找
public static void main(String[] args) {
List<Integer> list = Arrays.asList(23, 4, 1, -25, 0, 19, 65);
Stream<Integer> stream = list.stream();
// 查询流中大于10元素的个数
long count = stream.filter(e -> e>10).count();
System.out.println(count);
stream = list.stream();
// 查询流最大元素,参数是一个Comparator函数式接口
Optional<Integer> max = stream.max(Integer::compare);
System.out.println(max);
stream = list.stream();
// 查询流中最小元素,参数是Comparator函数式接口
Optional<Integer> min = stream.min(Integer::compare);
System.out.println(min);
stream = list.stream();
// 内部迭代,参数是一个消费型函数接口
stream.forEach(System.out::println);
// 集合中的forEach()方法,参数也是一个消费型接口
list.forEach(System.out::println);
}
2.3.2、归约
// 归约:将流中的数据结合起来得到一个值
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Stream<Integer> stream = list.stream();
// 求集合中元素的总和,初始值是0
// reduce() 参数一:初始值;参数二:一个BinaryOperator函数式接口,两个参数一个返回值
Integer sum1 = stream.reduce(0, (e1,e2) -> e1+e2);
System.out.println(sum1);
stream = list.stream();
// reduce() 只有一个参数BinaryOperator函数式接口时,它只返回一个Option<T>值,将流中的数据反复结合起来
// 他没有初始值,只会根据元素类型进行结合
Optional<Integer> sum2 = stream.reduce(Integer::sum);
System.out.println(sum2);
}
2.3.3、收集
Collector这个接口的实现决定了对流执行收集的操作(收集到List、Set、Map)。Collectors类是一个工具类提供了很多静态方法,可以创建常见的收集器实例。下图是Collectors的一些静态方法:
// 收集:将stream流中的数据收集到一个集合中
public static void main(String[] args) {
List<Integer> list = Arrays.asList(45, 2, -10, 32, -9, 2, -78,90,23);
Stream<Integer> stream = list.stream();
// collect()参数是一个Collector接口实例,通过Collectors来创建对应实例
List<Integer> result = stream.filter(e -> e > 10).collect(Collectors.toList());
result.forEach(System.out::println);
}
3、Optional 类
Optional:一个容器类,它可以保存类型T的值,代表这个值存在。或者仅仅保存null值表示这个值不存在。原来使用null来表示一个值不存在,现在可以使用Optional类来表示可以避免空指针异常问题。
3.1、实例化Optional类
// 实例化Optional类
public static void main(String[] args) {
User user = new User();
// of()参数是一个存放的数据,不要保证数据不为null,如果为null会报错
Optional<User> optional1 = Optional.of(user);
// 创建一个数据为null的Optional类
Optional<Object> optional2 = Optional.empty();
user = null;
// ofNullable()参数是存放的数据,存放的数据可以为空
Optional<User> optional3 = Optional.ofNullable(user);
}
3.2、常用方法
判断型方法:
// 判断型方法
public static void main(String[] args) {
User user = new User();
user = null;
Optional<User> optional = Optional.ofNullable(user);
// 判断容器中是否存在数据,数据为null返false
boolean b1 = optional.isPresent();
System.out.println(b1); // false
optional = Optional.ofNullable(new User("张三"));
// 参数是一个消费型接口,如果存放数据不为null就将数据作为消费型接口的参数
// 如果数据为空就不会执行消费型接口实现类中的方法
optional.ifPresent(System.out::println);
}
获取数据方法:
// 获取数据方法
public static void main(String[] args) {
User user = new User("张三");
Optional<User> optional = Optional.ofNullable(user);
// 返回容器中存放的数据,如果数据为null会报错
User user1 = optional.get();
System.out.println(user1); // 张三
optional = Optional.ofNullable(null);
// orElse() 参数是一个备胎参数,如果容器中的数据为null,它会返回参数这个对象
User user2 = optional.orElse(new User("李四"));
System.out.println(user2); // 李四
// 参数是一个供给型函数接口,如果容器数据为null,它会返回供给型接口的返回值
User user3 = optional.orElseGet(()->new User("王五"));
System.out.println(user3); // 王五
// 参数是一个供给型函数式接口,如果数据为null,它会抛出一个供给型函数接口抛出的异常
User user4 = optional.orElseThrow(() -> new RuntimeException("数据为空")); // 这里会抛出异常
System.out.println(user4);
}