Java 8 Интервью Вопросы (+ Ответы)

Java 8 Интервью Вопросы (+ ответы)

1. Вступление

В этой статье мы рассмотрим некоторые вопросы, связанные с JDK8, которые могут возникнуть во время интервью.

Java 8 - это платформа, включающая новые языковые функции и библиотечные классы. Большинство из этих новых функций направлены на создание более чистого и компактного кода, а некоторые добавляют новые функции, которые никогда не поддерживались в Java.

Дальнейшее чтение:

Управление памятью в Java. Вопросы для интервью (+ ответы)

Набор популярных вопросов, связанных с управлением памятью, и, конечно, ответы.

Read more

Интервью с коллекциями Java

Набор практических вопросов, касающихся собеседований на Java

Read more

2. Общие сведения о Java 8

Q1. Какие новые функции были добавлены в Java 8?

Java 8 поставляется с несколькими новыми функциями, но наиболее важными являются следующие:

  • Lambda Expressions - новая языковая функция, позволяющая рассматривать действия как объекты

  • Method References - включить определение лямбда-выражений, обращаясь к методам напрямую, используя их имена

  • Optional - специальный класс-оболочка, используемый для выражения необязательности

  • Functional Interface - интерфейс с максимум одним абстрактным методом, реализация может быть предоставлена ​​с использованием лямбда-выражения

  • Default methods - дает нам возможность добавлять полные реализации в интерфейсы помимо абстрактных методов

  • Nashorn, JavaScript Engine - Java-движок для выполнения и оценки кода JavaScript

  • Stream API - специальный класс итератора, позволяющий функционально обрабатывать коллекции объектов

  • Date API - улучшенный неизменяемый API даты на основе JodaTime

Наряду с этими новыми функциями, множество улучшений было сделано на уровне как на уровне компилятора, так и на уровне JVM.

3. Ссылки на метод

Q1. Что такое ссылка на метод?

Ссылка на метод - это конструкция Java 8, которую можно использовать для ссылки на метод, не вызывая его. Он используется для обработки методов как лямбда-выражений. Они работают только как синтаксический сахар, чтобы уменьшить многословие некоторых лямбд. Таким образом, следующий код:

(o) -> o.toString();

может стать:

Object::toString();

Ссылка на метод может быть идентифицирована двойным двоеточием, разделяющим имя класса или объекта и имя метода. Он имеет разные варианты, такие как ссылка на конструктор:

String::new;

Ссылка на статический метод:

String::valueOf;

Ссылка на метод привязанного экземпляра:

str::toString;

Ссылка на метод несвязанного экземпляра:

String::toString;

Вы можете прочитать подробное описание ссылок на методы с полными примерами, следуяthis link иthis one.

Q2. Что означает выражение String :: Valueof?

Это статическая ссылка на методvalueOf классаString.

4. По желанию

Q1. Что такоеOptional? Как его можно использовать?

Optional - это новый класс в Java 8, который инкапсулирует необязательное значение, т.е. значение, которое либо есть, либо нет. Это обертка вокруг объекта, и вы можете думать о нем как о контейнере с нулем или одним элементом.

Optional имеет специальное значениеOptional.empty() вместо обернутогоnull. Таким образом, во многих случаях его можно использовать вместо значения, допускающего значение NULL, чтобы избавиться отNullPointerException.

Вы можете прочитать специальную статью оOptionalhere.

Основная цельOptional, разработанная его создателями, заключалась в том, чтобы быть возвращаемым типом методов, которые ранее возвращали быnull. Такие методы потребуют от вас написания стандартного кода для проверки возвращаемого значения и иногда могут забыть сделать защитную проверку. В Java 8 возвращаемый типOptional явно требует, чтобы вы по-разному обрабатывали обернутые значения null или ненулевые значения.

Например, методStream.min() вычисляет минимальное значение в потоке значений. Но что, если поток пуст? Если бы неOptional, метод вернул быnull или выдал бы исключение.

Но он возвращает значение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);

Стоит отметить, чтоOptional не является классом общего назначения, какOption в Scala. Не рекомендуется использовать в качестве значения поля в классах сущностей, на что ясно указывает то, что он не реализует интерфейсSerializable.

5. Функциональные интерфейсы

Q1. Опишите некоторые функциональные интерфейсы в стандартной библиотеке.

В пакетеjava.util.function есть множество функциональных интерфейсов, наиболее распространенные из которых включают, но не ограничиваются:

  • Function - принимает один аргумент и возвращает результат

  • Consumer - принимает один аргумент и не возвращает результата (представляет собой побочный эффект)

  • Supplier - не принимает аргумент и возвращает результат

  • Predicate - принимает один аргумент и возвращает логическое значение

  • BiFunction - принимает два аргумента и возвращает результат

  • BinaryOperator - похоже наBiFunction, принимая два аргумента и возвращая результат. Два аргумента и результат имеют одинаковые типы

  • UnaryOperator - похоже наFunction, принимая единственный аргумент и возвращая результат того же типа

Подробнее о функциональных интерфейсах читайте в статье“Functional Interfaces in Java 8”.

Q2. Что такое функциональный интерфейс? Каковы правила определения функционального интерфейса?

Функциональный интерфейс - это интерфейс, не содержащий ни больше ни меньше, но один-единственный абстрактный метод (методыdefault не учитываются).

Если требуется экземпляр такого интерфейса, вместо него можно использовать лямбда-выражение. Более формально:Functional interfaces предоставляют целевые типы для лямбда-выражений и ссылок на методы.

Аргументы и возвращаемый тип такого выражения напрямую совпадают с аргументами одного абстрактного метода.

Например, интерфейсRunnable является функциональным интерфейсом, поэтому вместо:

Thread thread = new Thread(new Runnable() {
    public void run() {
        System.out.println("Hello World!");
    }
});

Вы могли бы просто сделать:

Thread thread = new Thread(() -> System.out.println("Hello World!"));

Функциональные интерфейсы обычно аннотируются аннотацией@FunctionalInterface, которая является информативной и не влияет на семантику.

6. Метод по умолчанию

Q1. Что такое метод по умолчанию и когда мы его используем?

Метод по умолчанию - это метод с реализацией, который можно найти в интерфейсе.

Мы можем использовать метод по умолчанию, чтобы добавить новую функциональность в интерфейс, поддерживая обратную совместимость с классами, которые уже реализуют интерфейс:

public interface Vehicle {
    public void move();
    default void hoot() {
        System.out.println("peep!");
    }
}

Обычно, когда новый абстрактный метод добавляется к интерфейсу, все реализующие классы прерываются, пока не реализуют новый абстрактный метод. В Java 8 эта проблема была решена с помощью метода по умолчанию.

Например, интерфейсCollection не имеет объявления методаforEach. Таким образом, добавление такого метода просто сломало бы API целых коллекций.

Java 8 вводит метод по умолчанию, так что интерфейсCollection может иметь реализацию по умолчанию методаforEach, не требуя от классов, реализующих этот интерфейс, реализовать то же самое.

Q2. Будет ли компилироваться следующий код?

@FunctionalInterface
public interface Function2 {
    public V apply(T t, U u);

    default void count() {
        // increment counter
    }
}

Yes. Код будет скомпилирован, потому что он следует спецификации функционального интерфейса, определяющей только один абстрактный метод. Второй метод,count, является методом по умолчанию, который не увеличивает количество абстрактных методов.

7. Лямбда-выражения

Q1. Что такое лямбда-выражение и для чего оно используется

Проще говоря, лямбда-выражение - это функция, на которую можно ссылаться и передавать как объект.

Лямбда-выражения вводят функциональную обработку стилей в Java и облегчают написание компактного и удобного для чтения кода.

Из-за этого лямбда-выражения являются естественной заменой анонимных классов в качестве аргументов метода. Одним из их основных применений является определение встроенных реализаций функциональных интерфейсов.

Q2. Объясните синтаксис и характеристики лямбда-выражения

Лямбда-выражение состоит из двух частей: части параметров и части выражений, разделенных стрелкой вперед, как показано ниже:

params -> expressions

Любое лямбда-выражение имеет следующие характеристики:

  • Optional type declaration - при объявлении параметров в левой части лямбда-выражения нам не нужно объявлять их типы, поскольку компилятор может вывести их из их значений. Итак,int param → … иparam →… действительны

  • Optional parentheses - когда объявлен только один параметр, нам не нужно заключать его в круглые скобки. Это означает, чтоparam → … и(param) → … действительны. Но когда объявлено более одного параметра, требуются скобки

  • Optional curly braces - когда часть выражений содержит только один оператор, фигурные скобки не нужны. Это означает, что всеparam – > statement иparam – > \{statement;} действительны. Но фигурные скобки требуются, когда существует более одного утверждения

  • Optional return statement - когда выражение возвращает значение и заключено в фигурные скобки, оператор return нам не нужен. Это означает, что(a, b) – > \{return a+b;} и(a, b) – > \{a+b;} действительны

Чтобы узнать больше о лямбда-выражениях, следуйтеthis link иthis one.

8. Nashorn Javascript

Q1. Что такое Nashorn в Java8?

Nashorn - это новый механизм обработки Javascript для платформы Java, поставляемый с Java 8. До JDK 7 платформа Java использовала Mozilla Rhino для той же цели. в качестве механизма обработки Javascript.

Nashorn обеспечивает лучшее соответствие нормированной спецификации ECMA JavaScript и лучшую производительность во время выполнения, чем его предшественник.

Q2. Что такое JJS?

В Java 8jjs - это новый исполняемый файл или инструмент командной строки, используемый для выполнения кода Javascript на консоли.

9. Streams

Q1. Что такое поток? Чем он отличается от коллекции?

Проще говоря, поток - это итератор, роль которого состоит в том, чтобы принять набор действий для применения к каждому из элементов, которые он содержит.

The stream представляет собой последовательность объектов из источника, например коллекции, которая поддерживает агрегированные операции. Они были разработаны, чтобы сделать обработку коллекции простой и лаконичной. В отличие от коллекций, логика итерации реализуется внутри потока, поэтому мы можем использовать такие методы, какmap иflatMap, для выполнения декларативной обработки.

Другое отличие состоит в том, что APIStream плавный и позволяет конвейерную обработку:

int sum = Arrays.stream(new int[]{1, 2, 3})
  .filter(i -> i >= 2)
  .map(i -> i * 3)
  .sum();

И еще одно важное отличие от коллекций заключается в том, что потоки по своей природе лениво загружаются и обрабатываются.

Q2. В чем разница между промежуточными и терминальными операциями?

Потоковые операции объединяются в конвейеры для обработки потоков. Все операции являются промежуточными или терминальными.

Промежуточные операции - это те операции, которые возвращают самStream, позволяя выполнять дальнейшие операции с потоком.

Эти операции всегда ленивы, т.е. они не обрабатывают поток на сайте вызова, промежуточная операция может обрабатывать данные только при наличии терминальной операции. Некоторые из промежуточных операций:filter,map иflatMap.

Терминальные операции завершают конвейер и инициируют обработку потока. Поток передается через все промежуточные операции во время вызова операции терминала. Терминальные операции включаютforEach,reduce, Collect иsum.

Чтобы прояснить этот вопрос, давайте рассмотрим пример с побочными эффектами:

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();
}

Вывод будет следующим:

Stream without terminal operation
Stream with terminal operation
doubling 1
doubling 2
doubling 3

Как видите, промежуточные операции запускаются только при наличии терминальной операции.

Q3. В чем разница между потокамиMap иflatMap?

Существует разница в сигнатуре междуmap иflatMap. Вообще говоря, операцияmap обертывает возвращаемое значение внутри своего порядкового типа, аflatMap - нет.

Например, вOptional операцияmap вернет типOptional<String>, аflatMap вернет типString.

Таким образом, после сопоставления необходимо развернуть (читать «сплющить») объект для получения значения, тогда как после плоского сопоставления такой необходимости нет, поскольку объект уже сплющен. Та же концепция применяется к отображению и плоскому отображению вStream.

Иmap, иflatMap являются промежуточными операциями потока, которые получают функцию и применяют эту функцию ко всем элементам потока.

Разница в том, что дляmap эта функция возвращает значение, а дляflatMap эта функция возвращает поток. ОперацияflatMap «сглаживает» потоки в один.

Вот пример, в котором мы берем карту имен пользователей и списки телефонов и «сводим» ее к списку телефонов всех пользователей, использующихflatMap:

Map> 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 phones = people.values().stream()
  .flatMap(Collection::stream)
    .collect(Collectors.toList());

Q4. Что такое конвейерная обработка потоков в Java 8?

Потоковая конвейерная обработка - это концепция объединения операций в цепочку. Это делается путем разделения операций, которые могут выполняться в потоке, на две категории: промежуточные операции и операции терминала.

Каждая промежуточная операция возвращает экземпляр самого потока, когда она выполняется, поэтому может быть установлено произвольное количество промежуточных операций для обработки данных, образующих конвейер обработки.

Затем должна быть терминальная операция, которая возвращает окончательное значение и завершает конвейер.

10. API даты и времени Java 8

Q1. Расскажите нам о новом API даты и времени в Java 8

Давняя проблема для разработчиков Java заключалась в недостаточной поддержке манипуляций с датой и временем, которые требуются обычным разработчикам.

Существующие классы, такие какjava.util.Date иSimpleDateFormatter, не являются потокобезопасными, что может привести к потенциальным проблемам с параллелизмом для пользователей.

Плохой дизайн API также является реальностью в старом Java Data API. Вот простой пример - годы вjava.util.Date начинаются с 1900, месяцы начинаются с 1, а дни начинаются с 0, что не очень интуитивно понятно.

Эти и некоторые другие проблемы привели к популярности сторонних библиотек даты и времени, таких как Joda-Time.

Чтобы решить эти проблемы и обеспечить лучшую поддержку в JDK, для Java SE 8 был разработан новый API даты и времени, свободный от этих проблем, в пакетеjava.time.

11. Заключение

В этой статье мы рассмотрели несколько очень важных вопросов для технических собеседований с предвзятым отношением к Java 8. Это ни в коем случае не исчерпывающий список, а содержит только те вопросы, которые, по нашему мнению, наиболее вероятно появятся в каждой новой функции Java 8.

Даже если вы только начинаете, незнание Java 8 - не лучший вариант для прохождения собеседования, особенно когда Java явно упоминается в вашем резюме. Поэтому важно, чтобы вы потратили некоторое время, чтобы понять ответы на эти вопросы и, возможно, провести дополнительные исследования.

Удачи в вашем интервью.