Рубрики
Без рубрики

Вопросы для интервью Java 8(+ Ответы)

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

Автор оригинала: baeldung.

1. введение

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

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

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

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

Вопросы для интервью с коллекциями Java

2. Общие знания Java 8

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

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

  • Лямбда − выражения – новая функция языка, позволяющая рассматривать действия как объекты
  • Ссылки на методы − позволяют нам определять лямбда-выражения, обращаясь непосредственно к методам, используя их имена
  • Необязательный − специальный класс обертки, используемый для выражения опциональности
  • Функциональный интерфейс – интерфейс с максимум одним абстрактным методом; реализация может быть обеспечена с помощью лямбда-выражения
  • Методы по умолчанию − дают нам возможность добавлять полные реализации в интерфейсы помимо абстрактных методов
  • 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;

Мы можем прочитать подробное описание ссылок на методы с полными примерами, перейдя по этой ссылке и этой .

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

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

4. Опционально

Q1. Что Является Необязательным? Как Его Можно Использовать?

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

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

Мы можем прочитать специальную статью о Необязательном здесь .

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

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

Однако он возвращает Необязательное значение, которое может быть Необязательным.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 имеется множество функциональных интерфейсов. Наиболее распространенные из них включают, но не ограничиваются:

  • Функция – принимает один аргумент и возвращает результат
  • Потребитель – он принимает один аргумент и не возвращает результата (представляет собой побочный эффект)
  • Поставщик – он не принимает аргументов и возвращает результат
  • Предикат – он принимает один аргумент и возвращает логическое значение
  • Бифункция – принимает два аргумента и возвращает результат
  • BinaryOperator – это похоже на бифункцию , берущую два аргумента и возвращающую результат. Два аргумента и результат относятся к одним и тем же типам.
  • UnaryOperator – он похож на функцию |, берущую один аргумент и возвращающую результат того же типа

Дополнительные сведения о функциональных интерфейсах см. в статье “Функциональные интерфейсы в Java 8.”

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

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

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

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

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

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

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

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

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

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
    }
}

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

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

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

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

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

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

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

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

params -> expressions

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

  • Необязательное объявление типа – при объявлении параметров в левой части лямбды нам не нужно объявлять их типы, так как компилятор может вывести их из их значений. Итак, int param -> … и param ->… все действительны
  • Необязательные скобки – когда объявлен только один параметр, нам не нужно заключать его в круглые скобки. Это означает, что param -> … и (param) -> … все допустимы, но при объявлении более одного параметра требуются круглые скобки
  • Необязательные фигурные скобки – когда часть выражений содержит только один оператор, фигурные скобки не нужны. Это означает, что param – > оператор и param – > {оператор;} все допустимы, но фигурные скобки требуются, когда существует более одного оператора
  • Необязательный оператор return – когда выражение возвращает значение и оно заключено в фигурные скобки, нам не нужен оператор return. Это означает, что (a, b) – > {return a+b;} и (a, b) – > {a+b;} оба действительны

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

8. Nashorn Javascript

Q1. Что такое Nashorn в Java 8?

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

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

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

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

9. Потоки

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

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

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

Кроме того, API Stream свободно работает и позволяет конвейеризировать:

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

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

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

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

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

Эти операции всегда ленивы, т. Е. Они не обрабатывают поток на сайте вызова. Промежуточная операция может обрабатывать данные только при наличии терминальной операции. Некоторые из промежуточных операций-это 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 type, в то время как flatMap вернет String type.

Поэтому после сопоставления нам нужно развернуть (читай “сгладить”) объект, чтобы получить значение, тогда как после плоского сопоставления такой необходимости нет, поскольку объект уже сглажен. Мы применяем ту же концепцию к отображению и плоскому отображению в потоке .

Оба 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 также является реальностью в старом API данных Java. Вот только краткий пример: годы в java.util.Дата начинается с 1900, месяцы начинаются с 1, а дни начинаются с 0, что не очень интуитивно понятно.

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

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

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

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

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

Удачи на собеседовании.