欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

java 8 基本特性

程序员文章站 2022-06-24 20:33:57
...

java8使用了一段时间了,但是有些项目组编码不允许使用java8新特性,认为会影响代码的可读性和可维护性,但是在java8标配化的当下,实在不能理解。啥时候程序猿是个可以偷懒的物种了?下面整理了java8的一些常用新特性,刚接触和使用java8的同学们可以看看,不求精深,上手还是很容易的哈。

接口的变化

在jdk8之前,interface之中可以定义变量和方法,变量必须是public、static、final的,方法必须是public、abstract的。并且这些修饰符都是默认的,可以不书写,直接生效的。
但在java8中,对接口做出了一定的变化。

接口支持静态方法

从java8开始接口里可以有静态方式,用static修饰,但是接口里的静态方法的修饰符只能是public,且默认是public。

接口支持默认方法

java8里,除了可以在接口里写静态方法,还可以写非静态方法,但是必须用default修饰,且只能是public,默认也是public。
默认方法和静态方法的区别是:接口方法可以通过接口名直接调用;而默认方法必须要实例化后才能调用。(这意味着内存空间不同。)
接口的默认方法可以被继承。
被继承(或实现)的两个接口如果有相同的默认方法,子接口(或实现类)需要重写该方法;
例如:

interface A {
     default void test() {
           System.out.println("接口A的默认方法");
     }
}
interface B {
     default void test() {
           System.out.println("接口B的默认方法");
     }
}
interface C extends A,B {
     @Override
     default void test() {
           A.super.test();
     }
}

函数式接口

定义

当接口里只有一个抽象方法的时候,就是函数式接口,可以使用注解(@FunctionalInterface)强制限定接口是函数式接口,即只能有一个抽象方法。

lambda表达式

​Lambda是一个匿名函数,我们可以把Lambda表达式理解为是一段可以传递的代码(将代码像数据一样进行传递),其可以代替实现接口中的抽象方法时的书写匿名内部类的繁琐代码。

lambda特性

lambda表达式允许把函数作为一个方法的参数(函数作为参数传递到方法中);
函数式接口
当接口里只有一个抽象方法的时候,就是函数式接口,可以使用注解(@FunctionalInterface)强制限定接口是函数式接口,即只能有一个抽象方法。
lambda表达式只能针对函数式接口编写;

lambda表达式语法

java8中引入了一个新的操作符 ->,该操作符称为箭头操作符或Lambda操作符,该箭头符号将整个Lambda表达式拆分成两部分:
左侧:Lambda表达式的参数列表,即接口中对应抽象方法的参数列表。
右侧:Lambda表达式中所需要执行的功能,即Lambda表达式体。即需要实现的抽象方法的功能体。

// 形参列表中,类型可选,编译器可以类型推断
// ()参数圆括号可选;有且只有1个参数时,可以无需圆括号。
//{}方法体大括号可选;方法体只有一句代码时,可以省略;但是注意省略后不允许存在return,如果方法定义了返回类型,这句代码就是返回的表达式。[形参列表,可选类型声明]-> {
    //实现的抽象方法代码块;
}
//eg:
Collections.sort(list,(x,y) -> {
    // 代码块
});

lambda表达式变量作用域

Lambda表达式只能引用外部被final修饰的局部变量,换句话说Lambda表达式中我们不能修改定义在外的局部变量。

java内置的四大核心函数式接口

/*
	实际上,这四中类型可以涵盖三种函数:无返回值有入参、无入参有返回函数、有入参有返回函数;
	断言接口可以作为最常见的判断;
*/
Consumer<T>  消费型接口 消费对象
    void accept(T t);
Supplier<T>  供给型接口 生成对象
    T get();
Function<R,T>  函数型接口 指定特定功能
    R apply(T t);
Predicate<T>  断言型接口 进行条件判断
    boolean test(T t); 

方法与构造器引用

函数式接口与lambda表达式结合使用,可以有效精简代码。对于不会被多处重复使用的代码块儿提供了一种灵活的方式,避免书写繁多的类和格式代码。如果有lambda表达式的内容体需要多个地方使用,可以在一个地方定义方法体,然后通过方法(构造器)引用实现复用。

/**
 * 三种语法格式:
 * 对象::实例方法名
 * 类::静态方法名
 * 类::实例方法名
 *
 * 注意:
 *  1. Lambda体中调用方法的参数列表与返回值类型,要与函数式接口中抽象方法的函数列表和返回值类型保存一致
 *  2.若Lambda参数列表中的第一个参数是实例方法的调用者,而第二个参数是实例方法的参数时,可以使用ClassName::method
 *
 *  二、构造器引用
 *  格式:
 *      ClassName::new
 *  注意:需要调用的构造器方法与函数式接口中抽象方法的参数列表保持一致
 *
 *  三、数组引用
 *  Type::new;
 *	进一步练习
 */

Stream

流是数据渠道,用于操作数据源,所生成一种新的元素序列。集合讲的是数据,流讲的是计算,是操作。
Stream是java8中处理集合的关键抽象概念,类似于SQL执行的数据库查询,也可以用来并行执行操作,其提供了一种高效且易于使用的处理数据方式。
注意点:

  • Stream自身不会存储元素;
  • Stream不会改变源数据对象,只会产生一个持有结果的新Stream;
  • Stream操作是延迟执行的,这意味着他们会等到需要结果的时候才执行。
  • Stream操作包括3部分:1,创建流;2,中间操作;3,终止操作。

创建流

创建流主要分为如下几种方式:

List<String> list = new ArrayList<>();
list.add("aa");
list.add("bb");
list.add("cc");
// stream、parallelStream;
Stream<String> cxStream = list.stream();
Stream<String> bxStream = list.parallelStream();
// Arrays.stream()
String[] strArray = {"aa","bb","cc"};
Stream<String> stream1 = Arrays.stream(strArray);
// Stream.of
Stream<String> stream2 = Stream.of("aa","bb","cc");
// Stream<T> iterate(final T seed, final UnaryOperator<T> f) 创建无限流
Stream<Long> stream3 = Stream.iterate(0L, x -> x + 1);
// Stream<T> Stream.generate(final Supplier<T> s) 创建无限流
// 使用无限流要注意短路,不然会无休止的执行下去。

Stream API 中间操作

Stream API中间操作对流进行操作处理,她并不立即执行,只有终止操作执行时,所有中间操作和终止操作一起执行,此称为:惰性求值。

Stream 中间操作 API包括:

**过滤:**Stream filter(Predicate<? Super T> predicate)
通过断言函数接口作为判断过滤条件;

Stream<Long> stream5 = stream3.filter(x -> x>10 && x<20);
stream5.forEach(System.out::println);

**限定数量:**Stream limit(long maxSize)
通过指定参数值限定数量,该API直接截断流,形成短路操作;

Stream<Long> stream5 = stream3.limit(10l);
stream5.forEach(System.out::println);

跳过元素:Stream skip(long n)
通过指定参数值跳过指定个数,与limit互补;

Stream<Long> stream5 = stream3.skip(1l);
stream5.forEach(System.out::println);

去重:Stream distinct();
通过指定参数值跳过指定个数,与limit互补;

Stream<Long> stream5 = stream3.distinct();
stream5.forEach(System.out::println);

映射: Stream map(Function<? super T, ? extends R> mapper);
将T类型的流中每个处理生成R类型,形成新流;

List<String> list1 = Arrays.asList("aaa","b","ccccc","eeeee");
Stream<Long> stream5 = list1.stream().map(x -> Long.valueOf(x.length()));
stream5.forEach(System.out::println);

映射: Stream flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
将T类型的流中每个处理生成R类流,并把所有流连接起来,形成新流;

List<String> list1 = Arrays.asList("aaa","b","ccccc","eeeee");
Stream<Character> stream5 = list1.stream().flatMap(x -> {
    List<Character> charList = Arrays.asList();
    for (Character theChar : x.toCharArray()) {
        charList.add(theChar);
    }
    return charList.stream();
});
stream5.forEach(System.out::println);

排序:Stream sorted();
自动排序,对应类型需要有实现compareTo方法;

List<String> list1 = Arrays.asList("aaa","b","ccccc","eeeee");
Stream<String> stream5 = list1.stream().sorted();
stream5.forEach(System.out::println);

排序:Stream sorted(Comparator<? super T> comparator);
按定制方法排序;

List<String> list1 = Arrays.asList("aaa","b","ccccc","eeeee");
Stream<String> stream5 = list1.stream().sorted((e1, e2) -> {
    return e1.compareTo(e2);
});
stream5.forEach(System.out::println);

Stream 终止操作 API包括:

**检查是否所有元素匹配校验:**boolean allMatch(Predicate<? super T> predicate)
通过断言函数接口作为判断条件;

List<String> list1 = Arrays.asList("aaa","b","ccccc","eeeee");
Stream<String> stream5 = list1.stream().sorted((e1, e2) -> {
    return e1.compareTo(e2);
});
boolean result = stream5.allMatch(e -> e.length() > 2);

**检查是否存在元素匹配校验:**boolean anyMatch(Predicate<? super T> predicate)
通过断言函数接口作为判断条件;

List<String> list1 = Arrays.asList("aaa","b","ccccc","eeeee");
Stream<String> stream5 = list1.stream().sorted((e1, e2) -> {
    return e1.compareTo(e2);
});
boolean result = stream5.anyMatch(e -> e.length() > 2);

**检查是否所有元素不匹配校验:**boolean noneMatch(Predicate<? super T> predicate)
通过断言函数接口作为判断条件;

List<String> list1 = Arrays.asList("aaa","b","ccccc","eeeee");
Stream<String> stream5 = list1.stream().sorted((e1, e2) -> {
    return e1.compareTo(e2);
});
boolean result = stream5.noneMatch(e -> e.length() > 2);

**查找第一个:**Optional findFirst()
查找第一个元素返回,结果封装在Optional容器中;

List<String> list1 = Arrays.asList("aaa","b","ccccc","eeeee");
Stream<String> stream5 = list1.stream().sorted((e1, e2) -> {
    return e1.compareTo(e2);
});
String result = stream5.findFirst().orElse("");

**查找任意一个:**Optional findFirst()
查找任意一个元素返回,结果封装在Optional容器中;

List<String> list1 = Arrays.asList("aaa","b","ccccc","eeeee");
Stream<String> stream5 = list1.stream().sorted((e1, e2) -> {
    return e1.compareTo(e2);
});
String result = stream5.findAny().orElse("");

**总个数:**long count()
获取流中元素总个数;

List<String> list1 = Arrays.asList("aaa","b","ccccc","eeeee");
Stream<String> stream5 = list1.stream().sorted((e1, e2) -> {
    return e1.compareTo(e2);
});
long result = stream5.count();

**最大值:**Optional max(Comparator<? super T> comparator)
获取流中最大的元素;

List<String> list1 = Arrays.asList("aaa","b","ccccc","eeeee");
Stream<String> stream5 = list1.stream().sorted((e1, e2) -> {
    return e1.compareTo(e2);
});
String result = stream5.max((e1, e2) -> e1.compareTo(e2)).orElse("");

**最小值:**Optional min(Comparator<? super T> comparator)
获取流中最小的元素;

List<String> list1 = Arrays.asList("aaa","b","ccccc","eeeee");
Stream<String> stream5 = list1.stream().sorted((e1, e2) -> {
    return e1.compareTo(e2);
});
String result = stream5.min((e1, e2) -> e1.compareTo(e2)).orElse("");

**合并流元素:**T reduce(T identity, BinaryOperator accumulator);
按照给定初始元素,把所有元素合并后返回;

List<String> list1 = Arrays.asList("aaa","b","ccccc","eeeee");
Stream<String> stream5 = list1.stream().sorted((e1, e2) -> {
    return e1.compareTo(e2);
});
String result = stream5.reduce("",(e1, e2) -> e1 + e2);

**归并1:**T reduce(T identity, BinaryOperator accumulator);
按照给定初始元素,把所有元素合并后返回;

List<String> list1 = Arrays.asList("aaa","b","ccccc","eeeee");
Stream<String> stream5 = list1.stream().sorted((e1, e2) -> {
    return e1.compareTo(e2);
});
String result = stream5.reduce("",(e1, e2) -> e1 + e2);

**归并2:**T reduce(BinaryOperator accumulator);
把所有元素合并后返回;

List<String> list1 = Arrays.asList("aaa","b","ccccc","eeeee");
Stream<String> stream5 = list1.stream().sorted((e1, e2) -> {
    return e1.compareTo(e2);
});
String result = stream5.reduce((x,y) -> x + y).orElse("");

收集:<R, A> R collect(Collector<? super T, A, R> collector);
把所有元素收集返回;

List<String> list1 = Arrays.asList("aaa","b","ccccc","eeeee");
Stream<String> stream5 = list1.stream().sorted((e1, e2) -> {
    return e1.compareTo(e2);
});
Map<String, String> result = stream5.collect(Collectors.toMap(String::trim, String::toUpperCase));

并/串行流:

并行(parallel())、串行(sequential())流是流操作的一种方式,单独提出来说这个,是因为这个强大而危险。
**强大:**如果要对流中数据进行耗时的计算的时候,开启并行流,并行流就会把整个流的处理拆分成一小块一小块的任务并行执行,执行完后再合并(底层是fork/join的实现)。强大在只需要加个parallel就可以开启并行流,并行的处理任务。
**危险:**并行流会基于ForkJoinPool线程池并行的执行任务块,这意味着任务执行没有顺序,并且会瞬间占满CPU等资源(想一想,一段时间内,你的CPU将被100%占用的。)。另外,fork-join本身也是有开销的。使用中要注意:1,是否会存在并发的问题?2,并行执行的时间段是否是允许的?

// 并行流开启只需要在流的执行中间添加.parallel()就可以了。

小结

java8除了上述新特性以外,还有一些Optional容器、重复注解等。但是最重要和需要掌握灵活使用的应该还是lambda表达式和Stream。