Java SE 8面试题与答案详解(上篇)
这是文章《Java SE 8面试问题和答案(部分一)》的第1部分(共3部分)。
在这篇文章中,我们将讨论一些与Java SE 8面试相关的重要问题和答案。我将写另一篇文章来讨论其他的Java SE 8面试问题。
Java 8 面试问题
- 我们为什么需要再次转向Java?
- Java SE 8的新特性是什么?
- Java SE 8的新特性有什么优点?
- Lambda表达式是什么?
- Lambda表达式由哪三个部分组成?Lambda表达式的类型是什么?
- 函数接口是什么?SAM接口是什么?
- 我们可以定义自己的函数接口吗?@FunctionalInterface是什么?定义函数接口的规则是什么?
- 定义函数接口是否需要使用@FunctionalInterface注解?@FunctionalInterface注解的作用是什么?为什么我们需要Java中的函数接口?
- 什么时候使用Java 8的流API?为什么我们需要在项目中使用Java 8的流API?
- 解释集合API和流API之间的区别?
- Java SE 8中的Spliterator是什么?Iterator和Spliterator在Java SE 8中有什么区别?
- Java 8中Optional是什么?Optional的用途是什么?Java 8 Optional的优点是什么?
- 什么是类型推断?类型推断在旧版本(如Java 7及更早版本)中是否可用,还是仅在Java SE 8中可用?
Java 8面试问题和答案
在这个部分,我们将从之前的部分中选择每个问题,并通过详细的描述来回答。如果您需要更多的信息和示例,请查阅《JournalDEV》上提供的之前的Java SE 8帖子。
我们为什么需要再次转换为Java呢?
Oracle公司在Java SE 8中引入了许多新概念,旨在带来以下的好处:
- 高效利用当前多核CPU
最近,我们可以观察到硬件方面的巨大变化。如今,所有系统都使用多核CPU(2核、4核、8核、16核等)来部署和运行它们的应用程序。我们需要在Java中使用新的编程结构来高效利用这些多核处理器,以开发高并发和高可扩展性的应用程序。 - 利用函数式编程特性
Oracle Corporation在Java SE 8中引入了许多函数式编程(FP)概念,以利用函数式编程的优势。
Java SE 8的新特性是什么?
- Lambda表达式(Lambda Expressions)
- 函数式接口(Functional Interfaces)
- 流API(Stream API)
- 日期和时间API(Date and Time API)
- 接口默认方法和静态方法(Interface Default Methods and Static Methods)
- 分割迭代器(Spliterator)
- 方法和构造器引用(Method and Constructor References)
- 集合API增强(Collections API Enhancements)
- 并发工具增强(Concurrency Utils Enhancements)
- Fork/Join框架增强(Fork/Join Framework Enhancements)
- 内部迭代(Internal Iteration)
- 并行数组和并行集合操作(Parallel Array and Parallel Collection Operations)
- Optional类
- 类型注解和可重复注解(Type Annotations and Repeatable Annotations)
- 方法参数反射(Method Parameter Reflection)
- Base64编码和解码(Base64 Encoding and Decoding)
- IO和NIO2增强(IO and NIO2 Enhancements)
- Nashorn JavaScript引擎
- javac增强
- JVM变更
- Java 8精简配置文件:compact1、compact2、compact3
- JDBC 4.2
- JAXP 1.6
- Java DB 10.10
- 网络(Networking)
- 安全变更(Security Changes)
Java SE 8新功能的好处是什么?
从Java SE 8新特性中,我们可以获得以下好处:
- 更简洁和可读的代码
- 更可重用的代码
- 更可测试和可维护的代码
- 高并发和高可扩展性的代码
- 编写并行代码的能力
- 编写类似数据库操作的能力
- 性能更好的应用程序
- 更高生产力的代码
Lambda表达式是什么?
Lambda表达式是一种匿名函数,它接受一组输入参数并返回结果。Lambda表达式是一段没有任何名称的代码块,可以有或没有参数,可以有或没有结果。这段代码在需要时执行。
Lambda表达式有哪三个部分?Lambda表达式的类型是什么?
一个Lambda表达式包含3个部分。
- 参数列表(Parameter List)
Lambda表达式可以包含零个、一个或多个参数。这是可选的。 - Lambda箭头运算符(Lambda Arrow Operator)
“->” 被称为Lambda箭头运算符。它分隔参数列表和函数体。 - Lambda表达式体(Lambda Expression Body)
它包含Lambda表达式的执行语句和逻辑。
“Journal Dev” 的类型是 java.lang.String。”true” 的类型是 Boolean。同样地,一个 Lambda 表达式的类型是函数式接口。例如:下述 Lambda 表达式的类型是什么?
() -> System.out.println("Hello World");
这个Lambda表达式没有参数,并且不返回任何结果。因此它的类型是”java.lang.Runnable”函数式接口。
什么是函数式接口?什么是SAM(单一抽象方法)接口?
一个功能接口是一个只包含一个抽象方法的接口。功能接口也被称为SAM接口,因为它只包含一个抽象方法。SAM接口代表单一抽象方法接口。Java SE 8 API定义了许多功能接口。
我们是否可以定义自己的函数式接口?@FunctionalInterface是什么?定义函数式接口的规则是什么?
是的,我们可以定义自己的函数式接口。@FunctionalInterface是Java SE 8中引入的一个注解,用于指示接口是函数式接口。定义函数式接口的规则包括:
- 接口只能有一个抽象方法
- 可以有多个默认方法和静态方法
- 可以重写Object类中的方法
这是文章《Java SE 8面试问题和答案(部分一)》的第2部分(共3部分)。
是的,我们可以定义自己的函数式接口。我们使用Java SE 8的@FunctionalInterface注解来标记一个接口作为函数式接口。我们需要遵循以下规则来定义一个函数式接口:
- 定义一个只有一个抽象方法的接口。
- 我们不能定义多个抽象方法。
- 在接口定义中使用@FunctionalInterface注解。
- 我们可以定义任意数量的其他方法,如默认方法、静态方法。
- 如果我们将java.lang.Object类的方法重写为抽象方法,这不计为抽象方法。
在Java中,是否需要使用@FunctionalInterface注解来定义函数式接口?@FunctionalInterface注解的作用是什么?为什么我们需要函数式接口?
在Java中不是强制使用@FunctionalInterface注解来定义函数式接口。如果不想使用,我们可以省略这个注解。然而,如果在函数式接口定义中使用了它,Java编译器会强制在该接口中只使用一个抽象方法。为什么我们需要函数式接口?在Java SE 8中,Lambda表达式的类型是函数式接口。无论我们在哪里使用Lambda表达式,就意味着我们正在使用函数式接口。
我们什么时候开始使用Java 8 Stream API?为什么我们需要在我们的项目中使用Java 8 Stream API?
当我们的Java项目需要执行以下操作时,最好使用Java 8 Stream API来获得许多好处。
- 当我们想要执行类似数据库的操作时。例如,我们想要执行分组操作、排序操作等。
- 当我们想要延迟执行操作时。
- 当我们想要编写函数式风格的编程时。
- 当我们想要执行并行操作时。
- 当我们想要使用内部迭代时。
- 当我们想要执行管道操作时。
- 当我们想要获得更好的性能时。
解释 Collection API 和 Stream API 之间的区别是什么?
序号 | Collection API | Stream API |
---|---|---|
1. | 自Java 1.2版本起可用 | 在Java SE 8中引入 |
2. | 用于存储数据(一组对象)。 | 用于计算数据(对一组对象进行计算)。 |
3. | 我们可以使用Spliterator和Iterator来迭代元素。我们可以使用forEach为此流中的每个元素执行一个操作。 | 我们不能使用Spliterator或Iterator来迭代元素。 |
4. | 用于存储无限数量的元素。 | Stream API用于处理集合中的元素。 |
5. | 通常,它使用外部迭代概念来迭代元素,如Iterator。 | Stream API使用内部迭代来迭代元素,使用forEach方法。 |
6. | 集合对象是急切构造的。 | 流对象是延迟构造的。 |
7. | 我们只有在集合对象完全计算后才能向其添加元素。 | 我们可以在没有任何预先计算的情况下向流对象添加元素。这意味着流对象是按需计算的。 |
8. | 我们可以任意次数地迭代和消耗集合对象中的元素。 | 我们只能迭代和消耗流对象中的元素一次。 |
在Java SE 8中,Spliterator是什么?迭代器和Spliterator在Java SE 8中的区别是什么?
Spliterator代表可分割迭代器。这是由Oracle Corporation作为Java SE 8的一部分新引入的。与Iterator和ListIterator一样,它也是Iterator接口中的一种。
序号 | Spliterator | Iterator |
---|---|---|
1. | 在Java SE 8中引入。 | 自Java 1.2版本起可用。 |
2. | 可分割迭代器 | 不可分割迭代器 |
3. | 用于Stream API。 | 用于Collection API。 |
4. | 它使用内部迭代概念来迭代流。 | 它使用外部迭代概念来迭代集合。 |
5. | 我们可以使用Spliterator以并行和顺序方式迭代流。 | 我们只能使用Iterator以顺序方式迭代集合。 |
6. | 我们可以通过在流对象上调用spliterator()方法获取Spliterator。 | 我们可以通过在集合对象上调用iterator()方法获取Iterator。 |
7. | 重要方法:tryAdvance() | 重要方法:next(), hasNext() |
在Java 8中,Optional是什么?Optional的用途是什么?Java 8 Optional的优势是什么?
Optional(可选):Optional是Java SE 8中引入的一个最终类,它定义在java.util包中。它用于表示可选存在或不存在的值。它可以包含一个值或者零个值。如果它包含一个值,我们可以获取它。否则,我们获取到的是空值。它是一个有界的集合,即它最多只能包含一个元素。它是”null”值的一个替代品。Optional的主要优势是:
- 用于避免空值检查。
- 用于避免”NullPointerException”。
什么是类型推断?类型推断在旧版本的Java 7及之前版本中可用吗?还是仅在Java SE 8中可用?
这是文章《Java SE 8面试问题和答案(部分一)》的第3部分(共3部分)。
类型推断是指在编译时由编译器确定类型。这在Java SE 8中并不是一个新功能,在Java 7中以及Java 7之前也可用。在Java 7之前,让我们来探索Java数组。定义一个包含如下所示值的字符串数组:
String str[] = { "Java 7", "Java 8", "Java 9" };
在这里,我们在右侧分配了一些字符串值,但没有定义其类型。Java编译器会自动推断其类型并创建一个字符串数组。Java 7:Oracle公司在Java SE 7中引入了”钻石操作符”新功能,以避免在泛型中进行不必要的类型定义。
Map<String,List<Customer>> customerInfoByCity = new HashMap<>();
在这里,我们没有在右侧定义类型信息,只是定义了Java SE 7的钻石运算符。在Java SE 8中,Oracle公司在类型推断的概念方面进行了许多改进。我们使用这个概念来定义Lambda表达式、函数、方法引用等。
ToIntBiFunction<Integer,Integer> add = (a,b) -> a + b;
这里,Java编译器在左侧观察到类型定义,并将Lambda表达式参数a和b的类型确定为整数。这就是关于Java 8面试问题的全部内容。我在这篇文章中讨论了一些Java SE 8面试问题,并将在我的下篇文章中继续讨论一些Java SE 8面试问题。如果您喜欢我的文章或有任何问题/建议,请给我留言。