1
1
Fork 0
java-interview/java8.md

924 lines
68 KiB
Markdown
Raw Normal View History

2017-05-07 07:05:47 +03:00
[Вопросы для собеседования на Java Junior](README.md)
#Java 8
+ [Какие нововведения, появились в Java 8 и JDK 8?](#Какие-нововведения-появились-в-java-8-и-jdk-8)
+ [Что такое _«лямбда»_? Какова структура и особенности использования лямбда-выражения?](#Что-такое-лямбда-Какова-структура-и-особенности-использования-лямбда-выражения)
+ [К каким переменным есть доступ у лямбда-выражений?](#К-каким-переменным-есть-доступ-у-лямбда-выражений)
+ [Как отсортировать список строк с помощью лямбда-выражения?](#Как-отсортировать-список-строк-с-помощью-лямбда-выражения)
+ [Что такое «ссылка на метод»?](#Что-такое-ссылка-на-метод)
+ [Какие виды ссылок на методы вы знаете?](#Какие-виды-ссылок-на-методы-вы-знаете)
+ [Объясните выражение `System.out::println`.](#Объясните-выражение-systemoutprintln)
+ [Что такое «функциональные интерфейсы»?](#Что-такое-функциональные-интерфейсы)
+ [Для чего нужны функциональные интерфейсы `Function<T,R>`, `DoubleFunction<R>`, `IntFunction<R>` и `LongFunction<R>`?](#Для-чего-нужны-функциональные-интерфейсы-functiontr-doublefunctionr-intfunctionr-и-longfunctionr)
+ [Для чего нужны функциональные интерфейсы `UnaryOperator<T>`, `DoubleUnaryOperator`, `IntUnaryOperator` и `LongUnaryOperator`?](#Для-чего-нужны-функциональные-интерфейсы-unaryoperatort-doubleunaryoperator-intunaryoperator-и-longunaryoperator)
+ [Для чего нужны функциональные интерфейсы `BinaryOperator<T>`, `DoubleBinaryOperator`, `IntBinaryOperator` и `LongBinaryOperator`?](#Для-чего-нужны-функциональные-интерфейсы-binaryoperatort-doublebinaryoperator-intbinaryoperator-и-longbinaryoperator)
+ [Для чего нужны функциональные интерфейсы `Predicate<T>`, `DoublePredicate`, `IntPredicate` и `LongPredicate`?](#Для-чего-нужны-функциональные-интерфейсы-predicatet-doublepredicate-intpredicate-и-longpredicate)
+ [Для чего нужны функциональные интерфейсы `Consumer<T>`, `DoubleConsumer`, `IntConsumer` и `LongConsumer`?](#Для-чего-нужны-функциональные-интерфейсы-consumert-doubleconsumer-intconsumer-и-longconsumer)
+ [Для чего нужны функциональные интерфейсы `Supplier<T>`, `BooleanSupplier`, `DoubleSupplier`, `IntSupplier` и `LongSupplier`?](#Для-чего-нужны-функциональные-интерфейсы-suppliert--booleansupplier-doublesupplier-intsupplier-и-longsupplier)
+ [Для чего нужен функциональный интерфейс `BiConsumer<T,U>`?](#Для-чего-нужен-функциональный-интерфейс-biconsumertu)
+ [Для чего нужен функциональный интерфейс `BiFunction<T,U,R>`?](#Для-чего-нужен-функциональный-интерфейс-bifunctiontur)
+ [Для чего нужен функциональный интерфейс `BiPredicate<T,U>`?](#Для-чего-нужен-функциональный-интерфейс-bipredicatetu)
+ [Для чего нужны функциональные интерфейсы вида `_To_Function`?](#Для-чего-нужны-функциональные-интерфейсы-вида-tofunction)
+ [Для чего нужны функциональные интерфейсы `ToDoubleBiFunction<T,U>`, `ToIntBiFunction<T,U>` и `ToLongBiFunction<T,U>`?](#Для-чего-нужны-функциональные-интерфейсы-todoublebifunctiontu-tointbifunctiontu-и-tolongbifunctiontu)
+ [Для чего нужны функциональные интерфейсы `ToDoubleFunction<T>`, `ToIntFunction<T>` и `ToLongFunction<T>`?](#Для-чего-нужны-функциональные-интерфейсы-todoublefunctiont-tointfunctiont-и-tolongfunctiont)
+ [Для чего нужны функциональные интерфейсы `ObjDoubleConsumer<T>`, `ObjIntConsumer<T>` и `ObjLongConsumer<T>`?](#Для-чего-нужны-функциональные-интерфейсы-objdoubleconsumert-objintconsumert-и-objlongconsumert)
+ [Что такое `StringJoiner`?](#Что-такое-stringjoiner)
+ [Что такое `default` методы интрефейса?](#Что-такое-default-методы-интрефейса)
+ [Как вызывать `default` метод интерфейса в реализующем этот интерфейс классе?](#Как-вызывать-default-метод-интерфейса-в-реализующем-этот-интерфейс-классе)
+ [Что такое `static` метод интерфейса?](#Что-такое-static-метод-интерфейса)
+ [Как вызывать `static` метод интерфейса?](#Как-вызывать-static-метод-интерфейса)
+ [Что такое `Optional`?](#Что-такое-optional)
+ [Что такое `Stream`?](#Что-такое-stream)
+ [Какие существуют способы создания стрима?](#Какие-существуют-способы-создания-стрима)
+ [В чем разница между `Collection` и `Stream`?](#В-чем-разница-между-collection-и-stream)
+ [Для чего нужен метод `collect()` в стримах?](#Для-чего-нужен-метод-collect-в-стримах)
+ [Для чего в стримах применяются методы `forEach()` и `forEachOrdered()`?](#Для-чего-в-стримах-применяются-методы-foreach-и-foreachordered)
+ [Для чего в стримах предназначены методы `map()` и `mapToInt()`, `mapToDouble()`, `mapToLong()`?](#Для-чего-в-стримах-предназначены-методы-map-и-maptoint-maptodouble-maptolong)
+ [Какова цель метода `filter()` в стримах?](#Какова-цель-метода-filter-в-стримах)
+ [Для чего в стримах предназначен метод `limit()`?](#Для-чего-в-стримах-предназначен-метод-limit)
+ [Для чего в стримах предназначен метод `sorted()`?](#Для-чего-в-стримах-предназначен-метод-sorted)
+ [Для чего в стримах предназначены методы `flatMap()`, `flatMapToInt()`, `flatMapToDouble()`, `flatMapToLong()`?](#Для-чего-в-стримах-предназначены-методы-flatmap-flatmaptoint-flatmaptodouble-flatmaptolong)
+ [Расскажите о параллельной обработке в Java 8.](#Расскажите-о-параллельной-обработке-в-java-8)
+ [Какие конечные методы работы со стримами вы знаете?](#Какие-конечные-методы-работы-со-стримами-вы-знаете)
+ [Какие промежуточные методы работы со стримами вы знаете?](#Какие-промежуточные-методы-работы-со-стримами-вы-знаете)
+ [Как вывести на экран 10 случайных чисел, используя `forEach()`?](#Как-вывести-на-экран-10-случайных-чисел-используя-foreach)
+ [Как можно вывести на экран уникальные квадраты чисел используя метод `map()`?](#Как-можно-вывести-на-экран-уникальные-квадраты-чисел-используя-метод-map)
+ [Как вывести на экран количество пустых строк с помощью метода `filter()`?](#Как-вывести-на-экран-количество-пустых-строк-с-помощью-метода-filter)
+ [Как вывести на экран 10 случайных чисел в порядке возрастания?](#Как-вывести-на-экран-10-случайных-чисел-в-порядке-возрастания)
+ [Как найти максимальное число в наборе?](#Как-найти-максимальное-число-в-наборе)
+ [Как найти минимальное число в наборе?](#Как-найти-минимальное-число-в-наборе)
+ [Как получить сумму всех чисел в наборе?](#Как-получить-сумму-всех-чисел-в-наборе)
+ [Как получить среднее значение всех чисел?](#Как-получить-среднее-значение-всех-чисел)
+ [Какие дополнительные методы для работы с ассоциативными массивами (maps) появились в Java 8?](#Какие-дополнительные-методы-для-работы-с-ассоциативными-массивами-maps-появились-в-java-8)
+ [Что такое `LocalDateTime`?](#Что-такое-localdatetime)
+ [Что такое `ZonedDateTime`?](#Что-такое-zoneddatetime)
+ [Как получить текущую дату с использованием Date Time API из Java 8?](#Как-получить-текущую-дату-с-использованием-date-time-api-из-java-8)
+ [Как добавить 1 неделю, 1 месяц, 1 год, 10 лет к текущей дате с использованием Date Time API?](#Как-добавить-1-неделю-1-месяц-1-год-10-лет-к-текущей-дате-с-использованием-date-time-api)
+ [Как получить следующий вторник используя Date Time API?](#Как-получить-следующий-вторник-используя-date-time-api)
+ [Как получить вторую субботу текущего месяца используя Date Time API?](#Как-получить-вторую-субботу-текущего-месяца-используя-date-time-api)
+ [Как получить текущее время с точностью до миллисекунд используя Date Time API?](#Как-получить-текущее-время-с-точностью-до-миллисекунд-используя-date-time-api)
+ [Как получить текущее время по местному времени с точностью до миллисекунд используя Date Time API?](#Как-получить-текущее-время-по-местному-времени-с-точностью-до-миллисекунд-используя-date-time-api)
+ [Как определить повторяемую аннотацию?](#Как-определить-повторяемую-аннотацию)
+ [Что такое `Nashorn`?](#Что-такое-nashorn)
+ [Что такое `jjs`?](#Что-такое-jjs)
+ [Какой класс появился в Java 8 для кодирования/декодирования данных?](#Какой-класс-появился-в-java-8-для-кодированиядекодирования-данных)
+ [Как создать Base64 кодировщик и декодировщик?](#Как-создать-base64-кодировщик-и-декодировщик)
##Какие нововведения, появились в Java 8 и JDK 8?
+ Методы интерфейсов по умолчанию;
+ Лямбда-выражения;
+ Функциональные интерфейсы;
+ Ссылки на методы и конструкторы;
+ Повторяемые аннотации;
+ Аннотации на типы данных;
+ Рефлексия для параметров методов;
+ _Stream API_ для работы с коллекциями;
+ Параллельная сортировка массивов;
+ Новое API для работы с датами и временем;
+ Новый движок JavaScript _Nashorn_;
+ Добавлено несколько новых классов для потокобезопасной работы;
+ Добавлен новый API для `Calendar` и `Locale`;
+ Добавлена поддержка _Unicode 6.2.0_;
+ Добавлен стандартный класс для работы с _Base64_;
+ Добавлена поддержка беззнаковой арифметики;
+ Улучшена производительность конструктора `java.lang.String(byte[], *)` и метода `java.lang.String.getBytes()`;
+ Новая реализация `AccessController.doPrivileged`, позволяющая устанавливать подмножество привилегий без необходимости проверки всех остальных уровней доступа;
+ _Password-based_ алгоритмы стали более устойчивыми;
+ Добавлена поддержка _SSL/TLS Server Name Indication (NSI)_ в _JSSE Server_;
+ Улучшено хранилище ключей (KeyStore);
+ Добавлен алгоритм _SHA-224_;
+ Удален мост _JDBC - ODBC_;
+ Удален _PermGen_, изменен способ хранения мета-данных классов;
+ Возможность создания профилей для платформы Java SE, которые включают в себя не всю платформу целиком, а некоторую ее часть;
+ Инструментарий
+ Добавлена утилита `jjs` для использования _JavaScript Nashorn_;
+ Команда `java` может запускать _JavaFX_ приложения;
+ Добавлена утилита `jdeps` для анализа _.class_-файлов.
[к оглавлению](#java-8)
##Что такое _«лямбда»_? Какова структура и особенности использования лямбда-выражения?
__Лямбда__ представляет собой набор инструкций, которые можно выделить в отдельную переменную и затем многократно вызвать в различных местах программы.
Основу лямбда-выражения составляет _лямбда-оператор_, который представляет стрелку `->`. Этот оператор разделяет лямбда-выражение на две части: левая часть содержит список параметров выражения, а правая собственно представляет тело лямбда-выражения, где выполняются все действия.
Лямбда-выражение не выполняется само по себе, а образует реализацию метода, определенного в функциональном интерфейсе. При этом важно, что функциональный интерфейс должен содержать только один единственный метод без реализации.
```java
interface Operationable {
int calculate(int x, int y);
}
public static void main(String[] args) {
Operationable operation = (x, y) -> x + y;
int result = operation.calculate(10, 20);
System.out.println(result); //30
}
```
По факту лямбда-выражения являются в некотором роде сокращенной формой внутренних анонимных классов, которые ранее применялись в Java.
+ _Отложенное выполнение (deferred execution) лямбда-выражения_- определяется один раз в одном месте программы, вызываются при необходимости, любое количество раз и в произвольном месте программы.
+ араметры лямбда-выражения_ должны соответствовать по типу параметрам метода функционального интерфейса:
```java
operation = (int x, int y) -> x + y;
//При написании самого лямбда-выражения тип параметров разрешается не указывать:
(x, y) -> x + y;
//Если метод не принимает никаких параметров, то пишутся пустые скобки, например:
() -> 30 + 20;
//Если метод принимает только один параметр, то скобки можно опустить:
n -> n * n;
```
+ _Конечные лямбда-выражения_ не обязаны возвращать какое-либо значение.
```java
interface Printable {
void print(String s);
}
public static void main(String[] args) {
Printable printer = s -> System.out.println(s);
printer.print("Hello, world");
}
```java
+ _Блочные лямбда-выражения_ обрамляются фигурными скобками. В блочных лямбда-выражениях можно использовать внутренние вложенные блоки, циклы, конструкции `if`, `switch`, создавать переменные и т.д. Если блочное лямбда-выражение должно возвращать значение, то явным образом применяется оператор `return`:
```java
Operationable operation = (int x, int y) -> {
if (y == 0) {
return 0;
}
else {
return x / y;
}
};
```
+ ередача лямбда-выражения в качестве параметра метода_:
```java
interface Condition {
boolean isAppropriate(int n);
}
private static int sum(int[] numbers, Condition condition) {
int result = 0;
for (int i : numbers) {
if (condition.isAppropriate(i)) {
result += i;
}
}
return result;
}
public static void main(String[] args) {
System.out.println(sum(new int[] {0, 1, 0, 3, 0, 5, 0, 7, 0, 9}, (n) -> n != 0));
}
```
[к оглавлению](#java-8)
##К каким переменным есть доступ у лямбда-выражений?
Доступ к переменным внешней области действия из лямбда-выражения очень схож к доступу из анонимных объектов. Можно ссылаться на:
+ неизменяемые (_effectively final_ - не обязательно помеченные как `final`) локальные переменные;
+ поля класса;
+ статические переменные.
К методам по умолчанию реализуемого функционального интерфейса обращаться внутри лямбда-выражения запрещено.
[к оглавлению](#java-8)
##Как отсортировать список строк с помощью лямбда-выражения?
```java
public static List<String> sort(List<String> list){
Collections.sort(list, (a, b) -> a.compareTo(b));
return list;
}
```
[к оглавлению](#java-8)
##Что такое «ссылка на метод»?
Если существующий в классе метод уже делает все, что необходимо, то можно воспользоваться механизмом __method reference (ссылка на метод)__ для непосредственной передачи этого метода. Такая ссылка передается в виде:
+ `имя_класса::имя_статическогоетода` для статического метода;
+ `объект_класса::имя_метода` для метода экземпляра;
+ азвание_класса::new` для конструктора.
Результат будет в точности таким же, как в случае определения лямбда-выражения, которое вызывает этот метод.
```java
private interface Measurable {
public int length(String string);
}
public static void main(String[] args) {
Measurable a = String::length;
System.out.println(a.length("abc"));
}
```
Ссылки на методы потенциально более эффективны, чем использование лямбда-выражений. Кроме того, они предоставляют компилятору более качественную информацию о типе и при возможности выбора между использованием ссылки на существующий метод и использованием лямбда-выражения, следует всегда предпочитать использование ссылки на метод.
[к оглавлению](#java-8)
##Какие виды ссылок на методы вы знаете?
+ на статический метод;
+ на метод экземпляра;
+ на конструктор.
[к оглавлению](#java-8)
##Объясните выражение `System.out::println`.
Заданное выражение иллюстрирует передачу ссылки на статический метод `println()` класса `System.out`.
[к оглавлению](#java-8)
##Что такое «функциональные интерфейсы»?
__Функциональный интерфейс__ - это интерфейс, который определяет только один абстрактный метод.
Чтобы точно определить интерфейс как функциональный, добавлена аннотация `@FunctionalInterface`, работающая по принципу `@Override`. Она обозначит замысел и не даст определить второй абстрактный метод в интерфейсе.
Интерфейс может включать сколько угодно `default` методов и при этом оставаться функциональным, потому что `default` методы - не абстрактные.
[к оглавлению](#java-8)
##Для чего нужны функциональные интерфейсы `Function<T,R>`, `DoubleFunction<R>`, `IntFunction<R>` и `LongFunction<R>`?
__`Function<T, R>`__ - интерфейс, с помощью которого реализуется функция, получающая на вход экземпляр класса `T` и возвращающая на выходе экземпляр класса `R`.
Методы по умолчанию могут использоваться для построения цепочек вызовов (`compose`, `andThen`).
```java
Function<String, Integer> toInteger = Integer::valueOf;
Function<String, String> backToString = toInteger.andThen(String::valueOf);
backToString.apply("123"); // "123"
```
+ `DoubleFunction<R>` - функция получающая на вход `Double` и возвращающая на выходе экземпляр класса `R`;
+ `IntFunction<R>` - функция получающая на вход `Integer` и возвращающая на выходе экземпляр класса `R`;
+ `LongFunction<R>` - функция получающая на вход `Long` и возвращающая на выходе экземпляр класса `R`.
[к оглавлению](#java-8)
##Для чего нужны функциональные интерфейсы `UnaryOperator<T>`, `DoubleUnaryOperator`, `IntUnaryOperator` и `LongUnaryOperator`?
__`UnaryOperator<T>` (унарный оператор)__ принимает в качестве параметра объект типа `T`, выполняет над ними операции и возвращает результат операций в виде объекта типа `T`:
```java
UnaryOperator<Integer> operator = x -> x * x;
System.out.println(operator.apply(5)); // 25
```
+ `DoubleUnaryOperator` - унарный оператор получающий на вход `Double`;
+ `IntUnaryOperator` - унарный оператор получающий на вход `Integer`;
+ `LongUnaryOperator` - унарный оператор получающий на вход `Long`.
[к оглавлению](#java-8)
##Для чего нужны функциональные интерфейсы `BinaryOperator<T>`, `DoubleBinaryOperator`, `IntBinaryOperator` и `LongBinaryOperator`?
__`BinaryOperator<T>` (бинарный оператор)__ - интерфейс, с помощью которого реализуется функция, получающая на вход два экземпляра класса `T` и возвращающая на выходе экземпляр класса `T`.
```java
BinaryOperator<Integer> operator = (a, b) -> a + b;
System.out.println(operator.apply(1, 2)); // 3
```
+ `DoubleBinaryOperator` - бинарный оператор получающий на вход `Double`;
+ `IntBinaryOperator` - бинарный оператор получающий на вход `Integer`;
+ `LongBinaryOperator` - бинарный оператор получающий на вход `Long`.
[к оглавлению](#java-8)
##Для чего нужны функциональные интерфейсы `Predicate<T>`, `DoublePredicate`, `IntPredicate` и `LongPredicate`?
__`Predicate<T>` (предикат)__ - интерфейс, с помощью которого реализуется функция, получающая на вход экземпляр класса `T` и возвращающая на выходе значение типа `boolean`.
Интерфейс содержит различные методы по умолчанию, позволяющие строить сложные условия (`and`, `or`, `negate`).
```java
Predicate<String> predicate = (s) -> s.length() > 0;
predicate.test("foo"); // true
predicate.negate().test("foo"); // false
```
+ `DoublePredicate` - предикат получающий на вход `Double`;
+ `IntPredicate` - предикат получающий на вход `Integer`;
+ `LongPredicate` - предикат получающий на вход `Long`.
[к оглавлению](#java-8)
##Для чего нужны функциональные интерфейсы `Consumer<T>`, `DoubleConsumer`, `IntConsumer` и `LongConsumer`?
__`Consumer<T>` (потребитель)__ - интерфейс, с помощью которого реализуется функция, которая получает на вход экземпляр класса `T`, производит с ним некоторое действие и ничего не возвращает.
```java
Consumer<String> hello = (name) -> System.out.println("Hello, " + name);
hello.accept("world");
```
+ `DoubleConsumer` - потребитель получающий на вход `Double`;
+ `IntConsumer` - потребитель получающий на вход `Integer`;
+ `LongConsumer` - потребитель получающий на вход `Long`.
[к оглавлению](#java-8)
##Для чего нужны функциональные интерфейсы `Supplier<T>`, `BooleanSupplier`, `DoubleSupplier`, `IntSupplier` и `LongSupplier`?
__`Supplier<T>` (поставщик)__ - интерфейс, с помощью которого реализуется функция, ничего не принимающая на вход, но возвращающая на выход результат класса `T`;
```java
Supplier<LocalDateTime> now = LocalDateTime::now;
now.get();
```
+ `DoubleSupplier` - поставщик возвращающий `Double`;
+ `IntSupplier` - поставщик возвращающий `Integer`;
+ `LongSupplier` - поставщик возвращающий `Long`.
[к оглавлению](#java-8)
##Для чего нужен функциональный интерфейс `BiConsumer<T,U>`?
__`BiConsumer<T,U>`__ представляет собой операцию, которая принимает два аргумента классов `T` и `U` производит с ними некоторое действие и ничего не возвращает.
[к оглавлению](#java-8)
##Для чего нужен функциональный интерфейс `BiFunction<T,U,R>`?
__`BiFunction<T,U,R>`__ представляет собой операцию, которая принимает два аргумента классов `T` и `U` и возвращающая результат класса `R`.
[к оглавлению](#java-8)
##Для чего нужен функциональный интерфейс `BiPredicate<T,U>`?
__`BiPredicate<T,U>`__ представляет собой операцию, которая принимает два аргумента классов `T` и `U` и возвращающая результат типа `boolean`.
[к оглавлению](#java-8)
##Для чего нужны функциональные интерфейсы вида `_To_Function`?
+ `DoubleToIntFunction` - операция принимающая аргумент класса `Double` и возвращающая результат типа `Integer`;
+ `DoubleToLongFunction` - операция принимающая аргумент класса `Double` и возвращающая результат типа `Long`;
+ `IntToDoubleFunction` - операция принимающая аргумент класса `Integer` и возвращающая результат типа `Double`;
+ `IntToLongFunction` - операция принимающая аргумент класса `Integer` и возвращающая результат типа `Long`;
+ `LongToDoubleFunction` - операция принимающая аргумент класса `Long` и возвращающая результат типа `Double`;
+ `LongToIntFunction` - операция принимающая аргумент класса `Long` и возвращающая результат типа `Integer`.
[к оглавлению](#java-8)
##Для чего нужны функциональные интерфейсы `ToDoubleBiFunction<T,U>`, `ToIntBiFunction<T,U>` и `ToLongBiFunction<T,U>`?
+ `ToDoubleBiFunction<T,U>` - операция принимающая два аргумента классов `T` и `U` и возвращающая результат типа `Double`;
+ `ToLongBiFunction<T,U>` - операция принимающая два аргумента классов `T` и `U` и возвращающая результат типа `Long`;
+ `ToIntBiFunction<T,U>` - операция принимающая два аргумента классов `T` и `U` и возвращающая результат типа `Integer`.
[к оглавлению](#java-8)
##Для чего нужны функциональные интерфейсы `ToDoubleFunction<T>`, `ToIntFunction<T>` и `ToLongFunction<T>`?
+ `ToDoubleFunction<T>` - операция принимающая аргумент класса `T` и возвращающая результат типа `Double`;
+ `ToLongFunction<T>` - операция принимающая аргумент класса `T` и возвращающая результат типа `Long`;
+ `ToIntFunction<T>` - операция принимающая аргумент класса `T` и возвращающая результат типа `Integer`.
[к оглавлению](#java-8)
##Для чего нужны функциональные интерфейсы `ObjDoubleConsumer<T>`, `ObjIntConsumer<T>` и `ObjLongConsumer<T>`?
+ `ObjDoubleConsumer<T>` - операция, которая принимает два аргумента классов `T` и `Double`, производит с ними некоторое действие и ничего не возвращает;
+ `ObjLongConsumer<T>` - операция, которая принимает два аргумента классов `T` и `Long`, производит с ними некоторое действие и ничего не возвращает;
+ `ObjIntConsumer<T>` - операция, которая принимает два аргумента классов `T` и `Integer`, производит с ними некоторое действие и ничего не возвращает.
[к оглавлению](#java-8)
##Что такое `StringJoiner`?
Класс `StringJoiner` используется, чтобы создать последовательность строк, разделенных разделителем с возможностью присоединить к полученной строке префикс и суффикс:
```java
StringJoiner joiner = new StringJoiner(".", "prefix-", "-suffix");
for (String s : "Hello the brave world".split(" ")) {
joiner.add(s);
}
System.out.println(joiner); //prefix-Hello.the.brave.world-suffix
```
[к оглавлению](#java-8)
##Что такое `default` методы интрефейса?
Java 8 позволяет добавлять неабстрактные реализации методов в интерфейс, используя ключевое слово `default`:
```java
interface Example {
int process(int a);
default void show() {
System.out.println("default show()");
}
}
```
+ Если класс реализует интерфейс, он может, но не обязан, реализовать методы по-умолчанию, уже реализованные в интерфейсе. Класс наследует реализацию по умолчанию.
+ Если некий класс реализует несколько интерфейсов, которые имеют одинаковый метод по умолчанию, то класс должен реализовать метод с совпадающей сигнатурой самостоятельно. Ситуация аналогична, если один интерфейс имеет метод по умолчанию, а в другом этот же метод является абстрактным - никакой реализации по умолчанию классом не наследуется.
+ Метод по умолчанию не может переопределить метод класса `java.lang.Object`.
+ Помогают реализовывать интерфейсы без страха нарушить работу других классов.
+ Позволяют избежать создания служебных классов, так как все необходимые методы могут быть представлены в самих интерфейсах.
+ Дают свободу классам выбрать метод, который нужно переопределить.
+ Одной из основных причин внедрения методов по умолчанию является возможность коллекций в Java 8 использовать лямбда-выражения.
[к оглавлению](#java-8)
##Как вызывать `default` метод интерфейса в реализующем этот интерфейс классе?
Используя ключевое слово `super` вместе с именем интерфейса:
```java
interface Paper {
default void show() {
System.out.println("default show()");
}
}
class Licence implements Paper {
public void show() {
Paper.super.show();
}
}
```
[к оглавлению](#java-8)
##Что такое `static` метод интерфейса?
Статические методы интерфейса похожи на методы по умолчанию, за исключением того, что для них отсутствует возможность переопределения в классах, реализующих интерфейс.
+ Статические методы в интерфейсе являются частью интерфейса без возможности использовать их для объектов класса реализации;
+ Методы класса `java.lang.Object` нельзя переопределить как статические;
+ Статические методы в интерфейсе используются для обеспечения вспомогательных методов, например, проверки на null, сортировки коллекций и т.д.
[к оглавлению](#java-8)
##Как вызывать `static` метод интерфейса?
Используя имя интерфейса:
```java
interface Paper {
static void show() {
System.out.println("static show()");
}
}
class Licence {
public void showPaper() {
Paper.show();
}
}
```
[к оглавлению](#java-8)
##Что такое `Optional`?
Опциональное значение `Optional` — это контейнер для объекта, который может содержать или не содержать значение `null`. Такая обёртка является удобным средством предотвращения `NullPointerException`, т.к.
имеет некоторые функции высшего порядка, избавляющие от добавления повторяющихся `if null/notNull` проверок:
```java
Optional<String> optional = Optional.of("hello");
optional.isPresent(); // true
optional.ifPresent(s -> System.out.println(s.length())); // 5
optional.get(); // "hello"
optional.orElse("ops..."); // "hello"
```
[к оглавлению](#java-8)
##Что такое `Stream`?
Интерфейс `java.util.Stream` представляет собой последовательность элементов, над которой можно производить различные операции.
Операции над стримами бывают или _промежуточными (intermediate)_ или онечными (terminal)_. Конечные операции возвращают результат определенного типа, а промежуточные операции возвращают тот же стрим. Таким образом вы можете строить цепочки из несколько операций над одним и тем же стримом.
У стрима может быть сколько угодно вызовов промежуточных операций и последним вызов конечной операции. При этом все промежуточные операции выполняются лениво и пока не будет вызвана конечная операция никаких действий на самом деле не происходит (похоже на создание объекта `Thread` или `Runnable`, без вызова `start()`).
Стримы создаются на основе источников каких-либо, например классов из `java.util.Collection`.
Ассоциативные массивы (maps), например `HashMap`, не поддерживаются.
Операции над стримами могут выполняться как последовательно, так и параллельно.
Потоки не могут быть использованы повторно. Как только была вызвана какая-нибудь конечная операция, поток закрывается.
Кроме универсальных объектных существуют особые виды стримов для работы с примитивными типами данных `int`, `long` и `double`: `IntStream`, `LongStream` и `DoubleStream`. Эти примитивные стримы работают так же, как и обычные объектные, но со следующими отличиями:
+ используют специализированные лямбда-выражения, например `IntFunction` или `IntPredicate` вместо `Function` и `Predicate`;
+ поддерживают дополнительные конечные операции `sum()`, `average()`, `mapToObj()`.
[к оглавлению](#java-8)
##Какие существуют способы создания стрима?
1. Из коллекции:
```java
Stream<String> fromCollection = Arrays.asList("x", "y", "z").stream();
```
2. Из набора значений:
```java
Stream<String> fromValues = Stream.of("x", "y", "z");
```
3. Из массива:
```java
Stream<String> fromArray = Arrays.stream(new String[]{"x", "y", "z"});
```
4. Из файла (каждая строка в файле будет отдельным элементом в стриме):
```java
Stream<String> fromFile = Files.lines(Paths.get("input.txt"));
```
5. Из строки:
```java
IntStream fromString = "0123456789".chars();
```
6. С помощью `Stream.builder()`:
```java
Stream<String> fromBuilder = Stream.builder().add("z").add("y").add("z").build();
```
7. С помощью `Stream.iterate()` (бесконечный):
```java
Stream<Integer> fromIterate = Stream.iterate(1, n -> n + 1);
```
8. С помощью `Stream.generate()` (бесконечный):
```java
Stream<String> fromGenerate = Stream.generate(() -> "0");
```
[к оглавлению](#java-8)
##В чем разница между `Collection` и `Stream`?
Коллекции позволяют работать с элементами по-отдельности, тогда как стримы так делать не позволяет, но вместо этого предоставляет возможность выполнять функции над данными как над одним целым.
[к оглавлению](#java-8)
##Для чего нужен метод `collect()` в стримах?
Метод `collect()` является конечной операцией, которая используется для представление результата в виде коллекции или какой-либо другой структуры данных.
`collect()` принимает на вход `Collector<Тип_источника, Тип_аккумулятора, Тип_результата>`, который содержит четыре этапа: _supplier_ - инициализация аккумулятора, _accumulator_ - обработка каждого элемента, _combiner_ - соединение двух аккумуляторов при параллельном выполнении, _[finisher]_ - необязательный метод последней обработки аккумулятора. В Java 8 в классе `Collectors` реализовано несколько распространённых коллекторов:
+ `toList()`, `toCollection()`, `toSet()` - представляют стрим в виде списка, коллекции или множества;
+ `toConcurrentMap()`, `toMap()` - позволяют преобразовать стрим в `Map`;
+ `averagingInt()`, `averagingDouble()`, `averagingLong()` - возвращают среднее значение;
+ `summingInt()`, `summingDouble()`, `summingLong()` - возвращает сумму;
+ `summarizingInt()`, `summarizingDouble()`, `summarizingLong()` - возвращают `SummaryStatistics` с разными агрегатными значениями;
+ `partitioningBy()` - разделяет коллекцию на две части по соответствию условию и возвращает их как `Map<Boolean, List>`;
+ `groupingBy()` - разделяет коллекцию на несколько частей и возвращает `Map<N, List<T>>`;
+ `mapping()` - дополнительные преобразования значений для сложных `Collector`-ов.
Так же существует возможность создания собственного коллектора через `Collector.of()`:
```java
Collector<String, List<String>, List<String>> toList = Collector.of(
ArrayList::new,
List::add,
(l1, l2) -> { l1.addAll(l2); return l1; }
);
```
[к оглавлению](#java-8)
##Для чего в стримах применяются методы `forEach()` и `forEachOrdered()`?
+ `forEach()` применяет функцию к каждому объекту стрима, порядок при параллельном выполнении не гарантируется;
+ `forEachOrdered()` применяет функцию к каждому объекту стрима с сохранением порядка элементов.
[к оглавлению](#java-8)
##Для чего в стримах предназначены методы `map()` и `mapToInt()`, `mapToDouble()`, `mapToLong()`?
Метод `map()` является промежуточной операцией, которая заданным образом преобразует каждый элемент стрима.
`mapToInt()`, `mapToDouble()`, `mapToLong()` - аналоги `map()`, возвращающие соответствующий числовой стрим (то есть стрим из числовых примитивов):
```java
Stream
.of("12", "22", "4", "444", "123")
.mapToInt(Integer::parseInt)
.toArray(); //[12, 22, 4, 444, 123]
```
[к оглавлению](#java-8)
##Какова цель метода `filter()` в стримах?
Метод `filter()` является промежуточной операцией принимающей предикат, который фильтрует все элементы, возвращая только те, что соответствуют условию.
[к оглавлению](#java-8)
##Для чего в стримах предназначен метод `limit()`?
Метод `limit()` является промежуточной операцией, которая позволяет ограничить выборку определенным количеством первых элементов.
[к оглавлению](#java-8)
##Для чего в стримах предназначен метод `sorted()`?
Метод `sorted()` является промежуточной операцией, которая позволяет сортировать значения либо в натуральном порядке, либо задавая `Comparator`.
Порядок элементов в исходной коллекции остается нетронутым - `sorted()` всего лишь создает его отсортированное представление.
[к оглавлению](#java-8)
##Для чего в стримах предназначены методы `flatMap()`, `flatMapToInt()`, `flatMapToDouble()`, `flatMapToLong()`?
Метод `flatMap()` похож на map, но может создавать из одного элемента несколько. Таким образом, каждый объект будет преобразован в ноль, один или несколько других объектов, поддерживаемых потоком. Наиболее очевидный способ применения этой операции — преобразование элементов контейнера при помощи функций, которые возвращают контейнеры.
```java
Stream
.of("H e l l o", "w o r l d !")
.flatMap((p) -> Arrays.stream(p.split(",")))
.toArray(String[]::new);//["H", "e", "l", "l", "o", "w", "o", "r", "l", "d", "!"]
```
`flatMapToInt()`, `flatMapToDouble()`, `flatMapToLong()` - это аналоги `flatMap()`, возвращающие соответствующий числовой стрим.
[к оглавлению](#java-8)
##Расскажите о параллельной обработке в Java 8.
Стримы могут быть последовательными и параллельными. Операции над последовательными стримами выполняются в одном потоке процессора, над параллельными — используя несколько потоков процессора. Параллельные стримы используют общий `ForkJoinPool` доступный через статический `ForkJoinPool.commonPool()` метод. При этом, если окружение не является многоядерным, то поток будет выполняться как последовательный. Фактически применение параллельных стримов сводится к тому, что данные в стримах будут разделены на части, каждая часть обрабатывается на отдельном ядре процессора, и в конце эти части соединяются, и над ними выполняются конечные операции.
Для создания параллельного потока из коллекции можно также использовать метод `parallelStream()` интерфейса `Collection`.
Чтобы сделать обычный последовательный стрим параллельным, надо вызвать у объекта `Stream` метод `parallel()`. Метод `isParallel()` позволяет узнать является ли стрим параллельным.
С помощью, методов `parallel()` и `sequential()` можно определять какие операции могут быть параллельными, а какие только последовательными. Так же из любого последовательного стрима можно сделать параллельный и наоборот:
```java
collection
.stream()
.peek(...) // операция последовательна
.parallel()
.map(...) // операция может выполняться параллельно,
.sequential()
.reduce(...) // операция снова последовательна
```
Как правило, элементы передаются в стрим в том же порядке, в котором они определены в источнике данных. При работе с параллельными стримами система сохраняет порядок следования элементов. Исключение составляет метод `forEach()`, который может выводить элементы в произвольном порядке. И чтобы сохранить порядок следования, необходимо применять метод `forEachOrdered()`.
Критерии, которые могут повлиять на производительность в параллельных стримах:
+ Размер данных - чем больше данных, тем сложнее сначала разделять данные, а потом их соединять.
+ Количество ядер процессора. Теоретически, чем больше ядер в компьютере, тем быстрее программа будет работать. Если на машине одно ядро, нет смысла применять параллельные потоки.
+ Чем проще структура данных, с которой работает поток, тем быстрее будут происходить операции. Например, данные из `ArrayList` легко использовать, так как структура данной коллекции предполагает последовательность несвязанных данных. А вот коллекция типа `LinkedList` - не лучший вариант, так как в последовательном списке все элементы связаны с предыдущими/последующими. И такие данные трудно распараллелить.
+ Над данными примитивных типов операции будут производиться быстрее, чем над объектами классов.
+ Крайне не рекомендуется использовать параллельные стримы для сколько-нибудь долгих операций (например сетевых соединений), так как все параллельные стримы работают c одним `ForkJoinPool`, то такие долгие операции могут остановить работу всех параллельных стримов в JVM из-за отсутствия доступных потоков в пуле, т.е. параллельные стримы стоит использовать лишь для коротких операций, где счет идет на миллисекунды, но не для тех где счет может идти на секунды и минуты;
+ Сохранение порядка в параллельных стримах увеличивает издержки при выполнении и если порядок не важен, то имеется возможность отключить его сохранение и тем самым увеличить производительность, использовав промежуточную операцию `unordered()`:
```java
collection.parallelStream()
.sorted()
.unordered()
.collect(Collectors.toList());
```
[к оглавлению](#java-8)
##Какие конечные методы работы со стримами вы знаете?
+ `findFirst()` возвращает первый элемент;
+ `findAny()` возвращает любой подходящий элемент;
+ `collect()` представление результатов в виде коллекций и других структур данных;
+ `count()` возвращает количество элементов;
+ `anyMatch()` возвращает `true`, если условие выполняется хотя бы для одного элемента;
+ `noneMatch()` возвращает `true`, если условие не выполняется ни для одного элемента;
+ `allMatch()` возвращает `true`, если условие выполняется для всех элементов;
+ `min()` возвращает минимальный элемент, используя в качестве условия `Comparator`;
+ `max()` возвращает максимальный элемент, используя в качестве условия `Comparator`;
+ `forEach()` применяет функцию к каждому объекту (порядок при параллельном выполнении не гарантируется);
+ `forEachOrdered()` применяет функцию к каждому объекту с сохранением порядка элементов;
+ `toArray()` возвращает массив значений;
+ `reduce()`позволяет выполнять агрегатные функции и возвращать один результат.
Для числовых стримов дополнительно доступны:
+ `sum()` возвращает сумму всех чисел;
+ `average()` возвращает среднее арифметическое всех чисел.
[к оглавлению](#java-8)
##Какие промежуточные методы работы со стримами вы знаете?
+ `filter()` отфильтровывает записи, возвращая только записи, соответствующие условию;
+ `skip()` позволяет пропустить определённое количество элементов в начале;
+ `distinct()` возвращает стрим без дубликатов (для метода `equals()`);
+ `map()` преобразует каждый элемент;
+ `peek()` возвращает тот же стрим, применяя к каждому элементу функцию;
+ `limit()` позволяет ограничить выборку определенным количеством первых элементов;
+ `sorted()` позволяет сортировать значения либо в натуральном порядке, либо задавая `Comparator`;
+ `mapToInt()`, `mapToDouble()`, `mapToLong()` - аналоги `map()` возвращающие стрим числовых примитивов;
+ `flatMap()`, `flatMapToInt()`, `flatMapToDouble()`, `flatMapToLong()` - похожи на `map()`, но могут создавать из одного элемента несколько.
Для числовых стримов дополнительно доступен метод `mapToObj()`, который преобразует числовой стрим обратно в объектный.
[к оглавлению](#java-8)
##Как вывести на экран 10 случайных чисел, используя `forEach()`?
```java
(new Random())
.ints()
.limit(10)
.forEach(System.out::println);
```
[к оглавлению](#java-8)
##Как можно вывести на экран уникальные квадраты чисел используя метод `map()`?
```java
Stream
.of(1, 2, 3, 2, 1)
.map(s -> s * s)
.distinct()
.collect(Collectors.toList())
.forEach(System.out::println);
```
[к оглавлению](#java-8)
##Как вывести на экран количество пустых строк с помощью метода `filter()`?
```java
System.out.println(
Stream
.of("Hello", "", ", ", "world", "!")
.filter(String::isEmpty)
.count());
```
[к оглавлению](#java-8)
##Как вывести на экран 10 случайных чисел в порядке возрастания?
```java
(new Random())
.ints()
.limit(10)
.sorted()
.forEach(System.out::println);
```
[к оглавлению](#java-8)
##Как найти максимальное число в наборе?
```java
Stream
.of(5, 3, 4, 55, 2)
.mapToInt(a -> a)
.max()
.getAsInt(); //55
```
[к оглавлению](#java-8)
##Как найти минимальное число в наборе?
```java
Stream
.of(5, 3, 4, 55, 2)
.mapToInt(a -> a)
.min()
.getAsInt(); //2
```
[к оглавлению](#java-8)
##Как получить сумму всех чисел в наборе?
```java
Stream
.of(5, 3, 4, 55, 2)
.mapToInt()
.sum(); //69
```
[к оглавлению](#java-8)
##Как получить среднее значение всех чисел?
```java
Stream
.of(5, 3, 4, 55, 2)
.mapToInt(a -> a)
.average()
.getAsDouble(); //13.8
```
[к оглавлению](#java-8)
##Какие дополнительные методы для работы с ассоциативными массивами (maps) появились в Java 8?
+ `putIfAbsent()` добавляет пару «ключ-значение», только если ключ отсутствовал:
`map.putIfAbsent("a", "Aa");`
+ `forEach()` принимает функцию, которая производит операцию над каждым элементом:
`map.forEach((k, v) -> System.out.println(v));`
+ `compute()` создаёт или обновляет текущее значение на полученное в результате вычисления (возможно использовать ключ и текущее значение):
`map.compute("a", (k, v) -> String.valueOf(k).concat(v)); //["a", "aAa"]`
+ `computeIfPresent()` если ключ существует, обновляет текущее значение на полученное в результате вычисления (возможно использовать ключ и текущее значение):
`map.computeIfPresent("a", (k, v) -> k.concat(v));`
+ `computeIfAbsent()` если ключ отсутствует, создаёт его со значением, которое вычисляется (возможно использовать ключ):
`map.computeIfAbsent("a", k -> "A".concat(k)); //["a","Aa"]`
+ `getOrDefault()` в случае отсутствия ключа, возвращает переданное значение по-умолчанию:
`map.getOrDefault("a", "not found");`
+ `merge()` принимает ключ, значение и функцию, которая объединяет передаваемое и текущее значения. Если под заданным ключем значение отсутствует, то записывает туда передаваемое значение.
`map.merge("a", "z", (value, newValue) -> value.concat(newValue)); //["a","Aaz"]`
[к оглавлению](#java-8)
##Что такое `LocalDateTime`?
`LocalDateTime` объединяет вместе `LocaleDate` и `LocalTime`, содержит дату и время в календарной системе ISO-8601 без привязки к часовому поясу. Время хранится с точностью до наносекунды. Содержит множество удобных методов, таких как plusMinutes, plusHours, isAfter, toSecondOfDay и т.д.
[к оглавлению](#java-8)
##Что такое `ZonedDateTime`?
`java.time.ZonedDateTime` — аналог `java.util.Calendar`, класс с самым полным объемом информации о временном контексте в календарной системе ISO-8601. Включает временную зону, поэтому все операции с временными сдвигами этот класс проводит с её учётом.
[к оглавлению](#java-8)
##Как получить текущую дату с использованием Date Time API из Java 8?
```java
LocalDate.now();
```
[к оглавлению](#java-8)
##Как добавить 1 неделю, 1 месяц, 1 год, 10 лет к текущей дате с использованием Date Time API?
```java
LocalDate.now().plusWeeks(1);
LocalDate.now().plusMonths(1);
LocalDate.now().plusYears(1);
LocalDate.now().plus(1, ChronoUnit.DECADES);
```
[к оглавлению](#java-8)
##Как получить следующий вторник используя Date Time API?
```java
LocalDate.now().with(TemporalAdjusters.next(DayOfWeek.TUESDAY));
```
[к оглавлению](#java-8)
##Как получить вторую субботу текущего месяца используя Date Time API?
```java
LocalDate
.of(LocalDate.now().getYear(), LocalDate.now().getMonth(), 1)
.with(TemporalAdjusters.nextOrSame(DayOfWeek.SATURDAY))
.with(TemporalAdjusters.next(DayOfWeek.SATURDAY));
```
[к оглавлению](#java-8)
##Как получить текущее время с точностью до миллисекунд используя Date Time API?
```java
new Date().toInstant();
```
[к оглавлению](#java-8)
##Как получить текущее время по местному времени с точностью до миллисекунд используя Date Time API?
```java
LocalDateTime.ofInstant(new Date().toInstant(), ZoneId.systemDefault());
```
[к оглавлению](#java-8)
##Как определить повторяемую аннотацию?
Чтобы определить повторяемую аннотацию, необходимо создать аннотацию-контейнер для списка повторяемых аннотаций и обозначить повторяемую мета-аннотацией `@Repeatable`:
```java
@interface Schedulers
{
Scheduler[] value();
}
@Repeatable(Schedulers.class)
@interface Scheduler
{
String birthday() default "Jan 8 1935";
}
```
[к оглавлению](#java-8)
##Что такое `Nashorn`?
__Nashorn__ - это движок JavaScript, разрабатываемый на Java компанией Oracle. Призван дать возможность встраивать код JavaScript в приложения Java. В сравнении с _Rhino_, который поддерживается Mozilla Foundation, Nashorn обеспечивает от 2 до 10 раз более высокую производительность, так как он компилирует код и передает байт-код виртуальной машине Java непосредственно в памяти. Nashorn умеет компилировать код JavaScript и генерировать классы Java, которые загружаются специальным загрузчиком. Так же возможен вызов кода Java прямо из JavaScript.
[к оглавлению](#java-8)
##Что такое `jjs`?
`jjs` это утилита командной строки, которая позволяет исполнять программы на языке JavaScript прямо в консоли.
[к оглавлению](#java-8)
##Какой класс появился в Java 8 для кодирования/декодирования данных?
`Base64` - потокобезопасный класс, который реализует кодировщик и декодировщик данных, используя схему кодирования base64 согласно _RFC 4648_ и _RFC 2045_.
Base64 содержит 6 основных методов:
`getEncoder()`/`getDecoder()` - возвращает кодировщик/декодировщик base64, соответствующий стандарту _RFC 4648_;
`getUrlEncoder()`/`getUrlDecoder()` - возвращает URL-safe кодировщик/декодировщик base64, соответствующий стандарту _RFC 4648_;
`getMimeEncoder()`/`getMimeDecoder()` - возвращает MIME кодировщик/декодировщик, соответствующий стандарту _RFC 2045_.
[к оглавлению](#java-8)
##Как создать Base64 кодировщик и декодировщик?
```java
// Encode
String b64 = Base64.getEncoder().encodeToString("input".getBytes("utf-8")); //aW5wdXQ==
// Decode
new String(Base64.getDecoder().decode("aW5wdXQ=="), "utf-8"); //input
```
[к оглавлению](#java-8)
#Источники
+ [Хабрахабр - Новое в Java 8](https://habrahabr.ru/post/216431/)
+ [Хабрахабр - Шпаргалка Java программиста 4. Java Stream API](https://habrahabr.ru/company/luxoft/blog/270383/)
+ [METANIT.COM](http://metanit.com/java/tutorial/9.1.php)
+ [javadevblog.com](http://javadevblog.com/interfejsy-v-java-8-staticheskie-metody-metody-po-umolchaniyu-funktsional-ny-e-interfejsy.html)