面试官问的Java8的新特性你真的了解了吗?

162 阅读8分钟

Java 8是Java自Java 5(发布于2004年)之后的最重要的版本。这个版本包含语言、编译器、库、工具和JVM等方面的十多个新特性。我们平时开发也主要使用的是这个版本进行开发,今天我们就来讲讲这些新特性都有哪些以及在平时开发是怎么样使用的。

一、Lambda表达式和函数式接口

在Java8中最经常看到和被猿人们使用的就是Lambda表达式了,也是面试过程中比较平常的回答。那它是怎么使用的呢?

最简单的Lambda表达式可由逗号分隔的参数列表、->符号和语句块组成,如果Lambda表达式需要更复杂的语句块,则可以使用花括号将该语句块括起来,类似于Java中的函数体,例如:

​Arrays.asList( "a", "b", "f" ).forEach( e -> {    System.out.print( e );    System.out.print( e );} );或者也可以引用类成员和局部变量final String separator = ",";Arrays.asList( "a", "b", "d" ).forEach(     ( String e ) -> System.out.print( e + separator ) );

Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。使用Lambda 表达式可以使代码变的更加简洁紧凑。

二、新的日期API

 Java 8通过发布新的Date-Time API (JSR 310)来进一步加强对日期与时间的处理。在旧版的 Java 中,日期时间 API 存在诸多问题,比如:

        1.非线程安全 − java.util.Date 是非线程安全的,所有的日期类都是可变的,这是Java日期类最大的问题之一。

        2.设计很差 − Java的日期/时间类的定义并不一致,在java.util和java.sql的包中都有日期类,此外用于格式化和解析的类在java.text包中定义。java.util.Date同时包含日期和时间,而java.sql.Date仅包含日期,将其纳入java.sql包并不合理。另外这两个类都有相同的名字,这本身就是一个非常糟糕的设计。

        3.时区处理麻烦 − 日期类并不提供国际化,没有时区支持,因此Java引入了java.util.Calendar和java.util.TimeZone类,但他们同样存在上述所有的问题。

   Java 8 在 java.time 包下提供了很多新的 API。以下为两个比较重要的 API:

       1.Local(本地) − 简化了日期时间的处理,没有时区的问题。

       2.Zoned(时区) − 通过制定的时区处理日期时间。

    新的java.time包涵盖了所有处理日期,时间,日期/时间,时区,时刻(instants),过程(during)与时钟(clock)的操作。

    这就是新式的处理方式

三、Java 编译器的新特性

Java 8 开始正式支持参数名称,终于不需要读 class 字节码来获取参数名称了,这对于经常使用反射的人特别有用。

在 Java8 这个特性默认是关闭的,需要开启参数才能获取参数名称:

<plugin>    
    <groupId>org.apache.maven.plugins</groupId> 
    <artifactId>maven-compiler-plugin</artifactId> 
    <version>3.1</version>  
    <configuration><compilerArgument>-parameters</compilerArgument>   
    <source>1.8</source>      
    <target>1.8</target>   
    </configuration>
</plugin>

四、Optional

 Optional类实际上是个容器:它可以保存类型T的值,或者仅仅保存null。Optional 类的引入很好的解决空指针异常。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。尽量避免在程序中直接调用Optional对象的get()和isPresent()方法,避免使用Optional类型声明实体类的属性。

    (1)Optional.of(T t) : 创建一个 Optional 实例

    (2)Optional.empty() : 创建一个空的 Optional 实例

    (3)Optional.ofNullable(T t):若 t 不为 null,创建 Optional 实例,否则创建空实例

    (4)isPresent() : 判断是否包含值 orElse(T t) : 如果调用对象包含值,返回该值,否则返回t

    (5)orElseGet(Supplier s) :如果调用对象包含值,返回该值,否则返回 s 获取的值

    (6)map(Function f): 如果有值对其处理,并返回处理后的Optional,否则返回Optional.empty()

    (7)flatMap(Function mapper):与 map 类似,要求返回值必须是Optional

    1.创建optional对象,一般用ofNullable()而不用of():

    (1)empty() :用于创建一个没有值的Optional对象:Optional emptyOpt = Optional.empty();

    (2)of() :使用一个非空的值创建Optional对象:Optional notNullOpt = Optional.of(str);

    (3)ofNullable() :接收一个可以为null的值:Optional nullableOpt = Optional.ofNullable(str);

    2.判断Null:

    (1)isPresent():如果创建的对象实例为非空值的话,isPresent()返回true,调用get()方法会返回该对象,如果没有值,调用isPresent()方法会返回false,调用get()方法抛出NullPointerException异常。

    3.获取对象:

    (1)get()

    4.使用map提取对象的值,如果我们要获取User对象中的roleId属性值,常见的方式是先判断是否为null然后直接获取,但使用Optional中提供的map()方法可以以更简单的方式实现。

    5.使用orElse方法设置默认值,Optional类还包含其他方法用于获取值,这些方法分别为:

    (1)orElse():如果有值就返回,否则返回一个给定的值作为默认值;

    (2)orElseGet():与orElse()方法作用类似,区别在于生成默认值的方式不同。该方法接受一个Supplier<? extends T>函数式接口参数,用于生成默认值;

    (3)orElseThrow():与前面介绍的get()方法类似,当值为null时调用这两个方法都会抛出NullPointerException异常,区别在于该方法可以指定抛出的异常类型。

    6.使用filter()方法过滤,filter()方法可用于判断Optional对象是否满足给定条件,一般用于条件过滤,在代码中,如果filter()方法中的Lambda表达式成立,filter()方法会返回当前Optional对象值,否则,返回一个值为空的Optional对象。

五 、stream流

    Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。

    Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。

    Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。

    这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。

    元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。

那么什么是stream?

    Stream(流)是一个来自数据源的元素队列并支持聚合操作。

    元素是特定类型的对象,形成一个队列。Java中的Stream并不会存储元素,而是按需计算。数据源 流的来源。可以是集合,数组,I/O channel, 产生器generator 等。聚合操作 类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。和以前的Collection操作不同, Stream操作还有两个基础的特征:

    1.Pipelining: 中间操作都会返回流对象本身。这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。

    2.内部迭代:以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。

在 Java 8 中, 集合接口有两个方法来生成流:

 1.stream() − 为集合创建串行流。

 2.parallelStream() − 为集合创建并行流。

那么具体体现是怎么样的呢?

List<Integer> evens = nums.stream().filter(num -> num % 2 == 0).collect(Collectors.toList());

先简单解释一下上面这行语句的含义,stream()操作将集合转换成一个流,filter()执行我们自定义的筛选处理,这里是通过lambda表达式筛选出所有偶数,最后我们通过collect()对结果进行封装处理,并通过Collectors.toList()指定其封装成为一个List集合返回。

由上面的例子可以看出,java8的流式处理极大的简化了对于集合的操作,实际上不光是集合,包括数组、文件等,只要是可以转换成流,我们都可以借助流式处理,类似于我们写SQL语句一样对其进行操作。

六、Optonal类

Optional 类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。

Optional 是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。

  1.     Optional 类的引入很好的解决空指针异常。

  2.     of(T t) : 创建一个Optional实例

  3.     empty() : 创建一个空的Optional实例

  4.     ofNullable(T t) :  若t不为null,创建Optional实例,否则创建空的Optional实例

  5.     isPresent() : 判断是否包含值

  6.     orElse(T t) : 如果调用对象包含值,返回该值,否则返回t

  7.     orElseGet(Suppplier s) : 如果调用对象包含值,返回该值,否则返回s获取的值

  8.     map(Function f) : 如果有值对其处理,并返回处理后的Optional,否则返回Optional.empty()

  9.     flatMap(Function mapper) : 与map类似,要求返回值必须是Optional

七、Annotation 注解

@interface Hints {     Hint[] value(); }@Repeatable(Hints.class)@interface Hint {undefined    String value();}

在Java 8中支持多重注解了,先看个例子来理解一下是什么意思。
首先定义一个包装类Hints注解用来放置一组具体的Hint注解:

Java 8允许我们把同一个类型的注解使用多次,只需要给该注解标注一下@Repeatable即可。

关于Java 8的新特性就写到这了,肯定还有更多的特性等待发掘。

    最后祝大家面试顺利,拿下好offer。

    如果觉得小二写得好就点个关注吧!