1. Introduction
1.介绍
In this tutorial, we’re going to explore some of the JDK8-related questions that might pop up during an interview.
在本教程中,我们将探讨在面试中可能出现的一些与JDK8相关的问题。
Java 8 is a platform release packed with new language features and library classes. Most of these new features are geared towards achieving cleaner and more compact code, while some add new functionality that has never before been supported in Java.
Java 8是一个充满了新的语言特性和库类的平台版本。这些新特性中的大多数都是为了实现更干净、更紧凑的代码,而有些则增加了Java中从未支持过的新功能。
2. Java 8 General Knowledge
2.Java 8常识
Q1. What New Features Were Added in Java 8?
Q1.Java 8中增加了哪些新功能?
Java 8 ships with several new features, but the most significant are the following:
Java 8有几个新功能,但最重要的是以下几个。
- Lambda Expressions − a new language feature allowing us to treat actions as objects
- Method References − enable us to define Lambda Expressions by referring to methods directly using their names
- Optional − special wrapper class used for expressing optionality
- Functional Interface – an interface with maximum one abstract method; implementation can be provided using a Lambda Expression
- Default methods − give us the ability to add full implementations in interfaces besides abstract methods
- Nashorn, JavaScript Engine − Java-based engine for executing and evaluating JavaScript code
- Stream API − a special iterator class that allows us to process collections of objects in a functional manner
- Date API − an improved, immutable JodaTime-inspired Date API
Along with these new features, lots of feature enhancements are done under the hood at both the compiler and JVM level.
除了这些新功能外,在编译器和JVM层面上都做了大量的功能增强。
3. Method References
3.方法参考
Q1. What Is a Method Reference?
Q1.什么是方法参考?
A method reference is a Java 8 construct that can be used for referencing a method without invoking it. It’s used for treating methods as Lambda Expressions. They only work as syntactic sugar to reduce the verbosity of some lambdas. This way the following code:
方法引用是一个Java 8结构,可以用来引用一个方法而不调用它。它被用来把方法当作Lambda表达式。它们只是作为语法上的糖,以减少一些lambdas的冗长。这样一来,下面的代码。
(o) -> o.toString();
Can become:
可以成为。
Object::toString();
A method reference can be identified by a double colon separating a class or object name, and the name of the method. It has different variations, such as constructor reference:
方法引用可以通过一个双冒号来识别,它将类或对象的名称和方法的名称分开。它有不同的变化,如构造函数引用。
String::new;
Static method reference:
静态方法参考。
String::valueOf;
Bound instance method reference:
绑定的实例方法引用。
str::toString;
Unbound instance method reference:
非绑定的实例方法引用。
String::toString;
We can read a detailed description of method references with full examples by following this link and this one.
我们可以按照这个链接和这个来阅读关于方法引用的详细描述和完整示例。
Q2. What Is the Meaning of String::Valueof Expression?
Q2.String::Valueof Expression的含义是什么?
It’s a static method reference to the valueOf method of the String class.
它是对String类的valueOf方法的一个静态方法引用。
4. Optional
4.可选
Q1. What Is Optional? How Can It Be Used?
Q1.什么是可选的?如何使用?
Optional is a new class in Java 8 that encapsulates an optional value, i.e. a value that is either there or not. It’s a wrapper around an object, and we can think of it as a container of zero or one element.
Optional是Java 8中的一个新类,它封装了一个可选的值,也就是说,一个要么有要么没有的值。它是对一个对象的封装,我们可以把它看成是一个零或一个元素的容器。
Optional has a special Optional.empty() value instead of wrapped null. Thus it can be used instead of a nullable value to get rid of NullPointerException in many cases.
Optional有一个特殊的Optional.empty()值,而不是包裹的null。因此,在许多情况下,它可以用来代替nullable值来摆脱NullPointerException。
We can read a dedicated article about Optional here.
我们可以阅读一篇关于Optional的专门文章。
The main purpose of Optional, as designed by its creators, is to be a return type of methods that previously would return null. Such methods would require us to write boilerplate code to check the return value, and we could sometimes forget to do a defensive check. In Java 8, an Optional return type explicitly requires us to handle null or non-null wrapped values differently.
Optional的主要目的,正如其创造者所设计的那样,是为了成为以前会返回null的方法的返回类型。这样的方法需要我们编写模板代码来检查返回值,而且我们有时可能会忘记做防御性检查。在Java 8中,Optional返回类型明确要求我们以不同的方式处理空或非空包裹的值。
For instance, the Stream.min() method calculates the minimum value in a stream of values. But what if the stream is empty? If it wasn’t for Optional, the method would return null or throw an exception.
例如,Stream.min()方法计算了一个数值流中的最小值。但是如果该流是空的呢?如果不是因为Optional,该方法将返回null或抛出一个异常。
However, it returns an Optional value, which may be Optional.empty() (the second case). This allows us to easily handle such cases:
然而,它返回一个Optional值,它可能是Optional.empty()(第二种情况)。这使我们能够轻松地处理这种情况。
int min1 = Arrays.stream(new int[]{1, 2, 3, 4, 5})
.min()
.orElse(0);
assertEquals(1, min1);
int min2 = Arrays.stream(new int[]{})
.min()
.orElse(0);
assertEquals(0, min2);
It’s worth noting that Optional is not a general purpose class like Option in Scala. It’s not recommended that we use it as a field value in entity classes, which is clearly indicated by it not implementing the Serializable interface.
值得注意的是,Optional并不像Scala中的Option那样是一个通用的类。我们不建议在实体类中使用它作为字段值,这一点从它没有实现Serializable接口就可以清楚地看出。
5. Functional Interfaces
5.功能性接口
Q1. Describe Some of the Functional Interfaces in the Standard Library
Q1.描述一下标准库中的一些功能接口
There are a lot of functional interfaces in the java.util.function package. The more common ones include, but are not limited to:
在java.util.function包中有很多功能接口。比较常见的包括,但不限于。
- Function – it takes one argument and returns a result
- Consumer – it takes one argument and returns no result (represents a side effect)
- Supplier – it takes no arguments and returns a result
- Predicate – it takes one argument and returns a boolean
- BiFunction – it takes two arguments and returns a result
- BinaryOperator – it is similar to a BiFunction, taking two arguments and returning a result. The two arguments and the result are all of the same types.
- UnaryOperator – it is similar to a Function, taking a single argument and returning a result of the same type
For more on functional interfaces, see the article “Functional Interfaces in Java 8.”
关于函数式接口的更多信息,请参见文章“Java 8中的函数式接口”。
Q2. What Is a Functional Interface? What Are the Rules of Defining a Functional Interface?
Q2.什么是功能接口?定义功能接口的规则是什么?
A functional interface is an interface with one single abstract method (default methods do not count), no more, no less.
一个功能性接口是一个只有一个抽象方法的接口(default方法不算),不多也不少。
Where an instance of such an interface is required, a Lambda Expression can be used instead. More formally put: Functional interfaces provide target types for lambda expressions and method references.
如果需要这种接口的实例,可以使用Lambda表达式来代替。更正式的说法是:功能接口为Lambda表达式和方法引用提供目标类型。
The arguments and return type of such an expression directly match those of the single abstract method.
这种表达式的参数和返回类型直接与单个抽象方法的参数和返回类型相匹配。
For instance, the Runnable interface is a functional interface, so instead of:
例如,Runnable接口是一个功能接口,所以,与其说。
Thread thread = new Thread(new Runnable() {
public void run() {
System.out.println("Hello World!");
}
});
We could simply do:
我们可以简单地做。
Thread thread = new Thread(() -> System.out.println("Hello World!"));
Functional interfaces are usually annotated with the @FunctionalInterface annotation, which is informative and doesn’t affect the semantics.
功能性接口通常使用@FunctionalInterface注解,它是信息性的,不影响语义。
6. Default Method
6.默认的方法
Q1. What Is a Default Method and When Do We Use It?
Q1.什么是默认方法,我们什么时候使用它?
A default method is a method with an implementation, which can be found in an interface.
默认方法是一个有实现的方法,可以在一个接口中找到。
We can use a default method to add a new functionality to an interface, while maintaining backward compatibility with classes that are already implementing the interface:
我们可以使用默认方法来为一个接口添加新的功能,同时保持与已经实现了接口的类的向后兼容性。
public interface Vehicle {
public void move();
default void hoot() {
System.out.println("peep!");
}
}
Usually when we add a new abstract method to an interface, all implementing classes will break until they implement the new abstract method. In Java 8, this problem was solved by using the default method.
通常,当我们为一个接口添加一个新的抽象方法时,所有的实现类都会中断,直到它们实现这个新的抽象方法。在Java 8中,这个问题通过使用默认方法得到了解决。
For example, the Collection interface does not have a forEach method declaration. Thus adding such a method would simply break the whole collections API.
例如,Collection接口没有一个forEach方法声明。因此,添加这样一个方法将简单地破坏整个集合的API。
Java 8 introduced the default method so that the Collection interface can have a default implementation of the forEach method without requiring the classes implementing this interface to implement the same.
Java 8引入了默认方法,以便Collection接口可以有一个forEach方法的默认实现,而不要求实现该接口的类也要实现。
Q2. Will the Following Code Compile?
Q2.以下代码是否可以编译?
@FunctionalInterface
public interface Function2<T, U, V> {
public V apply(T t, U u);
default void count() {
// increment counter
}
}
Yes, the code will compile because it follows the functional interface specification of defining only a single abstract method. The second method, count, is a default method that does not increase the abstract method count.
是的,这段代码会被编译,因为它遵循功能接口规范,只定义了一个抽象方法。第二个方法,count,是一个默认方法,不会增加抽象方法的数量。
7. Lambda Expressions
7.Lambda表达式
Q1. What Is a Lambda Expression and What Is It Used For?
Q1.什么是Lambda表达式,它的用途是什么?
In very simple terms, a lambda expression is a function that we can reference and pass around as an object.
用非常简单的话来说,lambda表达式是一个函数,我们可以把它作为一个对象来引用和传递。
Moreover, lambda expressions introduce functional style processing in Java, and facilitate the writing of compact and easy-to-read code.
此外,lambda表达式在Java中引入了函数式处理,便于编写紧凑和易于阅读的代码。
As a result, lambda expressions are a natural replacement for anonymous classes such as method arguments. One of their main uses is to define inline implementations of functional interfaces.
因此,lambda表达式是匿名类(如方法参数)的自然替代物。它们的主要用途之一是定义功能接口的内联实现。
Q2. Explain the Syntax and Characteristics of a Lambda Expression
问题2 解释Lambda表达式的语法和特征
A lambda expression consists of two parts, the parameter part and the expressions part separated by a forward arrow:
一个lambda表达式由两部分组成,参数部分和表达式部分由一个向前的箭头分开。
params -> expressions
Any lambda expression has the following characteristics:
任何lambda表达式都有以下特点。
- Optional type declaration – when declaring the parameters on the left-hand side of the lambda, we don’t need to declare their types as the compiler can infer them from their values. So int param -> … and param ->… are all valid
- Optional parentheses – when only a single parameter is declared, we don’t need to place it in parentheses. This means param -> … and (param) -> … are all valid, but when more than one parameter is declared, parentheses are required
- Optional curly braces – when the expressions part only has a single statement, there is no need for curly braces. This means that param – > statement and param – > {statement;} are all valid, but curly braces are required when there is more than one statement
- Optional return statement – when the expression returns a value and it is wrapped inside curly braces, then we don’t need a return statement. That means (a, b) – > {return a+b;} and (a, b) – > {a+b;} are both valid
To read more about Lambda expressions, follow this link and this one.
要阅读有关Lambda表达式的更多信息,请关注这个链接和这个。
8. Nashorn Javascript
8.Nashorn Javascript
Q1. What Is Nashorn in Java8?
Q1.什么是Java8中的Nashorn?
Nashorn is the new Javascript processing engine for the Java platform that shipped with Java 8. Until JDK 7, the Java platform used Mozilla Rhino for the same purpose, as a Javascript processing engine.
Nashorn是Java平台的新的Javascript处理引擎,随Java 8一起发货。在JDK 7之前,Java平台使用Mozilla Rhino作为Javascript处理引擎,其目的也是如此。
Nashorn provides better compliance with the ECMA normalized JavaScript specification and better runtime performance than its predecessor.
与其前身相比,Nashorn更符合ECMA规范化的JavaScript规范,并提供了更好的运行时性能。
Q2. What Is JJS?
Q2.什么是江山?
In Java 8, jjs is the new executable or command line tool we use to execute Javascript code at the console.
在Java 8中,jjs是新的可执行文件或命令行工具,我们用来在控制台执行Javascript代码。
9. Streams
9.溪流
Q1. What Is a Stream? How Does It Differ From a Collection?
Q1.什么是流?它与收藏品有何不同?
In simple terms, a stream is an iterator whose role is to accept a set of actions to apply on each of the elements it contains.
简单地说,流是一个迭代器,其作用是接受一组动作,应用于它所包含的每个元素。
The stream represents a sequence of objects from a source such as a collection, which supports aggregate operations. They were designed to make collection processing simple and concise. Contrary to the collections, the logic of iteration is implemented inside the stream, so we can use methods like map and flatMap for performing a declarative processing.
流表示来自一个源(如集合)的对象序列,它支持聚合操作。它们被设计为使集合处理简单而简洁。与集合相反,迭代的逻辑在流中实现,所以我们可以使用像map和flatMap这样的方法来执行声明式处理。
Additionally, the Stream API is fluent and allows pipelining:
此外,Stream API是流畅的,并允许管道化。
int sum = Arrays.stream(new int[]{1, 2, 3})
.filter(i -> i >= 2)
.map(i -> i * 3)
.sum();
Another important distinction from collections is that streams are inherently lazily loaded and processed.
与集合的另一个重要区别是,流在本质上是懒散的加载和处理。
Q2. What Is the Difference Between Intermediate and Terminal Operations?
Q2.中级业务和终端业务之间的区别是什么?
We combine stream operations into pipelines to process streams. All operations are either intermediate or terminal.
我们将流操作组合成管道来处理流。所有的操作都是中间的或终端的。
Intermediate operations are those operations that return Stream itself, allowing for further operations on a stream.
中间操作是那些返回Stream本身的操作,允许对流进行进一步操作。
These operations are always lazy, i.e. they do not process the stream at the call site. An intermediate operation can only process data when there is a terminal operation. Some of the intermediate operations are filter, map and flatMap.
这些操作总是懒惰的,也就是说,它们不会在调用地点处理流。中间操作只有在有终端操作的情况下才能处理数据。一些中间操作是filter、map和flatMap。
In contrast, terminal operations terminate the pipeline and initiate stream processing. The stream is passed through all intermediate operations during terminal operation call. Terminal operations include forEach, reduce, Collect and sum.
与此相反,终端操作终止了管道并启动了流处理。在终端操作调用期间,流被传递到所有的中间操作中。终端操作包括forEach、reduce、Collect和sum。
To drive this point home, let’s look at an example with side effects:
为了说明这一点,让我们看一个有副作用的例子。
public static void main(String[] args) {
System.out.println("Stream without terminal operation");
Arrays.stream(new int[] { 1, 2, 3 }).map(i -> {
System.out.println("doubling " + i);
return i * 2;
});
System.out.println("Stream with terminal operation");
Arrays.stream(new int[] { 1, 2, 3 }).map(i -> {
System.out.println("doubling " + i);
return i * 2;
}).sum();
}
The output will be as follows:
输出结果将如下。
Stream without terminal operation
Stream with terminal operation
doubling 1
doubling 2
doubling 3
As we can see, the intermediate operations are only triggered when a terminal operation exists.
我们可以看到,中间操作只有在终端操作存在时才会被触发。
Q3. What Is the Difference Between Map and flatMap Stream Operation?
Q3.Map和flatMap流操作之间的区别是什么?
There is a difference in signature between map and flatMap. Generally speaking, a map operation wraps its return value inside its ordinal type, while flatMap does not.
map和flatMap在签名上有区别。一般来说,map操作将其返回值包裹在其序数类型中,而flatMap则不。
For example, in Optional, a map operation would return Optional<String> type, while flatMap would return String type.
例如,在Optional中,map操作将返回Optional<String>类型,而flatMap将返回String类型。
So after mapping, we need to unwrap (read “flatten”) the object to retrieve the value, whereas after flat mapping, there is no such need as the object is already flattened. We apply the same concept to mapping and flat mapping in Stream.
所以在映射之后,我们需要解开对象的包装(读作 “扁平化”)来检索值,而在扁平化映射之后,由于对象已经被扁平化,所以没有这种需要。我们在Stream中对映射和平面映射应用同样的概念。
Both map and flatMap are intermediate stream operations that receive a function and apply this function to all the elements of a stream.
map和flatMap都是中间流操作,它们接收一个函数并将这个函数应用于流的所有元素。
The difference is that for the map, this function returns a value, but for flatMap, this function returns a stream. The flatMap operation “flattens” the streams into one.
不同的是,对于map,这个函数返回一个值,但对于flatMap,这个函数返回一个流。flatMap操作将流 “扁平化 “为一个。
Here’s an example where we take a map of users’ names and lists of phones and “flatten” it down to a list of phones of all the users using flatMap:
这里有一个例子,我们把用户的名字和电话列表的地图,用flatMap把它 “扁平化 “成所有用户的电话列表。
Map<String, List<String>> people = new HashMap<>();
people.put("John", Arrays.asList("555-1123", "555-3389"));
people.put("Mary", Arrays.asList("555-2243", "555-5264"));
people.put("Steve", Arrays.asList("555-6654", "555-3242"));
List<String> phones = people.values().stream()
.flatMap(Collection::stream)
.collect(Collectors.toList());
Q4. What Is Stream Pipelining in Java 8?
Q4.什么是 Java 8 中的流管道?
Stream pipelining is the concept of chaining operations together. We do this by splitting the operations that can happen on a stream into two categories: intermediate operations and terminal operations.
流管道化是将操作连锁起来的概念。我们通过将流中可能发生的操作分成两类:中间操作和终端操作。
Each intermediate operation returns an instance of Stream itself when it runs. Therefore, we can set up an arbitrary number of intermediate operations to process data, forming a processing pipeline.
每个中间操作运行时都会返回Stream本身的一个实例。因此,我们可以设置任意数量的中间操作来处理数据,形成一个处理管道。
There must then be a terminal operation which returns a final value and terminates the pipeline.
然后必须有一个终端操作,返回一个最终值并终止管道。
10. Java 8 Date and Time API
10.Java 8的日期和时间API
Q1. Tell Us About the New Date and Time API in Java 8
Q1.告诉我们关于Java 8中新的日期和时间API的情况
A long-standing problem for Java developers has been the inadequate support for the date and time manipulations required by ordinary developers.
对于Java开发者来说,一个长期存在的问题是对普通开发者所需的日期和时间操作的支持不足。
The existing classes such as java.util.Date and SimpleDateFormatter aren’t thread-safe, leading to potential concurrency issues for users.
现有的类如java.util.Date和SimpleDateFormatter不是线程安全的,导致用户潜在的并发问题。
Poor API design is also a reality in the old Java Data API. Here’s just a quick example: years in java.util.Date start at 1900, months start at 1, and days start at 0, which is not very intuitive.
糟糕的API设计也是老的Java数据API的一个现实。这里只是一个简单的例子:java.util.Date中的年从1900开始,月从1开始,日从0开始,这不是很直观。
These issues and several others have led to the popularity of third-party date and time libraries, such as Joda-Time.
这些问题和其他一些问题导致了第三方日期和时间库的流行,如Joda-Time。
In order to address these problems and provide better support in JDK, a new date and time API, which is free of these problems, has been designed for Java SE 8 under the package java.time.
为了解决这些问题并在JDK中提供更好的支持,我们在java.time包下为Java SE 8设计了一个新的日期和时间API,它不存在这些问题。
11. Conclusion
11.结论
In this article, we explored several important technical interview questions with a bias on Java 8. This is by no means an exhaustive list, but it contains questions that we think are most likely to appear in each new feature of Java 8.
在这篇文章中,我们探讨了几个重要的技术面试问题,偏重于Java 8。这绝不是一个详尽的列表,但它包含了我们认为最有可能出现在Java 8的每个新功能中的问题。
Even if we’re just starting out, ignorance of Java 8 isn’t a good way to go into an interview, especially when Java appears strongly on a resume. It is therefore important that we take some time to understand the answers to these questions and possibly do more research.
即使我们刚刚起步,对Java 8一无所知也不是进入面试的好办法,尤其是当Java在简历上出现得很强烈时。因此,我们必须花些时间来了解这些问题的答案,并在可能的情况下做更多的研究。
Good luck in the interview.
祝你在面试中好运。