новые функции java8 ~ финальная версия

Java JVM переводчик API

Автор: Ду Ци [перевод]

Оригинальная ссылка: http://www.jianshu.com/p/5b800057f2d8

1. Введение

Без сомнения, Java 8 является самой важной версией Java со времен Java 5 (выпущенной в 2004 году). Этот выпуск содержит более десятка новых функций в языках, компиляторах, библиотеках, инструментах и ​​JVM. В этой статье мы узнаем об этих новых функциях и на реальных примерах покажем, где они подходят для использования.

В этом руководстве рассматриваются несколько типов проблем, с которыми часто сталкиваются Java-разработчики:

  • язык

  • переводчик

  • библиотека

  • инструмент

  • Среда выполнения (JVM)

2. Новые возможности языка Java

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

2.1 Лямбда-выражения и функциональные интерфейсы

Лямбда-выражения (также известные как замыкания) — самое большое и наиболее ожидаемое языковое изменение в Java 8. Это позволяет нам передавать функции как параметры метода или рассматривать сам код как данные: разработчики функциональных функций хорошо знакомы с этими концепциями. Многие языки на платформе JVM (Groovy, Scala и т. д.) с момента своего появления поддерживают лямбда-выражения, но у Java-разработчиков нет другого выбора, кроме как использовать анонимные внутренние классы вместо лямбда-выражений.

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

  1. Arrays.asList( "a", "b", "d" ).forEach( e -> System.out.println( e ) );

скопировать код

Параметры в этом коде вышеeТип параметра определяется компилятором, вы также можете указать тип параметра явно, например:

  1. Arrays.asList( "a", "b", "d" ).forEach( ( String e ) -> System.out.println( e ) );

скопировать код

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

  1. Arrays.asList( "a", "b", "d" ).forEach( e -> {

  2.    System.out.print( e );

  3.    System.out.print( e );

  4. } );

скопировать код

Лямбда-выражения могут ссылаться на члены класса и локальные переменные (которые неявно преобразуются вfinal), например, следующие два блока кода имеют одинаковый эффект:

  1. String separator = ",";

  2. Arrays.asList( "a", "b", "d" ).forEach(

  3.    ( String e ) -> System.out.print( e + separator ) );

скопировать код

и

  1. final String separator = ",";

  2. Arrays.asList( "a", "b", "d" ).forEach(

  3.    ( String e ) -> System.out.print( e + separator ) );

скопировать код

Лямбда-выражения имеют возвращаемое значение, и тип возвращаемого значения также определяется компилятором. Если блок операторов в лямбда-выражении имеет только одну строку, вы не можете использоватьreturnзаявление, следующие два фрагмента кода имеют тот же эффект:

  1. Arrays.asList( "a", "b", "d" ).sort( ( e1, e2 ) -> e1.compareTo( e2 ) );

скопировать код

и

  1. Arrays.asList( "a", "b", "d" ).sort( ( e1, e2 ) -> {

  2.    int result = e1.compareTo( e2 );

  3.    return result;

  4. } );

скопировать код

Чтобы сделать существующие функции совместимыми с лямбда-выражениями, разработчики лямбда рассмотрели множество методов, в результате чегофункциональный интерфейсэто понятие. Функциональный интерфейс относится к интерфейсу только с одной функцией, и такой интерфейс может быть неявно преобразован в лямбда-выражение.java.lang.Runnableиjava.util.concurrent.Callableявляется лучшим примером функционального интерфейса. На практике функциональные интерфейсы очень хрупкие: как только разработчик добавляет в интерфейс функцию, интерфейс перестает работать, и компиляция завершается ошибкой. Чтобы преодолеть эту хрупкость на уровне кода и явно указать, что интерфейс является функциональным интерфейсом, Java 8 содержит специальную аннотацию@FunctionalInterface(Все соответствующие интерфейсы в библиотеке Java уже имеют эту аннотацию) для определения простого функционального интерфейса:

  1. @FunctionalInterface

  2. public interface Functional {

  3.    void method();

  4. }

скопировать код

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

  1. @FunctionalInterface

  2. public interface FunctionalDefaultMethods {

  3.    void method();

  4.    default void defaultMethod() {            

  5.    }        

  6. }

скопировать код

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

2.2 Методы по умолчанию и статические методы интерфейсов

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

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

  1. private interface Defaulable {

  2.    // Interfaces now allow default methods, the implementer may or

  3.    // may not implement (override) them.

  4.    default String notRequired() {

  5.        return "Default implementation";

  6.    }        

  7. }

  8. private static class DefaultableImpl implements Defaulable {

  9. }

  10. private static class OverridableImpl implements Defaulable {

  11.    @Override

  12.    public String notRequired() {

  13.        return "Overridden implementation";

  14.    }

  15. }

скопировать код

DefaulableИнтерфейс с использованием ключевых словdefaultопределяет метод по умолчаниюnotRequired().DefaultableImplКласс реализует этот интерфейс и по умолчанию наследует методы по умолчанию в этом интерфейсе;OverridableImplКласс также реализует этот интерфейс, но переопределяет методы интерфейса по умолчанию и предоставляет другую реализацию.

Еще одна интересная особенность Java 8 заключается в том, что статические методы могут быть определены в интерфейсах Пример кода выглядит следующим образом:

  1. private interface DefaulableFactory {

  2.    // Interfaces now allow static methods

  3.    static Defaulable create( Supplier< Defaulable > supplier ) {

  4.        return supplier.get();

  5.    }

  6. }

скопировать код

Следующий фрагмент кода объединяет сценарии использования методов по умолчанию и статических методов:

  1. public static void main( String[] args ) {

  2.    Defaulable defaulable = DefaulableFactory.create( DefaultableImpl::new );

  3.    System.out.println( defaulable.notRequired() );

  4.    defaulable = DefaulableFactory.create( OverridableImpl::new );

  5.    System.out.println( defaulable.notRequired() );

  6. }

скопировать код

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

  1. Default implementation

  2. Overridden implementation

скопировать код

Поскольку реализация метода по умолчанию в JVM обеспечивает поддержку на уровне байт-кода, она очень эффективна. Методы по умолчанию позволяют улучшать интерфейсы, не нарушая существующие системы наследования. Применение этой функции в официальной библиотеке:java.util.Collectionинтерфейс для добавления новых методов, таких какstream(),parallelStream(),forEach()иremoveIf()и Т. Д.

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

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

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

В случае Саймона,CarКлассы — это примеры различных ссылок на методы, которые могут помочь читателям различать четыре типа ссылок на методы.

  1. public static class Car {

  2.    public static Car create( final Supplier< Car > supplier ) {

  3.        return supplier.get();

  4.    }              

  5.    public static void collide( final Car car ) {

  6.        System.out.println( "Collided " + car.toString() );

  7.    }

  8.    public void follow( final Car another ) {

  9.        System.out.println( "Following the " + another.toString() );

  10.    }

  11.    public void repair() {  

  12.        System.out.println( "Repaired " + this.toString() );

  13.    }

  14. }

скопировать код

Тип первой ссылки на метод — это ссылка на конструктор, а синтаксис —Class::new, или в более общем виде:Class::new. Примечание. Этот конструктор не имеет параметров.

  1. final Car car = Car.create( Car::new );

  2. final List< Car > cars = Arrays.asList( car );

скопировать код

Тип второй ссылки на метод — это ссылка на статический метод, а синтаксис —Class::static_method. Примечание. Этот метод принимает параметр типа Car.

  1. cars.forEach( Car::collide );

скопировать код

Третий тип ссылки на метод — это ссылка на метод-член класса.Class::method, обратите внимание, что этот метод не имеет определенных входных параметров:

  1. cars.forEach( Car::repair );

скопировать код

Тип, на который ссылается четвертый метод, является ссылкой на метод-член экземпляра объекта.instance::method. Примечание. Этот метод принимает параметр типа Car:

  1. final Car police = Car.create( Car::new );

  2. cars.forEach( police::follow );

скопировать код

Запустив приведенный выше пример, вы увидите следующий вывод в консоли (экземпляр Car может быть другим):

  1. Collided com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197d

  2. Repaired com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197d

  3. Following the com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197d

скопировать код

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

2.4 Дублирование аннотаций

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

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

  1. package com.javacodegeeks.java8.repeatable.annotations;

  2. import java.lang.annotation.ElementType;

  3. import java.lang.annotation.Repeatable;

  4. import java.lang.annotation.Retention;

  5. import java.lang.annotation.RetentionPolicy;

  6. import java.lang.annotation.Target;

  7. public class RepeatingAnnotations {

  8.    @Target( ElementType.TYPE )

  9.    @Retention( RetentionPolicy.RUNTIME )

  10.    public @interface Filters {

  11.        Filter[] value();

  12.    }

  13.    @Target( ElementType.TYPE )

  14.    @Retention( RetentionPolicy.RUNTIME )

  15.    @Repeatable( Filters.class )

  16.    public @interface Filter {

  17.        String value();

  18.    };

  19.    @Filter( "filter1" )

  20.    @Filter( "filter2" )

  21.    public interface Filterable {        

  22.    }

  23.    public static void main(String[] args) {

  24.        for( Filter filter: Filterable.class.getAnnotationsByType( Filter.class ) ) {

  25.            System.out.println( filter.value() );

  26.        }

  27.    }

  28. }

скопировать код

Как видим, здесьFilterКласс украшен аннотацией @Repeatable(Filters.class) иFiltersхранитьFilterАннотированный контейнер, компилятор пытается скрыть эти детали от разработчика. так,FilterableИнтерфейс может использовать дваFilterКомментарии к аннотациям (здесь нет упоминания какой-либо информации о Фильтрах).

Кроме того, API отражения предоставляет новый метод:getAnnotationsByType(), который может возвращать повторяющуюся аннотацию определенного типа, напримерFilterable.class.getAnnoation(Filters.class)Будет возвращено два экземпляра Filter, а вывод в консоль будет выглядеть так:

  1. filter1

  2. filter2

скопировать код

Если вы хотите узнать больше, вы можете обратиться к официальной документации.

2.5 Улучшенный вывод типов

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

  1. package com.javacodegeeks.java8.type.inference;

  2. public class Value< T > {

  3.    public static< T > T defaultValue() {

  4.        return null;

  5.    }

  6.    public T getOrDefault( T value, T defaultValue ) {

  7.        return ( value != null ) ? value : defaultValue;

  8.    }

  9. }

скопировать код

Следующий кодValueТип заявления:

  1. package com.javacodegeeks.java8.type.inference;

  2. public class TypeInference {

  3.    public static void main(String[] args) {

  4.        final Value< String > value = new Value<>();

  5.        value.getOrDefault( "22", Value.defaultValue() );

  6.    }

  7. }

скопировать код

параметрValue.defaultValue()Тип определяется компилятором и не требует явного указания. В Java 7 этот код будет компилировать ошибки, если не используетсяValue.<String>defaultValue().

2.6 Расширить сценарии применения аннотаций

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

  1. package com.javacodegeeks.java8.annotations;

  2. import java.lang.annotation.ElementType;

  3. import java.lang.annotation.Retention;

  4. import java.lang.annotation.RetentionPolicy;

  5. import java.lang.annotation.Target;

  6. import java.util.ArrayList;

  7. import java.util.Collection;

  8. public class Annotations {

  9.    @Retention( RetentionPolicy.RUNTIME )

  10.    @Target( { ElementType.TYPE_USE, ElementType.TYPE_PARAMETER } )

  11.    public @interface NonEmpty {        

  12.    }

  13.    public static class Holder< @NonEmpty T > extends @NonEmpty Object {

  14.        public void method() throws @NonEmpty Exception {            

  15.        }

  16.    }

  17.    @SuppressWarnings( "unused" )

  18.    public static void main(String[] args) {

  19.        final Holder< String > holder = new @NonEmpty Holder< String >();        

  20.        @NonEmpty Collection< @NonEmpty String > strings = new ArrayList<>();        

  21.    }

  22. }

скопировать код

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

3. Новые возможности компилятора Java

3.1 Название параметра

Чтобы получить имена параметров метода в программе Java во время выполнения, программисты старшего поколения должны были использовать другой метод, например, библиотеку Paranamer. Java 8, наконец, нормализует эту функцию на уровне языка (используя API отражения иМетод Parameter.getName()) и уровень байт-кода (с использованием новогоjavacкомпилятор и-parametersпараметры) для поддержки.

  1. package com.javacodegeeks.java8.parameter.names;

  2. import java.lang.reflect.Method;

  3. import java.lang.reflect.Parameter;

  4. public class ParameterNames {

  5.    public static void main(String[] args) throws Exception {

  6.        Method method = ParameterNames.class.getMethod( "main", String[].class );

  7.        for( final Parameter parameter: method.getParameters() ) {

  8.            System.out.println( "Parameter: " + parameter.getName() );

  9.        }

  10.    }

  11. }

скопировать код

Эта функция по умолчанию отключена в Java 8, поэтому, если вы не-parametersСкомпилируйте приведенный выше код с параметрами и запустите его, он выведет следующий результат:

  1. Parameter: arg0

скопировать код

если с-parametersпараметр, он выведет следующий результат (правильный результат):

  1. Parameter: args

скопировать код

Если вы используете Maven для управления проектами, вы можетеmaven-compiler-pluginНастраивается в пункте конфигурации компилятора-parametersпараметр:

  1. <plugin>

  2.    <groupId>org.apache.maven.plugins</groupId>

  3.    <artifactId>maven-compiler-plugin</artifactId>

  4.    <version>3.1</version>

  5.    <configuration>

  6.        <compilerArgument>-parameters</compilerArgument>

  7.        <source>1.8</source>

  8.        <target>1.8</target>

  9.    </configuration>

  10. </plugin>

скопировать код

4. Новые возможности официальной библиотеки Java

В Java 8 добавлено много новых служебных классов (классы даты/времени) и расширены существующие служебные классы для поддержки современного параллельного программирования, функционального программирования и многого другого.

4.1 Optional

Наиболее распространенной ошибкой в ​​приложениях Java является нулевое исключение. До Java 8 Google Guava представилаOptionalsкласс для решенияNullPointerException, чтобы предотвратить использование исходного кода различнымиnullПроверяйте наличие загрязнения, чтобы разработчики могли писать более чистый код. Java 8 также будетOptionalДобавлена ​​официальная библиотека.

OptionalПросто: сохраните значение типа T или null. Он предоставляет несколько полезных интерфейсов, позволяющих избежать явных проверок null. Более подробную информацию можно найти в официальной документации по Java 8.

Давайте посмотрим на использованиеOptionalПример: возможно нулевое значение или значение некоторого типа:

  1. Optional< String > fullName = Optional.ofNullable( null );

  2. System.out.println( "Full Name is set? " + fullName.isPresent() );        

  3. System.out.println( "Full Name: " + fullName.orElseGet( () -> "[none]" ) );

  4. System.out.println( fullName.map( s -> "Hey " + s + "!" ).orElse( "Hey Stranger!" ) );

скопировать код

еслиOptionalinstance содержит ненулевое значение, тогдаisPresent()Метод возвращает true, в противном случае он возвращает false;orElseGet()метод,OptionalЕсли экземпляр имеет значение null, он может принять значение по умолчанию, сгенерированное лямбда-выражением;map()метод преобразования существующихOpetionalЗначение экземпляра преобразуется в новое значение;orElse()метод сorElseGet()Метод аналогичен, но возвращает переданное значение по умолчанию, когда удерживается значение null.

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

  1. Full Name is set? false

  2. Full Name: [none]

  3. Hey Stranger!

скопировать код

Давайте рассмотрим еще один простой пример:

  1. Optional< String > firstName = Optional.of( "Tom" );

  2. System.out.println( "First Name is set? " + firstName.isPresent() );        

  3. System.out.println( "First Name: " + firstName.orElseGet( () -> "[none]" ) );

  4. System.out.println( firstName.map( s -> "Hey " + s + "!" ).orElse( "Hey Stranger!" ) );

  5. System.out.println();

скопировать код

Результат этого примера:

  1. First Name is set? true

  2. First Name: Tom

  3. Hey Tom!

скопировать код

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

4.2 Streams

Новый Stream API (java.util.stream) переносит функциональное программирование среды сборки в библиотеку Java. Это, безусловно, крупнейшее улучшение библиотеки Java, позволяющее разработчикам писать более эффективный, лаконичный и компактный код.

Stream API значительно упрощает операции с коллекциями (позже мы увидим больше, чем просто коллекции).Давайте сначала посмотрим на этот класс под названием Task:

  1. public class Streams  {

  2.    private enum Status {

  3.        OPEN, CLOSED

  4.    };

  5.    private static final class Task {

  6.        private final Status status;

  7.        private final Integer points;

  8.        Task( final Status status, final Integer points ) {

  9.            this.status = status;

  10.            this.points = points;

  11.        }

  12.        public Integer getPoints() {

  13.            return points;

  14.        }

  15.        public Status getStatus() {

  16.            return status;

  17.        }

  18.        @Override

  19.        public String toString() {

  20.            return String.format( "[%s, %d]", status, points );

  21.        }

  22.    }

  23. }

скопировать код

Класс Task имеет понятие дроби (или псевдосложности) и двух других состояний: OPEN или CLOSED. Теперь предположим, что есть набор задач:

  1. final Collection< Task > tasks = Arrays.asList(

  2.    new Task( Status.OPEN, 5 ),

  3.    new Task( Status.OPEN, 13 ),

  4.    new Task( Status.CLOSED, 8 )

  5. );

скопировать код

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

  1. // Calculate total points of all active tasks using sum()

  2. final long totalPointsOfOpenTasks = tasks

  3.    .stream()

  4.    .filter( task -> task.getStatus() == Status.OPEN )

  5.    .mapToInt( Task::getPoints )

  6.    .sum();

  7. System.out.println( "Total points: " + totalPointsOfOpenTasks );

скопировать код

Консольный вывод запуска этого метода:

  1. Total points: 18

скопировать код

Здесь можно многое сказать. Во-первых, коллекция задач преобразуется в паровое представление, во-вторых,filterОперация отфильтрует все ЗАКРЫТЫЕ задачи; в-третьих,mapToIntОперации основаны на каждом экземпляре задачи.Task::getPointsМетод преобразует поток задач в набор целых чисел; наконец,sumМетод вычисляет сумму и выдает окончательный результат.

Прежде чем перейти к следующему примеру, следует помнить несколько вещей о парах (нажмите здесь, чтобы узнать больше). Операции в Steam можно разделить на промежуточные операции и поздние операции.

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

Поздние операции (т.е.forEachилиsum), он будет проходить через пар и получать результаты или случайные результаты; после выполнения поздней операции линия обработки пара была обработана и не может быть использована. Почти во всех случаях поздние операции сразу же проходят через поток.

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

  1. // Calculate total points of all tasks

  2. final double totalPoints = tasks

  3.   .stream()

  4.   .parallel()

  5.   .map( task -> task.getPoints() ) // or map( Task::getPoints )

  6.   .reduce( 0, Integer::sum );

  7. System.out.println( "Total points (all tasks): " + totalPoints );

скопировать код

Здесь мы используемparallelметод для параллельной обработки всех задач и использованияreduceМетод расчета конечного результата. Вывод консоли выглядит следующим образом:

  1. Total points(all tasks): 26.0

скопировать код

Для набора часто необходимо сгруппировать входящие в него элементы по какому-либо условию. С помощью предоставляемого Steam API можно быстро выполнить такие задачи, код выглядит следующим образом:

  1. // Group tasks by their status

  2. final Map< Status, List< Task > > map = tasks

  3.    .stream()

  4.    .collect( Collectors.groupingBy( Task::getStatus ) );

  5. System.out.println( map );

скопировать код

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

  1. {CLOSED=[[CLOSED, 8]], OPEN=[[OPEN, 5], [OPEN, 13]]}

скопировать код

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

  1. // Calculate the weight of each tasks (as percent of total points)

  2. final Collection< String > result = tasks

  3.    .stream()                                        // Stream< String >

  4.    .mapToInt( Task::getPoints )                     // IntStream

  5.    .asLongStream()                                  // LongStream

  6.    .mapToDouble( points -> points / totalPoints )   // DoubleStream

  7.    .boxed()                                         // Stream< Double >

  8.    .mapToLong( weigth -> ( long )( weigth * 100 ) ) // LongStream

  9.    .mapToObj( percentage -> percentage + "%" )      // Stream< String>

  10.    .collect( Collectors.toList() );                 // List< String >

  11. System.out.println( result );

скопировать код

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

  1. [19%, 50%, 30%]

скопировать код

Наконец, как упоминалось ранее, API Steam может работать не только с коллекциями Java, традиционные операции ввода-вывода (построчное чтение данных из файла или сети) могут выиграть от обработки Steam, вот небольшой пример:

  1. final Path path = new File( filename ).toPath();

  2. try( Stream< String > lines = Files.lines( path, StandardCharsets.UTF_8 ) ) {

  3.    lines.onClose( () -> System.out.println("Done!") ).forEach( System.out::println );

  4. }

скопировать код

Методы потокаonCloseВозвращает эквивалентный Stream с дополнительным дескриптором, который будет выполняться при вызове метода Stream close(). Stream API, лямбда-выражения и ссылки на методы, поддерживаемые стандартными методами интерфейса и статическими методами, являются ответом Java 8 на современную парадигму разработки программного обеспечения.

4.3 Date/Time API(JSR 310)

В Java 8 представлен новый API Date-Time (JSR 310) для улучшения обработки времени и даты. Управление временем и датой всегда было самой болезненной проблемой для Java-разработчиков.java.util.Dateи позжеjava.util.CalendarЭта проблема не решена (и еще больше запутывает разработчиков).

По этим причинам родилась сторонняя библиотека Joda-Time, которая может заменить API управления временем Java. Новый API управления временем и датой в Java 8 находится под сильным влиянием Joda-Time и поглощает большую часть Joda-Time. Новый пакет java.time содержит все классы для даты, времени, часового пояса, Instant (похожие на дату, но с точностью до наносекунд), длительность (duration) и часы. Недавно разработанный API серьезно относится к неизменяемости этих классов (урок, извлеченный из java.util.Calendar), возвращая новый объект, если экземпляр необходимо изменить.

Далее мы рассмотрим ключевые классы в пакете java.time и соответствующие примеры их использования. Во-первых,ClockКласс использует часовой пояс для возврата текущего времени и даты в наносекундах.Clockможно заменитьSystem.currentTimeMillis()иTimeZone.getDefault().

  1. // Get the system clock as UTC offset

  2. final Clock clock = Clock.systemUTC();

  3. System.out.println( clock.instant() );

  4. System.out.println( clock.millis() );

скопировать код

Результат этого примера:

  1. 2014-04-12T15:19:29.282Z

  2. 1397315969360

скопировать код

Во-вторых, обратите вниманиеLocalDateиLocalTimeДобрый.LocalDateСодержит только часть даты календарной системы ISO-8601;LocalTimeзатем содержит только временную часть календарной системы. Объекты обоих классов могут быть созданы с использованием объектов Clock.

  1. // Get the local date and local time

  2. final LocalDate date = LocalDate.now();

  3. final LocalDate dateFromClock = LocalDate.now( clock );

  4. System.out.println( date );

  5. System.out.println( dateFromClock );

  6. // Get the local date and local time

  7. final LocalTime time = LocalTime.now();

  8. final LocalTime timeFromClock = LocalTime.now( clock );

  9. System.out.println( time );

  10. System.out.println( timeFromClock );

скопировать код

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

  1. 2014-04-12

  2. 2014-04-12

  3. 11:25:54.568

  4. 15:25:54.568

скопировать код

LocalDateTimeКласс содержит сведения о LocalDate и LocalTime, но не содержит сведений о часовом поясе в календарной системе ISO-8601. Вот несколько примеров LocalDate и LocalTime:

  1. // Get the local date/time

  2. final LocalDateTime datetime = LocalDateTime.now();

  3. final LocalDateTime datetimeFromClock = LocalDateTime.now( clock );

  4. System.out.println( datetime );

  5. System.out.println( datetimeFromClock );

скопировать код

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

  1. 2014-04-12T11:37:52.309

  2. 2014-04-12T15:37:52.309

скопировать код

Если вам нужна информация о данных/времени для определенного часового пояса, вы можете использоватьZoneDateTime, который содержит дату и время системы дат ISO-8601, а также информацию о часовом поясе. Вот несколько примеров использования разных часовых поясов:

  1. // Get the zoned date/time

  2. final ZonedDateTime zonedDatetime = ZonedDateTime.now();

  3. final ZonedDateTime zonedDatetimeFromClock = ZonedDateTime.now( clock );

  4. final ZonedDateTime zonedDatetimeFromZone = ZonedDateTime.now( ZoneId.of( "America/Los_Angeles" ) );

  5. System.out.println( zonedDatetime );

  6. System.out.println( zonedDatetimeFromClock );

  7. System.out.println( zonedDatetimeFromZone );

скопировать код

Результат этого примера:

  1. 2014-04-12T11:47:01.017-04:00[America/New_York]

  2. 2014-04-12T15:47:01.017Z

  3. 2014-04-12T08:47:01.017-07:00[America/Los_Angeles]

скопировать код

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

  1. // Get duration between two dates

  2. final LocalDateTime from = LocalDateTime.of( 2014, Month.APRIL, 16, 0, 0, 0 );

  3. final LocalDateTime to = LocalDateTime.of( 2015, Month.APRIL, 16, 23, 59, 59 );

  4. final Duration duration = Duration.between( from, to );

  5. System.out.println( "Duration in days: " + duration.toDays() );

  6. System.out.println( "Duration in hours: " + duration.toHours() );

скопировать код

В этом примере вычисляется количество дней и часов между 16 апреля 2014 г. и 16 апреля 2015 г. Результат выглядит следующим образом:

  1. Duration in days: 365

  2. Duration in hours: 8783

скопировать код

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

4.4 Движок JavaScript Nashorn

Java 8 предоставляет новый движок Nashorn JavaScript, который позволяет нам разрабатывать и запускать JS-приложения на JVM. Механизм Nashorn JavaScript — это еще одна версия реализации javax.script.ScriptEngine. Этот тип механизма сценариев следует тем же правилам и позволяет интерактивно использовать Java и JavaScript. Пример кода выглядит следующим образом:

  1. ScriptEngineManager manager = new ScriptEngineManager();

  2. ScriptEngine engine = manager.getEngineByName( "JavaScript" );

  3. System.out.println( engine.getClass().getName() );

  4. System.out.println( "Result:" + engine.eval( "function f() { return 1; }; f() + 1;" ) );

скопировать код

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

  1. jdk.nashorn.api.scripting.NashornScriptEngine

  2. Result: 2

скопировать код

4.5 Base64

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

  1. package com.javacodegeeks.java8.base64;

  2. import java.nio.charset.StandardCharsets;

  3. import java.util.Base64;

  4. public class Base64s {

  5.    public static void main(String[] args) {

  6.        final String text = "Base64 finally in Java 8!";

  7.        final String encoded = Base64

  8.            .getEncoder()

  9.            .encodeToString( text.getBytes( StandardCharsets.UTF_8 ) );

  10.        System.out.println( encoded );

  11.        final String decoded = new String(

  12.            Base64.getDecoder().decode( encoded ),

  13.            StandardCharsets.UTF_8 );

  14.        System.out.println( decoded );

  15.    }

  16. }

скопировать код

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

  1. QmFzZTY0IGZpbmFsbHkgaW4gSmF2YSA4IQ==

  2. Base64 finally in Java 8!

скопировать код

Новый Base64API также поддерживает кодирование и декодирование URL и MINE. (Base64.getUrlEncoder() / Base64.getUrlDecoder()Base64.getMimeEncoder() / Base64.getMimeDecoder()).

4.6 Параллельные массивы

В версии Java8 добавлено много новых методов для поддержки параллельной обработки массивов. Наиболее важным методом являетсяparallelSort(), что может значительно ускорить сортировку массивов на многоядерных машинах. Следующий пример демонстрируетparallexXxxСерия методов:

  1. package com.javacodegeeks.java8.parallel.arrays;

  2. import java.util.Arrays;

  3. import java.util.concurrent.ThreadLocalRandom;

  4. public class ParallelArrays {

  5.    public static void main( String[] args ) {

  6.        long[] arrayOfLong = new long [ 20000 ];        

  7.        Arrays.parallelSetAll( arrayOfLong,

  8.            index -> ThreadLocalRandom.current().nextInt( 1000000 ) );

  9.        Arrays.stream( arrayOfLong ).limit( 10 ).forEach(

  10.            i -> System.out.print( i + " " ) );

  11.        System.out.println();

  12.        Arrays.parallelSort( arrayOfLong );        

  13.        Arrays.stream( arrayOfLong ).limit( 10 ).forEach(

  14.            i -> System.out.print( i + " " ) );

  15.        System.out.println();

  16.    }

  17. }

скопировать код

Эти коды выше используютparallelSetAll()метод для генерации 20000 случайных чисел, а затем использоватьparallelSort()метод сортировки. Эта программа выведет первые 10 элементов неупорядоченного массива и отсортированного массива. Вывод кода для приведенного выше примера:

  1. Unsorted: 591217 891976 443951 424479 766825 351964 242997 642839 119108 552378

  2. Sorted: 39 220 263 268 325 607 655 678 723 793

скопировать код

4.7 Параллелизм

На основе новых лямбда-выражений и функций Steam в Java 8java.util.concurrent.ConcurrentHashMapкласс добавляет новые методы для поддержки операций фокуса; кроме того, дляjava.util.concurrentForkJoinPoolКласс добавляет новые методы для поддержки общих операций пула потоков (подробнее об этом в нашем курсе параллельного программирования).

Java 8 также добавляет новыеjava.util.concurrent.locks.StampedLockКласс для поддержки блокировок на основе емкости — эта блокировка имеет три модели для поддержки операций чтения и записи (подумайте об этой блокировке как оjava.util.concurrent.locks.ReadWriteLockзаменять).

существуетjava.util.concurrent.atomicВ пакет также добавлено много новых классов инструментов, которые перечислены ниже:

  • DoubleAccumulator

  • DoubleAdder

  • LongAccumulator

  • LongAdder

5. Новые инструменты Java

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

5.1 Двигатель Nashorn: jjs

jjsЭто инструмент командной строки, основанный на стандартном движке Nashorn, который может принимать исходный код js и выполнять его. Например, мы пишемfunc.jsфайл со следующим содержимым:

  1. function f() {

  2.     return 1;

  3. };

  4. print( f() + 1 );

скопировать код

Вы можете выполнить эту команду в командной строке:jjs func.js, вывод консоли:

  1. 2

скопировать код

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

5.2 Анализатор зависимостей классов: jdeps

jdeps— довольно крутой инструмент командной строки, который отображает зависимости классов Java на уровне пакетов и классов..classПринимает файл, каталог или JAR-файл в качестве входных данных и выводит зависимости на консоль.

Мы можем использовать jedps для анализа библиотеки Spring Framework, чтобы сделать результаты меньше, просто проанализируйте файл JAR:org.springframework.core-3.0.5.RELEASE.jar.

  1. jdeps org.springframework.core-3.0.5.RELEASE.jar

скопировать код

Эта команда выдаст много результатов, мы рассмотрим только некоторые из них: зависимости сгруппированы по пакетам, и если зависимость не найдена в пути к классам, будет отображаться «не найдено».

  1. org.springframework.core-3.0.5.RELEASE.jar -> C:\Program Files\Java\jdk1.8.0\jre\lib\rt.jar

  2.   org.springframework.core (org.springframework.core-3.0.5.RELEASE.jar)

  3.      -> java.io                                            

  4.      -> java.lang                                          

  5.      -> java.lang.annotation                              

  6.      -> java.lang.ref                                      

  7.      -> java.lang.reflect                                  

  8.      -> java.util                                          

  9.      -> java.util.concurrent                              

  10.      -> org.apache.commons.logging                         not found

  11.      -> org.springframework.asm                            not found

  12.      -> org.springframework.asm.commons                    not found

  13.   org.springframework.core.annotation (org.springframework.core-3.0.5.RELEASE.jar)

  14.      -> java.lang                                          

  15.      -> java.lang.annotation                              

  16.      -> java.lang.reflect                                  

  17.      -> java.util

скопировать код

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

6. Новые возможности JVM

использоватьMetaspace(JEP 122) вместо постоянной генерации (PermGenпространство). С точки зрения аргументов JVM используйте-XX:MetaSpaceSizeи-XX:MaxMetaspaceSizeзаменить оригинал-XX:PermSizeи-XX:MaxPermSize.

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

Java 8 делает платформу Java большим шагом вперед, предоставляя разработчикам множество функций, повышающих производительность. Java 8 пока не подходит для применения в производственных системах, но скорость применения Java 8 будет постепенно увеличиваться в ближайшие несколько месяцев (PS: исходное время — 9 мая 2014 г., и сейчас Java 8 используется во многих компаниях). Он стал мейнстримом.Из-за его большого размера наша компания сейчас немного использует Java 8. Хоть он и медленный, но все равно обновляется). Как разработчик, пришло время получить некоторые знания о Java 8 и подготовиться к обновлению.

О Spring: Для разработки на уровне предприятия мы также должны обратить внимание на поддержку сообщества Spring для Java 8, вы можете обратиться к этой статье — список новых функций Java 8, поддерживаемых Spring 4.

БОЛЬШЕ | Другие интересные статьи