# 第 18 章:随堂复习与企业真题(JDK8-17 新特性)


# 一、随堂复习

# 1. JDK 新特性的概述

  • 几个重要的版本
    • jdk 5.0 /jdk 8.0 :里程碑式的版本
    • jdk9.0 开始每 6 个月发布一个新的版本
    • LTS : jdk8 、 jdk 11 、 jdk 17
  • 如何学习新特性
    • 新的语法规则 (多关注):自动装箱、自动拆箱、注解、enum、Lambda 表达式、方法引用、switch 表达式、try-catch 变化、record 等

    • 增加、过时、删除 API:StringBuilder、ArrayList、新的日期时间的 API、Optional 等

    • 底层的优化、JVM 参数的调整、GC 的变化、内存结构(永久代 ---> 元空间)

# 2. JDK8:lambda 表达式

# 2.1 什么情况下可以使用 lambda 表达式

  • 在给函数式接口提供实例时,都可以考虑使用 lambda 表达式。
  • 基本语法的使用(重要)

# 2.2 函数式接口

  • 常见的函数式接口。Comparator \ Runnable \ java.util.function 下定义的丰富的函数式接口

    消费型接口:Consumer<T>     void accept(T t)
    供给型接口:Supplier<T>     T get()
    函数型接口:Function<T,R>   R apply(T t)
    判断型接口:Predicate<T>    boolean test(T t)
    

# 2.3 方法引用、构造器引用、数组引用

  • 方法引用、构造器引用、数组引用:看做是 lambda 表达式的进一步刻画、表达。

  • (掌握)方法引用、构造器引用、数组引用的使用场景。

# 3. JDK8:Stream API 的使用

  • Stream 关注于内存中的多个数据的运算。
  • 使用步骤:
    • Stream 的实例化
    • 一系列的中间操作
    • 终止操作

# 4. JDK8 之后的新特性:语法层面

  • jShell 工具
  • try-catch 结构的变化,资源自动关闭。try(...)
  • 局部变量的类型推断:var
  • instanceof 的模式匹配
  • switch 表达式switch 的模式匹配
  • 文本块的使用:"""文本块"""
  • 新的引用数据类型:record (记录)
  • 密封类: sealed class

# 5. JDK8 之后的新特性:其它

  • Optional类 的使用
  • 其他:了解

# 二、企业真题

# 2.1 JDK8 新特性

# 1. 谈谈 java8 新特性 (京 * 旗下、时代 * 宇,信必 *、招 * 信诺,中 * 外包,金 * 软件、阿 ** 巴)

类似问题
> JDK1.8相较于JDK1.7有什么不一样?(惠*)
> JDK1.8的新特性有哪些?Stream API + Lambda表达式,还有吗?(久*国际物流)
  1. Lambda 表达式:在给函数式接口提供实例时使用,可简化函数式编程的语法形式。

  2. 方法引用:用于直接引用已有的方法、构造函数来提供更简洁的 Lambda 表达式。

  3. Stream API:用于对集合进行处理和操作的 API,可以在集合中进行过滤、排序、映射等操作。

  4. 时间日期 API( java.time包 ):引入了全新的时间日期 API,解决了旧有的日期时间类库的许多问题,如线程安全、设计缺陷等。

  5. 接口默认方法和私有方法:允许在接口中定义默认的实现方法、私有方法,避免影响原有的继承关系。

  6. 可重复注解:允许同一个注解在同一个元素上使用多次,并且不会产生冲突。

  7. 新的类型注解:允许开发者对类型进行注释,提高代码可读性和可靠性。

  8. CompletableFuture 类:用于执行异步任务,可以轻松管理并发操作。

  9. 内存结构:Java 8 引入了 元空间(Metaspace) ,代替了旧版的永久代(PermGen Space)。元空间是 JVM 中存储类元数据(Class Metadata)的区域,能够动态的调整大小,并且可以设置元空间最大值。这对于需要动态生成类的应用程序而言,更为灵活和安全。

  10. HashMap 底层结构:Java 8 的 HashMap 实现中,链表长度超过一定阈值时,链表会转换成 红黑树 。在红黑树中查询、插入、删除的时间复杂度均为 O(logn)O(log n),相较于链表更为高效。该变化使得 HashMap 对于冲突较多的场景效率更高。

此外,还有 ArrayList、ConcurrentHashMap、Collections 等相关数据结构也做出了一些优化和改进。

# 2. JDK1.8 在数据结构上发生了哪些变化?(银 * 数据)

  • 内存结构:Java 8 引入了 元空间(Metaspace) ,代替了旧版的永久代(PermGen Space)。元空间是 JVM 中存储类元数据(Class Metadata)的区域,能够动态的调整大小,并且可以设置元空间最大值。这对于需要动态生成类的应用程序而言,更为灵活和安全。

    • 对于 HotSpot,方法区在 jdk7 中是:永久代(PermGen Space)
    • 对于 HotSpot,方法区在 jdk8 中是:元空间(Metaspace)
  • HashMap 底层结构:Java 8 的 HashMap 实现中,链表长度超过一定阈值时,链表会转换成 红黑树 。在红黑树中查询、插入、删除的时间复杂度均为 O(logn)O(log n),相较于链表更为高效。该变化使得 HashMap 对于冲突较多的场景效率更高。

# 3. JDK1.8 用的是哪个垃圾回收器?(O**O)

Parallel GC --> jdk9:默认使用 G1GC --> ZGC (低延迟)

# 2.2 Lambda 表达式

# 1. Lambda 表达式有了解吗,说说如何使用的(O**O)

类似问题:
> Lambda的理解(国*)

在给函数式接口提供实例时,都可以考虑使用 lambda 表达式。

Lambda 表达式是 Java 8 中引入的一种新特性,它本质上是一个匿名函数,用于表示将一个功能传递给某个方法。Lambda 表达式可以看作是函数式编程的核心思想,它是可传递的代码块,实现了代码的重复利用和简化。

使用 Lambda 表达式的语法如下:

(parameter1, parameter2, ...) -> expression

或者

(parameter1, parameter2, ...) ->

其中,

  • 参数列表 parameter1, parameter2 指定了 Lambda 表达式中的参数。
  • 箭头符号 -> 将参数列表和 Lambda 表达式的主体分开。
  • 表达式 expression 或语句块组成了 Lambda 表达式的主体。

Lambda 表达式可以被赋值给一个变量,也可以像参数一样传递到方法中去。在 Java 中,Lambda 通常会关联函数式接口(Functional Interface),即只包含一个抽象方法的接口。例如,Runnable 接口只有一个 run () 方法,可以将一个 Lambda 表达式作为 Runnable 对象进行调用:

Runnable r = () -> System.out.println("Hello, world!");
new Thread(r).start();

除此之外,Lambda 表达式还可以与集合框架中的 Stream API 结合使用来对数据流进行过滤、映射等处理操作,从而简化操作实现。例如:

List<Integer> nums = Arrays.asList(1, 2, 3, 4, 5);
int sum = nums.stream().mapToInt(Integer::intValue).sum();
System.out.println(sum); // 输出:15

Lambda 表达式的使用,让 Java 语言有了更多的编程方式和更好的代码可读性,它是 Java 8 中最具有代表性的新特性之一。

# 2. 什么是函数式接口?有几种函数式接口(阿 ** 巴)

** 函数式接口(functional interface)** 是 Java 8 中引入的一种新类型接口,它只包含一个抽象方法,用于支持 Lambda 表达式和方法引用等函数式编程特性。当声明一个函数式接口时,可以使用 @FunctionalInterface 注解标记以便在编译时进行验证,确保该接口只有一个抽象方法。

在 Java 8 中的 java.util.function包 下,已经为常见的函数式接口提供了内置支持,比如:

称谓函数式接口参数类型说明用途
消费 型接口Consumer<T>T有参数输入 T,没有输出对类型为 T 的对象应用操作,包含方法: void accept(T t)
供给 型接口Supplier<T>无参数输入,返回一个 T 类型结果返回类型为 T 的对象,包含方法: T get()
函数 型接口Function<T, R>T有一个 T 类型的输入参数,输出 R 类型的结果对类型为 T 的对象应用操作,并返回结果。结果是 R 类型的对象。包含方法: R apply(T t)
判断 型接口Predicate<T>T输入 T,返回一个布尔值结果确定类型为 T 的对象是否满足某约束,并返回 boolean 值。包含方法: boolean test(T t)

# 2.3 Stream API

# 1. 创建 Stream 的方式(阿 ** 巴)

Stream 操作分 3 个步骤:

  • 创建 Stream 实例(3 种主要方式)

    • 通过集合 (Collection)

      Java8 中的 Collection 接口被扩展,提供了两个获取流的方法:

      • default Streamstream () : 返回一个顺序流

      • default StreamparallelStream () : 返回一个并行流

      @Test
      public void test01(){
          List<Integer> list = Arrays.asList(1,2,3,4,5);
          //JDK1.8 中,Collection 系列集合增加了方法
          Stream<Integer> stream = list.stream();
      }
    • 通过数组

      Java8 中的 Arrays.stream(T[] array) 可以获取数组流,这里的 T 既可以是引用数据类型,也可以是基本数据类型:

      • staticStreamstream (T [] array): 返回一个流
      • public static IntStream stream(int[] array)
      • public static LongStream stream(long[] array)
      • public static DoubleStream stream(double[] array)
      @Test
      public void test02(){
          String[] arr = {"hello","world"};
          Stream<String> stream = Arrays.stream(arr); 
      }
      @Test
      public void test03(){
          int[] arr = {1,2,3,4,5};
          IntStream stream = Arrays.stream(arr);
      }
    • 通过 Stream 类的静态方法 of ()

      可以调用 Stream.of(T... values) , 通过显示值创建一个流。它可以接收任意数量的参数

      • public staticStreamof (T... values) : 返回一个流
      @Test
      public void test04(){
          Stream<Integer> stream = Stream.of(1,2,3,4,5);
          stream.forEach(System.out::println);
      }
  • 一系列的中间操作

  • 终止操作

# 2. 你讲讲 stream 表达式是咋用的,干啥的?(中 * 国际,上海 ** 网络)

> Stream API 关注的是多个数据的计算(排序、查找、过滤、映射、遍历等),面向CPU的。
  集合关注的数据的存储,面向内存的。
> Stream API 之于集合,类似于SQL之于数据表的查询。

在 Java 8 中,Stream 是一种新的集合处理方式,它是用来对集合或者数组进行一系列操作的。Stream API 简化了对集合数据的处理,使代码更易读、更短。

stream 表达式通常由三个部分构成: 零个或多个中间操作 ,以及 一个终止操作 。其中,中间操作可有可无,终止操作是不可缺少的,只有在调用终止操作时才开始执行 stream 表达式中的各种操作,从而提高效率。

Stream 的使用步骤:

  • 创建 Stream 实例(3 种主要方式)
    • 通过集合 (Collection)
    • 通过数组
    • 通过 Stream 类的静态方法 of ()
  • 一系列的中间操作
  • 终止操作

stream 表达式提供了一种简洁、灵活的集合处理方式,使得代码更加清晰、易读,同时使用 stream 可以将数据处理部分代码进行函数式抽象,避免重复代码。

# 3. 集合用 Stream 流怎么实现过滤?(润 * 软件)

filter (Predicate predicate) 方法

# 4. 用 Stream 怎么选出 List 里想要的数据?(惠 *)

使用 Stream 选出 List 中我们想要的数据通常可以通过过滤( filter )操作来实现,例如:

List<String> list = Arrays.asList("apple", "banana", "orange", "grape", "peach");
List<String> result = list.stream()
                          .filter(str -> str.contains("a"))
                          .collect(Collectors.toList());
System.out.println(result);   // 输出:[apple, banana, grape, peach]

在这个例子中,我们首先通过 Arrays.asList () 方法初始化一个字符串列表,然后使用 stream () 方法将其转换为一个流对象。接着调用 filter () 方法,使用 Lambda 表达式实现一个简单的条件判断,该条件将保留包含字符 “a” 的字符串。最后使用 collect () 方法将过滤后的结果收集回 List 对象。

在 filter () 中,我们可以根据实际需求自定义过滤条件。例如,可以使用正则表达式、时间戳等其他方式进行过滤操作,只留下符合条件的元素。无论具体的过滤条件是什么,Stream 应该是处理集合或数组数据集的良好选择,并且在许多常见的情况下可以提供比传统循环更加清晰和简洁的代码实现方式。

# 2.4 其它版本新特性

# 1. 说说 JDK15、JDK16、JDK17 中的新特性都有什么?(银 * 数据)

Java SE 15

  • Sealed Classes(密封类) :通过限制子类,增强类的封装性和安全性。

  • Hidden Classes(隐式类):让开发人员在运行时动态创建类,并加强对于执行代码的保护。

  • Text Blocks(文本块) :为多行字符串提供了一种更加可读性和可写性的表示方式,优化了文本处理操作。

  • Record Classes(记录类) :声明用于存储数据并带有标识性的类。

Java SE 16

  • Records :进一步完善记录类(Record Classes),使得构造函数等细节更加灵活。

  • instanceof的模式匹配 :简化了 instanceof 操作符的使用,使得当匹配成功时可以直接将类型转换

  • Foreign Function & Memory API:为 Java 应用程序提供了与外部应用程序交互的基础设施,方便实现本地方法。

  • Vector API(预览):为处理向量形式数据提供了一个类库,从而提高处理性能。

Java SE 17

  • Sealed Classes 和 Hidden Classes 的迭代升级。

  • switch的模式匹配 (预览) :扩展了模式匹配功能,支持在 switch 语句中使用模式匹配,增强了代码的可读性和可维护性。

  • 接口中默认方法的隐式继承 :使得接口的默认方法可以隐式继承自其他接口,而不需要进行实现。

  • Enhanced Pseudo-Random Number Generators:升级并扩展了 Java 中的随机数生成器 API,支持更多的数据类型和方式。