Developer Tales or everything about everything

25Апр/141

Новое в Java 8

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

В данной статье представлен обзор нововведений Java 8 с примерами. Статья является частичным переводом замечательного труда с некоторыми дополнениями.

Обзор

Краткий список нововведений в Java 8:

  • Java
    • Ламбда-выражения;
    • Ссылки на методы;
    • Повторяемые аннотации;
    • Аннотации на типы данных;
    • Рефлексия для параметров методов;
    • Методы по-умолчанию.
  • Коллекции
    • Новый API для потоков;
    • Параллельная сортировка массивов;
    • Улучшение производительности HashMaps.
  • Компактные сборки Java
    • Возможность создания профилей для платформы Java SE, которые включают в себя не всю платформу целиком, а некоторую ее часть.
  • Безопасность
    • Новая реализация AccessController.doPrivileged, позволяющая устанавливать подмножество привелегий без необходимости проверки всех остальных уровней доступа;
    • Password-based алгоритмы стали более устойчивыми;
    • Добавлена поддержка SSL/TLS Server Name Indication (NSI) в JSSE Server;
    • Улучшено хранилище ключей (KeyStore);
    • Добавлен алгоритм SHA-224;
  • Инструменты
    • Добавлена команда jjs для использования нового JavaScript-движка Nashorn;
    • Команда java может запускать JavaFX приложения;
    • Добавлена команда jdeps для анализа .class-файлов.
  • Интернационализация
    • Добавлена поддержка Unicode 6.2.0;
    • Добавлен новый API для Calendar и Locale;
  • Новый API Date/Time;
  • Новый движок JavaScript Nashorn;
  • [N]IO
    • Улучшена производительность конструктора java.lang.String(byte[], *) и метода java.lang.String.getBytes().
  • Добавлен стандартный класс для работы с Base64;
  • Добавлена поддержка беззнаковой арифметики;
  • Удален мост JDBC-ODBC;
  • Удален PermGen, изменен способ хранения мета-данных классов.
  • Добавлено несколько новых классов для потокобезопасной работы с ConcurrentHashMap, Atomics, ForkJoinPool, Locks.

Официальный список изменений

Table of Contents

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

Наиболее значимым нововведением в Java 8 являются ламбда-выражения (Проект Ламбда). Ламбда выражения, по факту, являются "синтаксическим сахаром" для анонимных классов с автоматическим выведением типов.

Синтаксис ламбда-выражений

Структура ламбда-выражения выглядит так:

параметры -> тело

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

  1. Задание типов параметров не является обязательным;
  2. Использование скобок, заключающих параметры ламбда-выражения не обязательно;
  3. Использование фигурных скобок в теле ламбда-выражения не обязательно, если тело состоит из одного оператора;
  4. Ключевое слово return не является обязательным, если тело ламбда-выражения состоит из одного оператора.

Примеры ламбда-выражений:

Вот так будет выглядеть сортировка массива строк:

Область видимости

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

Например, так выглядит доступ к экземпляру класса и его методам:

А в этом примере в ламбда-выражении используется инициализированная переменная:

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

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

Простой пример реализациии файлового фильтра PDF, TXT и RTF-документов:

Теперь эти функции можно использовать следующим образом:

FileFilters::fileIsRtf - это ссылка на метод, который будет вызван для каждого файла из списка.

Ссылки на методы могут указывать на:

  1. Статические методы;
  2. Методы объектов;
  3. Конструкторы.

Пример применения функции trim к строкам в файле и вывода строк в консоль:

Здесь для каждой строки с помощью функции map применяется функция trim, затем отбрасываются пустые строки с помощью фильтра и ламбда-выражения и, наконец, все отобранные и преобразованные строки выводятся в консоль.

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

В Java 8 большое внимание было уделено возможности функционального программирования.

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

  1. Function<T, R> - принимает объект типа T и возвращает объект типа R;
  2. Supplier<T> - возвращает объект типа T;
  3. Predicate<T> - возвращает булево значение в зависимости от типа T;
  4. Consumer<T> - производит действие над объектом типа T;
  5. BiFunction - функция Function с двумя параметрами;
  6. BiConsumer - Consumer с двумя параметрами.

Также Java 8 содержит стандартные интерфейсы для примитивных типов:

  1. IntConsumer;
  2. IntFunction<R>;
  3. IntPredicate;
  4. IntSupplier
  5. и т.д.

Небольшой пример относительно функциональных интерфейсов:

Также, помимо стандартных функциональных интерфейсов, можно определять свои собственные. Для этого интерфейсу необходимо присвоить аннотацию @FunctionalInterface:

Сравнение с Java 7 или краткость - сестра таланта

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

ActionListener

Вывод в консоль списка строк

Сортировка списка строк

Сортировка объектов

Определим класс Person:

Сортировка объектов Person будет выглядеть следующим образом:

Особенно нагляден последний пример - вместо десятка строк имеем всего одну :)

Интерфейсы

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

Для реализации нового потокового API, в языке Java потребовалось ввести новую конструкцию - методы по умолчанию (Default Methods, также извесные как Defender Methods или Virtual Extension Methods).

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

Например, метод stream для интерфейса Collection выглядит так:

Функциональные интерфейсы также могут иметь методы по умолчанию.

Одноименные методы по умолчанию

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

Здесь переопределяется метод talk и в нем вызывается метод Foo.talk. Данный механизм аналогичен работе с суперклассами в Java.

Статические методы в интерфейсах

В интерфейсах появилась возможность определять полноценные статические методы:

Много статических методов имеет интерфейс Stream. Например:

Потоки

Интерфейс Stream стал фундаментальным в Java 8 - большое количество существующих библиотек работают с этим интерфейсом.

Потоковая работа с файловой системой

Класс BufferedReader теперь имеет метод lines, возвращающий поток. К примеру:

Особвенно здесь в том, что потоковые метод lines не читает весь файл целиком.

Потоковое дерево файловой структуры

В классе Files появились методы для навигации по файловой структуре:

  1. list(Path dir) - поток файлов в некоторой директории;
  2. walk(Path dir) - то же дерево, но первыми в списке будут файлы из текущей директории;
  3. walk(Path dir, int maxDepth) - файловая структура с указанием максимальной глубины рекурсии сканирования.

Потоковые текстовые шаблоны

Класс Pattern теперь имеет метод splitAsStream(CharSequence), который создает поток. Например:

Потоковые генераторы

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

Однако, аналогичный функционал уже предоставляется классом Random, в котором доступны методы ints, longs и doubles.

Также в Java 8 были добавлены генераторы данных в диапазонах:

Вообще говоря, создать поток можно из любой коллекции или массива. В этом помогут методы Stream.of(...) и Arrays.stream(...).

С помощью метода peek можно произвести действия над элементами потока, не изменяя состояние самого потока:

Сортировка и выборка

В потоковом API доступны функция sorted для сортировки элементов потока и limit(int n) для выборки n элементов из потока.

Например:

Здесь происходит следующее:

  1. Получение списка файлов в текущей директории;
  2. Получение списка имен файлов;
  3. С помощью фильтра выбираются только файлы с расширением .java;
  4. Список имен сортируется;
  5. Выбираются первые 5 элементов и выводятся в консоль.

Коллекторы и статистика

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

Коллекторы состоят из трех элементов:

  1. Поставщик (Supplier) начального значения;
  2. Аккумулятор (Accumulator), который добавляет значения элементов к начальному;
  3. Сумматор (Combiner), комбинирующий несколько результатов вычислений в один.

Например, так выглядит преобразование потока в список:

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

Здесь averagingInt отвечает за расчет среднего значения длины строки.

В некоторых случаях необходимо выполнить несколько агрегирующих операций над элементами потока. В таком случае гораздо оптимальнее выполнить сразу все операции за одну выборку из потока, вместо того, чтобы при каждой операции выбирать все элементы. Здесь на помощь приходит семество классов *SummaryStatistics (IntSummaryStatistics, DoubleSummaryStatistics, LongSummaryStatistics).

Работа с данными классами осуществляется следующим образом:

Аналогичный объект IntSummaryStatistics для потока можно получить, преобразовав элементы потока в примитивный тип и вызвав метод summaryStatistics:

Группировка и разбиение

Коллектор groupingBy группирует элементы потока на основании результатов выполнения заданной функции:

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

Хотя добавление отдельного partitioningBy не совсем понятно, т.к. абсолютно аналогичный функционал можно обеспечить с помощью grouppingBy:

Map, Filter, Collect

Ламбда-выражения позволяют эффективно реализовать алгоритмы Map, Filter и Reduce.

Мэппинг присутсвовал во многих примерах выше. Он позволяет выполнять функции над элементами потока:

Функция reduce позволяет агрегировать некоторую информацию, связанную с элемнтами потока, при помощи задаваемой пользователем функции:

Функция редукции принимает два параметра:

  1. Начальное значение (identity);
  2. Аккумулятор (accumulator) - функция, принимающая на вход два параметра: частичный результат редукции и следующий элемент потока.

Функция редукции всегда генерирует новое значение, возвращаемое как результат выполнения функции-аккумулятора.

Функция collect очень похожа на reduce. Главное отличие collect от reduce: collect возвращает только общий результат без промежуточных результатов.

Функция collect принимает на вход три ламбда-выражения:

  1. Функция-поставщик (supplier), отвечающая за создание новых объектов, создает объекты для результирующего контейнера данных;
  2. Функция-аккумулятор (accumulator), осуществляющая размещение в результирующем контейнере;
  3. Функция-комбинатор (combiner), используемая для комбинирования контейнеров при параллельных вычислениях.

Выглядит это примерно следующим образом:

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

Интересно, что нигде в документации не сказано, как, все-таки, функционирует комбинатор. Комбинатор необходим в случае выполнения reduce-операций в параллельных потоках. Если заменить stream на parallelStream, то вместо вызова функций accept для однажды созданного объекта Averager и передачи значения, будут созданы несколько объектов Averager и для каждого объекта вызвана функция combine. В функцию combine нового объекта Averager будет передан объект из предыдущего этапа вычисления.

В Java 8 добавлен интерфейс Collector. Все классы, реализующие его, становятся "автономными" коллекторами и использовать их в функции collect можно следующим образом:

То есть, вместо трех параметров разрешается просто передавать объект-collector.

Вообще говоря, класс Collectors содержит достаточно много полезных коллекторов. Среди них:

  • toCollection - коллектор преобразования в коллекцию;
  • toList - коллектор преобразования в список;
  • toSet - коллектор преобразования в множество;
  • joining - коллектор, обеспечивающий конкатенацию строк с заданным разделителем;
  • mapMerger - коллектор для выполнения функции Map.merge над элементами;
  • map - позволяет преобразовать элементы одного типа в элементы другого типа;
  • collectingAndThen - позволяет выполнить заданную функцию после завершения collect-операции;
  • counting - позволяет посчитать количество элементов потока;
  • minBy/maxBy - коллектор для нахождения минимального или максимального элемента в потоке;
  • summingInt/summinLong/summinDouble - коллектор, выполняющий суммирование элементов;
  • averagingIng/averagingLong/averagingDouble - коллектор, вычисляющий среднее арифметическое элементов потока;
  • reducing - коллектор, осуществляющий редукцию элементов на основании заданного бинарного оператора. Также может быть задана mapping-функция для преобразования элементов до осуществления редукции;
  • groupingBy/groupingByConcurrent - коллектор группировки элементов потока;
  • partitioningBy - коллектор разбиения элементов потока;
  • toMap/toConcurrentMap - уже известный коллектор, позволяющий создать таблицу (Map) элементов потока;
  • summarizingInt/summarizingLong/summarizingDouble - сумматор элементов потока, осуществляющий суммирование элементов потока с помощью заданной функции и суммарный результат выполнения всех таких операций.

Подробнее о map, reduce, collect.

Параллельные потоки

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

Работа с параллельным потоком ничем не отличается от работы с обычным потоком:

Реализацию параллельных вычислений Java берет на себя, равно как и предоставление обощенного API.

Класс Optional и NullPointerException

В Java 8 появился новый класс java.util.Optional, который используется для того, чтобы избежать исключения NullPointerException. Класс Optional может служить оберткой для любого объекта.

Проблема на миллиард долларов

Тони Хоар, создатель null-значения, написал запись под названием "Ошибка на миллиард долларов". Несмотря на чье-либо мнение о null, были приложены большие усилия на борьбу с исключениями, порождаемыми этим изобретением.

Для создания обертки ненулевого значения используется функция Optional.of(value); Optional.empty() предоставляет обертку над инвалидным нулевым объектом; Optional.ofNullable(value) используется для обертки значения, состояние которого не известно:

Java 8 реализует очень удобный метод orElse(defaultValue), который вернет либо объект, содержащийся в контейнере Optional, либо значение по умолчанию, переданное в этот метод.

Класс Optional содержит еще несколько полезных методов:

  • orElseGet(Supplier<T>) - аналог orElse, но здесь передается функция-поставщик для создания объекта по умолчанию;
  • orElseThrow(Supplier<X extends Throwable>) - вызывает исключение, если в Optional содержится null;
  • filter(Predicate<? super T>) возврвщает следующие значения:
    • если объект равен null, то возвращается текущий пустой Optional-объект;
    • если предикат вернул true, то возвращается текущий Optional-объект;
    • если предикат вернул false, то возвращается пустой Optional-объект.
  • map(Function<? super T, ? extends U> mapper) - применяет функцию мэппинга над объектом, содержащимся в контейнере Optional;
  • flatMap(Function<? super T, Optional<U>> mapper) - аналогично map выполняет функцию мэппинга, возвращающую в качестве результата Optional-объект;
  • ifPresent(Consumer<? super T> consumer) - выполняет consumer-функцию в случае, если объект не равен null.

Nashorn

JDK 8 использует в качестве JavaScript-движка Nashorn, ставший заменой движку Rhino. Nashorn значительно быстрее Rhino, т.к. он использует invokedynamic возможность JVM. Nashorn поставляется с cmd-утилитой jjs, которая позволяет исполнять JavaScript прямо в консоли.

Nashorn, как и Rhino, может быть использован в Java-программах для выполнения JavaScript-кода:

По-прежнему поддерживаются такие вещи, как импорт пакетов Java в JavaScript-код, наследование Java-классов в JavaScript и выполнение Java-методов в JavaScript.

Новый Date/Time API

В Java 8 введен новый Date/Time API, являющийся более безопасным, легким для чтения и более обощенным по сравнению с предыдущими версиями.

Новые Date/Time классы

Основное отличие - появление нескольких новых классов в Date/Time API, представляющих время, дату, временной период и временные зоны, а также - классы-трансформеры.

Если не требуется использование временных зон, то для работы с датами используется класс LocalDate, для работы со временем - класс LocalTime, и для работы со временем и датами - LocalDateTime. Для работы с временными зонами используется класс ZonedDateTime.

Давайте сравним новый API:

Вот так можно перейти к последнему дню месяца:

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

Здесь функция format позволяет отформатировать дату в нужном виде. В Java 8 включено несколько стандартных форматов даты и времени в классе DateTimeFormatter.

В Java 8 определено несколько Helper-функций для ZonedDateTime, LocalDate и т.д. (здесь DateTime представляет абстрактный класс работы со временем в Java 8):

  1. DateTime.of - в эту функцию передаются значения года, месяца, дня, часа, минуты и т.д. Функция возвращает новый Date/Time объект. Передаваемые параметры зависят от того, к какому классу принадлежит функция of;
  2. DateTime.from - создает новый Date/Time-объект из другого Date/Time-объекта;
  3. DateTime.parse - позволяет "спарсить" дату из строки. Можно задать свой собственный шаблон для сканирования строки;

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

Для работы со временем в наносекундах добавлен класс Instant. Данный класс специальным образом хранит количество секунд и количество наносекунд текущей секунды, чтобы не превышать размер Long.

Класс Clock

Класс Clock, являющийся частью Date/Time API Java 8, представляет собой абстракцию, созданную для возможности использования различных Date/Time-объектов в одном месте:

Замена PermGen на Metaspace

В Java 8 мета-данные классов размещаются в native memory, а intern-строки и статические данные классов - в куче. Частично такое решение принято по соглашению с проектом JRockit, т.к. в JRockit нет Permanent Generation.

В частности это означает, что отпадает необходимость настраивать параметр XX:PermSize, что облегчает компиляцию, запуск и конфигурацию приложений.

Теперь размер пространства мета-данных по умолчанию ограничен только native-памятью JVM. Для ситуаций, когда необходимо установить определенный лимит памяти, используемыми мета-данными, введен параметр MaxMetaspaceSize. Также этот параметр влияет на логику работы сборщика мусора - при достижении ограничения занимаемого объема памяти, будет запущена сборка мусора в мета-данных.

Вместо исключения "java.lang.OutOfMemoryError: Permgen space" теперь при превышении лимита памяти будет генерироваться исключение "java.lang.OutOfMemoryError: Metadata space".

Base64

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

Фабрика Base64 содержит 6 основных методов:

  1. getEncoder/getDecoder - возвращает кодировщик/декодировщик base64, соответствующий стандарту RFC 4648;
  2. getUrlEncoder/getUrlDecoder - возвращает URL-safe кодировщик/декодировщик base 64, соответствующий стандарту RFC 4648;
  3. getMimeEncoder/getMimeDecoder - возвращает MIME кодировщик/декодировщик, соответствующий стандарту 2045.

Причина существования url-safe стандарта base64 объясняется следующим:

Использование URL-кодировщика над стандартом Base64, несмотря на это, неудобно, так как он преобразует символы '/' и '+' в специальные шестнадцатеричные последовательности. Если позднее эта строка используется вместе с базой данных или через гетерогенные системы, они прекращают работу на символе '%', сгенерированном URL-кодировщиком (потому что символ '%' также используется в ANSI SQL как шаблон).

По причине этого, существует изменённый Base64 для URL,где не используется заполнение символом '=' и символы '+' и '/' соответственно заменяются на '*' и '-', так, чтобы использование кодеров/декодеров URL перестаёт быть необходимым и не имеет никакого воздействия на длину закодированного значения, оставляя ту же самую закодированную форму, неповреждённую для использования в реляционных базах данных, веб-формах, и идентификаторах объекта вообще.

MIME base64 используется для преобразования произвольной последовательности байт в последовательность печатных символов ASCII. В основном применяется в электронной почте.

Аннотациии над типами данных

В Java 8 стало возможным объявлять аннотации над типами данных. Вот несколько примеров:

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

Повторяемые аннотации

Еще одна замечательная вещь, появившаяся в Java  8 - возможность использовать одну и ту же аннотацию в одном и том же месте несколько раз:

Чтобы получить список таких аннотаций, необходимо вызвать refleсtion-метод getAnnotationsByType(Class).

Эта особенность может упростить и сделать более элегантными многие решения.

Стандартные аннотации не могут быть повторяемыми. Чтобы определить повторяемую аннотацию, необходимо создать аннотацию-контейнер для списка повторяемых аннотаций и обозначить аннотацию мета-аннотацией Repeatable:

Поддержка беззнаковых целых

В Java 8 разработчики постарались ввести поддержку беззнаковых типов данных, а именно - unsigned int и unsigned long. Способ работы с беззнаковыми целыми в Java 8 довольно специфичен: классы Integer и Long содержат статические методы для выполнения операций над такими числами:

  • compareUnsined - сравнивает два беззнаковых целых;
  • divideUnsigned - делит два беззнаковых целых и возвращает результат деления;
  • remainderUnsigned - возвращает остаток от деления;
  • parseUnsignedInt/parseUnsignedLong - конвертирует строковое значение в беззнаковое целое;
  • toUnsignedString - конвертирует целое в строку, представляющую беззнаковое целое;
  • toUnsignedLong - конвертирует число типа int в число типа long с помощью беззнакового преобразования.

Реализация довольно странная, но у Oracle есть объяснение выбранному подходу.

Компактные сборки Java

В Java 8 появилась возможность формировать компактные сборки - сборки JVM, которые весят значительно меньше, чем стандартная Java SE.

Существует три стандартных профиля компактных сборок:

  1. compact1 включает в себя следующие компоненты:
    1. Ядро (java.lang.*);
    2. Сеть;
    3. Date/Time API;
    4. Логирование;
    5. Работа с JAR;
    6. Интернационализация;
    7. Механизм расширений;
    8. Пакет безопасности;
    9. IO;
    10. Потокобезопасность;
    11. Работа с ZIP;
    12. JNDI;
    13. Работа со скриптами;
    14. Сериализация;
    15. Регулярные выражения;
    16. Коллекции;
    17. Рефлексия;
    18. Версионинг;
    19. Механизм переопределения (override).
  2. compct2 - это compact1 +
    1. JDBC;
    2. RMI;
    3. XML JAXP.
  3. comact3 - это compact2 +
    1. XML JAXP криптография;
    2. Пакет менеджмента;
    3. Пакет Java Instrumentation;
    4. JMX;
    5. Безопасность: Kerberos, SASL.

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

Для статического анализа зависимостей можно воспользоваться утилитой jdeps с ключом -profile profile.

JavaFX

В Java 8 появилась новая тема под названием Modena:

Modena-Windows-v1

А также:

  • Класс SwingNode теперь позволяет внедрять контент Swing в JavaFX-приложения;
  • Добавлены элементы интерфейса DatePicker и TreeTableView;
  • Пакет javafx.print предоставляет классы для JavaFX Printing API;
  • 3D-движок теперь включает в себя 3D-фигуры, камеру, свет, материалы, сортировку, антиалиасинг;
  • У класса WebView появилась поддержка HTML5 (веб-сокеты, шрифты и пр.);
  • Расширенная поддержка текста (двунаправленный текст, Хинди и пр.)
  • Добавлена поддержка Hi-DPI мониторов;
  • CSS Stylable классы стали общедоступными;
  • Новый ScheduledService позволяет автоматически перезапускать сервисы;
  • JavaFX стала доступна для платформ ARM.

Заключение

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

Просмотров: 8102
Комментарии (1) Пинги (0)
  1. Довольно странно решение на счет unsigned. Могли бы ввести полноценные новые типы данных, что бы не было особых проблем. Для более низких JVM вполне бы сошла конвертация в long/BigDecimal


Leave a comment


9 − девять =

http://microfork.com/wp-content/plugins/wp-monalisa/icons/wpml_bye.gif 
http://microfork.com/wp-content/plugins/wp-monalisa/icons/wpml_good.gif 
http://microfork.com/wp-content/plugins/wp-monalisa/icons/wpml_negative.gif 
http://microfork.com/wp-content/plugins/wp-monalisa/icons/wpml_scratch.gif 
http://microfork.com/wp-content/plugins/wp-monalisa/icons/wpml_wacko.gif 
http://microfork.com/wp-content/plugins/wp-monalisa/icons/wpml_yahoo.gif 
http://microfork.com/wp-content/plugins/wp-monalisa/icons/wpml_cool.gif 
http://microfork.com/wp-content/plugins/wp-monalisa/icons/wpml_heart.gif 
http://microfork.com/wp-content/plugins/wp-monalisa/icons/wpml_rose.gif 
http://microfork.com/wp-content/plugins/wp-monalisa/icons/wpml_smile.gif 
http://microfork.com/wp-content/plugins/wp-monalisa/icons/wpml_whistle3.gif 
http://microfork.com/wp-content/plugins/wp-monalisa/icons/wpml_yes.gif 
http://microfork.com/wp-content/plugins/wp-monalisa/icons/wpml_cry.gif 
http://microfork.com/wp-content/plugins/wp-monalisa/icons/wpml_mail.gif 
http://microfork.com/wp-content/plugins/wp-monalisa/icons/wpml_sad.gif 
http://microfork.com/wp-content/plugins/wp-monalisa/icons/wpml_unsure.gif 
http://microfork.com/wp-content/plugins/wp-monalisa/icons/wpml_wink.gif 
 

Trackbacks are disabled.