干货 | Java8新特性-总结版

点击上方“中兴开发者社区”,关注我们

每天读一篇一线开发者原创好文

作者简介:

作者史培培,主要关注Java以及云计算方向。插科打诨于前端,后端,产品工程师之间;在现实中分层抽象,在Bug的坟头上蹦迪;常于Github海岸边拾捡贝壳,沾沾自喜;用Java可倚天屠龙,用Shell则庖丁解牛;常修程序员之道,常读Man文档。

作者的公众号“码上论剑”分享Java相关技术干货及实践,关注“码上论剑”,可获取更多精彩内容~



Jdk目前已经发展到Java 9了,历史上有两个版本变化比较大,一个是Java 5,另一个就是Java 8

本Java8新特性系列将着重分析理解Java8的新特性,以及其是怎么为我们开发提升效率的。

Java8变化最大的几个特性分别是:

  • Interface

  • Lambda

  • 引用

  • Stream

  • Optional

Interface

在Java8版本以前,Interface接口中所有的方法都是抽象方法常量,那么在Java8中,Interface有什么新特性呢?

静态成员

在Java8以前,我们要定义一些常量,一般会写一个类,类中都是final static的一些变量,如下:

  
  
  1. public class Constants {

  2.    public static final int MAX_SERVICE_TIME = 100;

  3. }

  4. public class Hello {

  5.    public static void main(String[] args) {

  6.        System.out.println(Constants.MAX_SERVICE_TIME);

  7.    }

  8. }

在Java8中Interface支持静态成员,成员默认是public final static的,可以在类外直接调用。

  
  
  1. public interface MyInterface {

  2.    int MAX_SERVICE_TIME = 100;

  3. }

  4. public class Hello {

  5.    public static void main(String[] args) {

  6.        System.out.println(MyInterface.MAX_SERVICE_TIME);

  7.    }

  8. }

default函数

在Java8以前,Interface中的函数是不能实现的,如下:

  
  
  1. public interface MyInterface {

  2.    int MAX_SERVICE_TIME = 100;

  3.    void test();

  4. }

在Java8中,Interface中支持函数有实现,只要在函数前加上default关键字即可,如下:

  
  
  1. public interface MyInterface {

  2.    int MAX_SERVICE_TIME = 100;

  3.    void test();

  4.    default void doSomething() {

  5.        System.out.println("do something");

  6.    }

  7. }

default函数,实现类可以不实现这个方法,如果不想子类去实现的一些方法,可以写成default函数。

在Java8之前,如果我们想实现这样的功能,也是有办法的,那就是先定义Interface,然后定义Abstract Class实现Interface,然后再定义Class继承Abstract Class,这样Class就不用实现Interface中的全部方法。

static函数

在Java8中允许Interface定义static方法,这允许API设计者在接口中定义像getInstance一样的静态工具方法,这样就能够使得API简洁而精练,如下:

  
  
  1. public interface MyInterface {

  2.    default void doSomething() {

  3.        System.out.println("do something");

  4.    }

  5. }

  6. public class MyClass implements MyInterface {

  7. }

  8. public interface MyClassFactory {

  9.    public static MyClass getInstance() {

  10.        return new MyClass();

  11.    }

  12. }

@FunctionalInterface注解

  • 什么是函数式接口

函数式接口其实本质上还是一个接口,但是它是一种特殊的接口:SAM类型的接口(Single Abstract Method)。定义了这种类型的接口,使得以其为参数的方法,可以在调用时,使用一个Lambda表达式作为参数。

  
  
  1. @FunctionalInterface

  2. public interface MyInterface {

  3.    void test();

  4. }

@FunctionalInterface注解能帮我们检测Interface是否是函数式接口,但是这个注解是非必须的,不加也不会报错

  
  
  1. @FunctionalInterface

  2. public interface MyInterface {//报错

  3.    void test();

  4.    void doSomething();

  5. }

Interface中的非default/static方法都是abstract的,所以上面的接口不是函数式接口,加上@FunctionalInterface的话,就会提示我们这不是一个函数式接口。

  • 函数式接口的作用?

函数式接口,可以在调用时,使用一个lambda表达式作为参数。

Lamdba表达式

定义

Lambda表达式基于函数式接口实现,故可以认为Lambda表达式是对函数式接口的匿名内部类的一种简写形式。

格式

Lambda表达式的具体形式为:()->{}

箭头表达式->将Lambda表达式分为了左右两部分,左侧为参数列表,右侧为具体实现,即Lambda体。

具体有以下以及情形:

1. 无参数无返回值
  
  
  1. Runnable runnable = () -> {

  2.    System.out.println("run");

  3. };

2. 有一个参数无返回值
  
  
  1. public interface MyInterface {

  2.    void test(int x);

  3. }

  4. MyInterface i = (x) -> System.out.println(x);

3. 只要一个参数,小括号可以不写
  
  
  1. MyInterface i = x -> System.out.println(x);

4. 有多个参数有返回值,并且Lambda体有多条语句
  
  
  1. Comparator<Integer> comparator = (x, y) -> {

  2.    System.out.println("Comparator");

  3.    return Integer.compare(x, y);

  4. };

5. Lambda体中只有一条语句,return和{}可以省略
  
  
  1. Comparator<Integer> comparator = (x, y) -> Integer.compare(x, y);

6. Lambda 表达式的参数列表的数据类型可以省略不写,因为JVM编译器通过上下文推断出,数据类型,即“类型推断”
  
  
  1. Comparator<Integer> comparator = (Integer x, Intergery) -> Integer.compare(x, y);

  2. Comparator<Integer> comparator = (x, y) -> Integer.compare(x, y);

总结:

  • 参数类型可忽略,若写所有参数都要写,若不写,可以类型推断

  • 参数有且仅有一个时()可以省略

  • Lambda体只有一条语句,return{}都可忽略

Java8内置四大核心函数式接口(java.util.function.*包)

  •  Consumer

     消费型接口

  
  
  1. public static void main(String[] args) {

  2.    String str = "str";

  3.    consumer(str, s -> System.out.println(s));

  4. }

  5. public static void consumer(String str, Consumer<String> function) {

  6.    function.accept(str);

  7. }

  • Supplier

    供给型接口
  
  
  1. public static void main(String[] args) {

  2.    supplier(() -> "str");

  3. }

  4. public static String supplier(Supplier<String> function) {

  5.    return function.get();

  6. }

  • Function

     函数型接口
  
  
  1. public static void main(String[] args) {

  2.    String str = "str";

  3.    function(str, s -> s);

  4. }

  5. public static String function(String str, Function<String, String> function) {

  6.    return function.apply(str);

  7. }

  • Predicate

     断定型接口
  
  
  1. public static void main(String[] args) {

  2.    String str = "str";

  3.    predicate(str, s -> s.isEmpty());

  4. }

  5. public static boolean predicate(String str, Predicate<String> function) {

  6.    return function.test(str);

  7. }

Lambda表达式就到这里了,一开始用起来会不习惯,用多了上手起来就熟练了,而且越用越信手拈来。

引用


一、方法引用

定义

若Lambda体中的功能,已经有方法提供实现,可以使用方法引用,可以将方法引用理解为Lambda 表达式的另外一种表现形式。

格式

方法引用的具体形式为:

1. 对象的引用 :: 实例方法名
  
  
  1. public class People implements Serializable {

  2.    private static final long serialVersionUID = -2052988928272007869L;

  3.    private String id;

  4.    public String getId() {

  5.        return id;

  6.    }

  7.    public void setId(String id) {

  8.        this.id = id;

  9.    }

  10.    public static void main(String[] args) {

  11.        People p = new People();

  12.        // Supplier<String> supplier = () -> p.getId();

  13.        Supplier<String> supplier = p::getId;

  14.        // Consumer<String> consumer = id -> p.setId(id);

  15.        Consumer<String> consumer = p::setId;

  16.        ...

  17.    }

  18. }

2. 类名 :: 静态方法名
  
  
  1. public static void main(String[] args) {

  2.    // Comparator<Integer> comparator = (x, y) -> Integer.compare(x, y);

  3.    Comparator<Integer> comparator = Integer::compare;

  4.    ...

  5. }

当需要引用方法的第一个参数是调用对象,并且第二个参数是需要引用方法的第二个参数 (或无参数) 时:ClassName::methodName

3. 类名 :: 实例方法名
  
  
  1. public static void main(String[] args) {

  2.    // Predicate<String> predicate = x -> x.isEmpty();

  3.    Predicate<String> predicate = String::isEmpty;

  4.    ...

  5. }

总结:

  • 方法引用所引用的方法的参数列表与返回值类型,需要与函数式接口中抽象方法的参数列表和返回值类型保持一致(就是函数签名和返回值一致)

  • 若Lambda的参数列表的第一个参数,是实例方法的调用者,第二个参数(或无参)是实例方法的参数时,格式:ClassName::MethodName


二、构造器引用

定义

构造器的参数列表,需要与函数式接口中参数列表保持一致 (就是函数签名一致)

格式

类名 :: new

  
  
  1. public class People implements Serializable {

  2.    private static final long serialVersionUID = -2052988928272007869L;

  3.    private String id;

  4.    public String getId() {

  5.        return id;

  6.    }

  7.    public void setId(String id) {

  8.        this.id = id;

  9.    }

  10.    public static void main(String[] args) {

  11.        // Supplier<People> supplier = () -> new People();

  12.        Supplier<People> supplier = People::new;

  13.        ...

  14.    }

  15. }

三、数组引用

格式

类型[] :: new

  
  
  1. public static void main(String[] args) {

  2.    Function<Integer, People[]> function = People[]::new;

  3.    People[] peoples = function.apply(10);

  4. }

Stream

Stream是什么?

在Java8源代码中,是这么定义Stream的:

A sequence of elements supporting sequential and parallel aggregate operations.

简单翻译就是流是支持顺序和并行的汇聚操作的一组元素。

从这个定义上来说,Stream可以说是一个高级版本的Iterator,Iterator只能一个一个遍历元素从而对元素进行操作,但是Stream可以执行非常复杂的查找、过滤和映射数据等操作,并且中间操作可以一直迭代。

Collections是存储元素,Stream是计算。

Stream可以理解为一个管道(Pipeline),数据从管道的一边进入,经过中间各种处理,然后从管道的另一边出来新的数据。

几个注意点:

  • Stream自己不会存储元素。

  • Stream不会改变原对象。相反,他们会返回一个持有结果的新Stream。

  • Stream操作是延迟执行。这意味着他们会等到需要结果的时候才执行。

Stream的pipeline

  • 创建Stream

  • 中间操作:一个中间操作链,对数据源数据进行处理,但是是延迟执行的

  • 终止操作:执行中间操作链,并产生结果,正如上面注意点3


创建Stream

1、java.util.Collection内置了获取流的方法,分别为串行流与并行流
  
  
  1. default Stream<E> stream() {

  2.    return StreamSupport.stream(spliterator(), false);

  3. }

  4. default Stream<E> parallelStream() {

  5.    return StreamSupport.stream(spliterator(), true);

  6. }

2、java.util.Arrays内置了获取流的方法
  
  
  1. public static <T> Stream<T> stream(T[] array) {

  2.    return stream(array, 0, array.length);

  3. }

3、java.util.stream.Stream内置了创建流的方法,分别为通过对象创建流和通过函数创建流
  
  
  1. public static<T> Stream<T> of(T t) {

  2.    return StreamSupport.stream(new Streams.StreamBuilderImpl<>(t), false);

  3. }

  4. public static<T> Stream<T> of(T... values) {

  5.    return Arrays.stream(values);

  6. }

  7. public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f) {

  8.    Objects.requireNonNull(f);

  9.    final Iterator<T> iterator = new Iterator<T>() {

  10.        @SuppressWarnings("unchecked")

  11.        T t = (T) Streams.NONE;

  12.        @Override

  13.        public boolean hasNext() {

  14.            return true;

  15.        }

  16.        @Override

  17.        public T next() {

  18.            return t = (t == Streams.NONE) ? seed : f.apply(t);

  19.        }

  20.    };

  21.    return StreamSupport.stream(Spliterators.spliteratorUnknownSize(

  22.                iterator,

  23.                Spliterator.ORDERED | Spliterator.IMMUTABLE), false);

  24. }

  25. public static<T> Stream<T> generate(Supplier<T> s) {

  26.    Objects.requireNonNull(s);

  27.    return StreamSupport.stream(

  28.                new StreamSpliterators.InfiniteSupplyingSpliterator.OfRef<>(Long.MAX_VALUE, s), false);

  29. }

中间操作(java.util.stream.Stream)

1、截断与切片
  • filter:过滤

  
  
  1. Stream<T> filter(Predicate<? super T> predicate);

  • distinct:去除重复元素(通过equals和hashCode)

  
  
  1. Stream<T> distinct();

  • limit:限制数量

  
  
  1. Stream<T> limit(long maxSize);

  • skip:跳过

  
  
  1. Stream<T> skip(long n);

是不是有点类似SQL语句呢?

2、映射
  • map

  
  
  1. <R> Stream<R> map(Function<? super T, ? extends R> mapper);

  • mapToInt

  • mapToLong

  • mapToDouble

  • flatMap

  
  
  1. <R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);

  • flatMapToInt

  • flatMapToLong

  • flatMapToDouble

3、排序
  • sorted

  
  
  1. Stream<T> sorted();

  2. Stream<T> sorted(Comparator<? super T> comparator);

4、包装
  • peek

  
  
  1. Stream<T> peek(Consumer<? super T> action);

终止操作

查找与匹配
  • allMatch:检查是否匹配所有元素

  
  
  1. boolean allMatch(Predicate<? super T> predicate);

  • anyMatch:检查是否至少匹配一个元素

  
  
  1. boolean anyMatch(Predicate<? super T> predicate);

  • noneMatch:检查是否没有匹配所有元素

  
  
  1. boolean noneMatch(Predicate<? super T> predicate);

  • findFirst:返回第一个元素

  
  
  1. Optional<T> findFirst();

  • findAny:返回当前流中的任意元素

  
  
  1. Optional<T> findAny();

  • count:返回流中元素总数

  
  
  1. long count();

  • max:返回流中最大值

  
  
  1. Optional<T> max(Comparator<? super T> comparator);

  • min:返回流中最小值

  
  
  1. Optional<T> min(Comparator<? super T> comparator);

  • forEach:内部迭代

  
  
  1. void forEach(Consumer<? super T> action);

规约
  • reduce

  
  
  1. T reduce(T identity, BinaryOperator<T> accumulator);

  2. Optional<T> reduce(BinaryOperator<T> accumulator);

  3. <U> U reduce(U identity,

  4.                 BiFunction<U, ? super T, U> accumulator,

  5.                 BinaryOperator<U> combiner);

收集
  • collect

  
  
  1. <R, A> R collect(Collector<? super T, A, R> collector);

  2. <R> R collect(Supplier<R> supplier,

  3.                  BiConsumer<R, ? super T> accumulator,

  4.                  BiConsumer<R, R> combiner);

  • Collectors静态方法

  
  
  1. List<T> toList()

  2. Set<T> toSet()

  3. Collection<T> toCollection

  4. Long counting

  5. Integer summingInt

  6. Double averagingInt

  7. IntSummaryStatistics summarizingInt

  8. String joining

  9. Optional<T> maxBy

  10. Optional<T> minBy

  11. ...

Stream是不是很方便呢?

Optional

背景

只要是Java程序员,都应该遇到过空指针异常:NullPointerException简称NPE
在Java8之前,我们都要判断下对象是否为null,或者用Google提供的Guava的Optional
在Java8中,提供了Optional

使用

  • init

  • empty 构造一个空对象

  • of 不能传null

  • ofNullable 可以为null

  
  
  1. public static<T> Optional<T> empty() {

  2.    @SuppressWarnings("unchecked")

  3.    Optional<T> t = (Optional<T>) EMPTY;

  4.    return t;

  5. }

  6. public static <T> Optional<T> of(T value) {

  7.    return new Optional<>(value);

  8. }

  9. public static <T> Optional<T> ofNullable(T value) {

  10.    return value == null ? empty() : of(value);

  11. }

  • get

  • 如果为null,会抛异常,用isPresent来判断

  
  
  1. public T get() {

  2.    if (value == null) {

  3.        throw new NoSuchElementException("No value present");

  4.    }

  5.    return value;

  6. }

  • isPresent

  • 判断元素是否为null

  
  
  1. public boolean isPresent() {

  2.    return value != null;

  3. }

  • ifPresent

  • 判断不为null时执行操作,如optional.ifPresent(System.out::println)

  
  
  1. public void ifPresent(Consumer<? super T> consumer) {

  2.    if (value != null)

  3.        consumer.accept(value);

  4. }

  • filter

  
  
  1. public Optional<T> filter(Predicate<? super T> predicate) {

  2.        Objects.requireNonNull(predicate);

  3.    if (!isPresent())

  4.        return this;

  5.    else

  6.        return predicate.test(value) ? this : empty();

  7. }

  • map

  
  
  1. public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {

  2.        Objects.requireNonNull(mapper);

  3.    if (!isPresent())

  4.        return empty();

  5.    else {

  6.        return Optional.ofNullable(mapper.apply(value));

  7.    }

  8. }

  • flatMap

  
  
  1. public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {

  2.        Objects.requireNonNull(mapper);

  3.    if (!isPresent())

  4.        return empty();

  5.    else {

  6.        return Objects.requireNonNull(mapper.apply(value));

  7.    }

  8. }

  • orElse

  • 如果为null返回某个默认值,否则返回具体值

  
  
  1. public T orElse(T other) {

  2.    return value != null ? value : other;

  3. }

  • orElseGet

  
  
  1. public T orElseGet(Supplier<? extends T> other) {

  2.    return value != null ? value : other.get();

  3. }

  • orElseThrow

  
  
  1. public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {

  2.    if (value != null) {

  3.        return value;

  4.    } else {

  5.        throw exceptionSupplier.get();

  6.    }

  7. }

总结

Optional有什么用?

  • 简化代码

  • 类似于Lambda

  • 能一定程度避免空指针

    比如:optional.ifPresent(…)等

  • 增加可读性

    比如:optional.ifPresent(…).orElse(…);是不是比if/else分支更可读呢?

Optional正确使用姿势:使用Optional时尽量不直接调用Optional.get()方法,Optional.isPresent()更应该被视为一个私有方法,应依赖于其他像Optional.orElse()、Optional.orElseGet()、Optional.map()等这样的方法。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Spark Streaming 和 Flink 都是流处理框架,但在一些方面有所不同。 1. 数据处理模型 Spark Streaming 基于批处理模型,将流数据分成一批批进行处理。而 Flink 则是基于流处理模型,可以实时处理数据流。 2. 窗口处理 Spark Streaming 的窗口处理是基于时间的,即将一段时间内的数据作为一个窗口进行处理。而 Flink 的窗口处理可以基于时间和数据量,可以更加灵活地进行窗口处理。 3. 状态管理 Spark Streaming 的状态管理是基于 RDD 的,需要将状态存储在内存中。而 Flink 的状态管理是基于内存和磁盘的,可以更加灵活地管理状态。 4. 容错性 Flink 的容错性比 Spark Streaming 更加强大,可以在节点故障时快速恢复,而 Spark Streaming 则需要重新计算整个批次的数据。 总的来说,Flink 在流处理方面更加强大和灵活,而 Spark Streaming 则更适合批处理和数据仓库等场景。 ### 回答2: Spark Streaming 和 Flink 都是流处理框架,它们都支持低延迟的流处理和高吞吐量的批处理。但是,它们在处理数据流的方式和性能上有许多不同之处。下面是它们的详细比较: 1. 处理模型 Spark Streaming 采用离散化流处理模型(DPM),将长周期的数据流划分为离散化的小批量,每个批次的数据被存储在 RDD 中进行处理,因此 Spark Streaming 具有较好的容错性和可靠性。而 Flink 采用连续流处理模型(CPM),能够在其流处理过程中进行事件时间处理和状态管理,因此 Flink 更适合处理需要精确时间戳和状态管理的应用场景。 2. 数据延迟 Spark Streaming 在处理数据流时会有一定的延迟,主要是由于对数据进行缓存和离散化处理的原因。而 Flink 的数据延迟比 Spark Streaming 更低,因为 Flink 的数据处理和计算过程是实时进行的,不需要缓存和离散化处理。 3. 机器资源和负载均衡 Spark Streaming 采用了 Spark 的机器资源调度和负载均衡机制,它们之间具有相同的容错和资源管理特性。而 Flink 使用 Yarn 和 Mesos 等分布式计算框架进行机器资源调度和负载均衡,因此 Flink 在大规模集群上的性能表现更好。 4. 数据窗口处理 Spark Streaming 提供了滑动、翻转和窗口操作等灵活的数据窗口处理功能,可以使用户更好地控制数据处理的逻辑。而 Flink 也提供了滚动窗口和滑动窗口处理功能,但相对于 Spark Streaming 更加灵活,可以在事件时间和处理时间上进行窗口处理,并且支持增量聚合和全量聚合两种方式。 5. 集成生态系统 Spark Streaming 作为 Apache Spark 的一部分,可以充分利用 Spark 的分布式计算和批处理生态系统,并且支持许多不同类型的数据源,包括Kafka、Flume和HDFS等。而 Flink 提供了完整的流处理生态系统,包括流SQL查询、流机器学习和流图形处理等功能,能够灵活地适应不同的业务场景。 总之,Spark Streaming 和 Flink 都是出色的流处理框架,在不同的场景下都能够发挥出很好的性能。选择哪种框架取决于实际需求和业务场景。 ### 回答3: Spark Streaming和Flink都是流处理引擎,但它们的设计和实现方式有所不同。在下面的对比中,我们将比较这两种流处理引擎的主要特点和差异。 1. 处理模型 Spark Streaming采用离散流处理模型,即将数据按时间间隔分割成一批一批数据进行处理。这种方式可以使得Spark Streaming具有高吞吐量和低延迟,但也会导致数据处理的粒度比较粗,难以应对大量实时事件的高吞吐量。 相比之下,Flink采用连续流处理模型,即数据的处理是连续的、实时的。与Spark Streaming不同,Flink的流处理引擎能够应对各种不同的实时场景。Flink的实时流处理能力更强,因此在某些特定的场景下,它的性能可能比Spark Streaming更好。 2. 窗口计算 Spark Streaming内置了许多的窗口计算支持,如滑动窗口、滚动窗口,但支持的窗口计算的灵活性较低,只适合于一些简单的窗口计算。而Flink的窗口计算支持非常灵活,可以支持任意窗口大小或滑动跨度。 3. 数据库支持 在处理大数据时,存储和读取数据是非常重要的。Spark Streaming通常使用HDFS作为其数据存储底层的系统。而Flink支持许多不同的数据存储形式,包括HDFS,以及许多其他开源和商业的数据存储,如Kafka、Cassandra和Elasticsearch等。 4. 处理性能 Spark Streaming的性能比Flink慢一些,尤其是在特定的情况下,例如在处理高吞吐量的数据时,在某些情况下可能受制于分批处理的架构。Flink通过其流处理模型和不同的调度器和优化器来支持更高效的实时数据处理。 5. 生态系统 Spark有着庞大的生态系统,具有成熟的ML库、图处理库、SQL框架等等。而Flink的生态系统相对较小,但它正在不断地发展壮大。 6. 规模性 Spark Streaming适用于规模小且不太复杂的项目。而Flink可扩展性更好,适用于更大、更复杂的项目。Flink也可以处理无限制的数据流。 综上所述,Spark Streaming和Flink都是流处理引擎,它们有各自的优缺点。在选择使用哪一个流处理引擎时,需要根据实际业务场景和需求进行选择。如果你的业务场景较为复杂,需要处理海量数据并且需要比较灵活的窗口计算支持,那么Flink可能是更好的选择;如果你只需要简单的流处理和一些通用的窗口计算,Spark Streaming是更为简单的选择。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值