Подробное объяснение десяти новых функций JAVA8.

Java задняя часть

Предисловие: Java 8 был выпущен в течение длительного времени, и во многих отчетах указывается, чтоJava8 является основным обновлением версии. На Java Code Geeks уже есть много статей о новых возможностях Java 8,

НапримерИгра с Java 8 — лямбда-выражения и параллелизм,Java 8 Date Time API Tutorial : LocalDateTimeа такжеAbstract Class Versus Interface in the JDK 8 Era.

Эта статья также ссылается на некоторые другие источники, такие как:15 Must Read Java 8 Tutorialsа такжеThe Dark Side of Java 8.

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

1. Введение

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

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

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

  • язык
  • переводчик
  • библиотека
  • инструмент
  • Среда выполнения (JVM)

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

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

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

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

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

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

Самое простое лямбда-выражение состоит из списка параметров, разделенных запятыми.->Символы и блоки операторов, например:

Arrays.asList( "a", "b", "d" ).forEach( e -> System.out.println( e ) );скопировать код

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

Arrays.asList( "a", "b", "d" ).forEach( ( String e ) -> System.out.println( e ) );скопировать код

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

Arrays.asList( "a", "b", "d" ).forEach( e -> {
    System.out.print( e );
    System.out.print( e );
} );скопировать код

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

String separator = ",";
Arrays.asList( "a", "b", "d" ).forEach( 
    ( String e ) -> System.out.print( e + separator ) );скопировать код

а также

final String separator = ",";
Arrays.asList( "a", "b", "d" ).forEach( 
    ( String e ) -> System.out.print( e + separator ) );скопировать код

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

Arrays.asList( "a", "b", "d" ).sort( ( e1, e2 ) -> e1.compareTo( e2 ) );скопировать код

а также

Arrays.asList( "a", "b", "d" ).sort( ( e1, e2 ) -> {
    int result = e1.compareTo( e2 );
    return result;
} );скопировать код

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

@FunctionalInterface
public interface Functional {
    void method();
}скопировать код

Но есть одно замечание,Стандартные и статические методыНе нарушает определения функционального интерфейса, поэтому следующий код допустим.

@FunctionalInterface
public interface FunctionalDefaultMethods {
    void method();

    default void defaultMethod() {            
    }        
}скопировать код

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

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

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

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

private interface Defaulable {
    // Interfaces now allow default methods, the implementer may or 
    // may not implement (override) them.
    default String notRequired() { 
        return "Default implementation"; 
    }        
}

private static class DefaultableImpl implements Defaulable {
}

private static class OverridableImpl implements Defaulable {
    @Override
    public String notRequired() {
        return "Overridden implementation";
    }
}скопировать код

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

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

private interface DefaulableFactory {
    // Interfaces now allow static methods
    static Defaulable create( Supplier< Defaulable > supplier ) {
        return supplier.get();
    }
}скопировать код

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

public static void main( String[] args ) {
    Defaulable defaulable = DefaulableFactory.create( DefaultableImpl::new );
    System.out.println( defaulable.notRequired() );

    defaulable = DefaulableFactory.create( OverridableImpl::new );
    System.out.println( defaulable.notRequired() );
}скопировать код

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

Default implementation
Overridden implementationскопировать код

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

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

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

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

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

public static class Car {
    public static Car create( final Supplier< Car > supplier ) {
        return supplier.get();
    }              

    public static void collide( final Car car ) {
        System.out.println( "Collided " + car.toString() );
    }

    public void follow( final Car another ) {
        System.out.println( "Following the " + another.toString() );
    }

    public void repair() {   
        System.out.println( "Repaired " + this.toString() );
    }
}скопировать код

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

final Car car = Car.create( Car::new );
final List< Car > cars = Arrays.asList( car );скопировать код

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

cars.forEach( Car::collide );скопировать код

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

cars.forEach( Car::repair );скопировать код

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

final Car police = Car.create( Car::new );
cars.forEach( police::follow );скопировать код

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

Collided com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197d
Repaired com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197d
Following the com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197dскопировать код

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

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

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

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

package com.javacodegeeks.java8.repeatable.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

public class RepeatingAnnotations {
    @Target( ElementType.TYPE )
    @Retention( RetentionPolicy.RUNTIME )
    public @interface Filters {
        Filter[] value();
    }

    @Target( ElementType.TYPE )
    @Retention( RetentionPolicy.RUNTIME )
    @Repeatable( Filters.class )
    public @interface Filter {
        String value();
    };

    @Filter( "filter1" )
    @Filter( "filter2" )
    public interface Filterable {        
    }

    public static void main(String[] args) {
        for( Filter filter: Filterable.class.getAnnotationsByType( Filter.class ) ) {
            System.out.println( filter.value() );
        }
    }
}скопировать код

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

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

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

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

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

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

package com.javacodegeeks.java8.type.inference;

public class Value< T > {
    public static< T > T defaultValue() { 
        return null; 
    }

    public T getOrDefault( T value, T defaultValue ) {
        return ( value != null ) ? value : defaultValue;
    }
}скопировать код

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

package com.javacodegeeks.java8.type.inference;

public class TypeInference {
    public static void main(String[] args) {
        final Value< String > value = new Value<>();
        value.getOrDefault( "22", Value.defaultValue() );
    }
}скопировать код

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

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

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

package com.javacodegeeks.java8.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.Collection;

public class Annotations {
    @Retention( RetentionPolicy.RUNTIME )
    @Target( { ElementType.TYPE_USE, ElementType.TYPE_PARAMETER } )
    public @interface NonEmpty {        
    }

    public static class Holder< @NonEmpty T > extends @NonEmpty Object {
        public void method() throws @NonEmpty Exception {            
        }
    }

    @SuppressWarnings( "unused" )
    public static void main(String[] args) {
        final Holder< String > holder = new @NonEmpty Holder< String >();        
        @NonEmpty Collection< @NonEmpty String > strings = new ArrayList<>();        
    }
}скопировать код

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

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

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

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

package com.javacodegeeks.java8.parameter.names;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

public class ParameterNames {
    public static void main(String[] args) throws Exception {
        Method method = ParameterNames.class.getMethod( "main", String[].class );
        for( final Parameter parameter: method.getParameters() ) {
            System.out.println( "Parameter: " + parameter.getName() );
        }
    }
}скопировать код

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

Parameter: arg0скопировать код

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

Parameter: argsскопировать код

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

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.1</version>
    <configuration>
        <compilerArgument>-parameters</compilerArgument>
        <source>1.8</source>
        <target>1.8</target>
    </configuration>
</plugin>скопировать код

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

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

4.1 Optional

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

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

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

Optional< String > fullName = Optional.ofNullable( null );
System.out.println( "Full Name is set? " + fullName.isPresent() );        
System.out.println( "Full Name: " + fullName.orElseGet( () -> "[none]" ) ); 
System.out.println( fullName.map( s -> "Hey " + s + "!" ).orElse( "Hey Stranger!" ) );скопировать код

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

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

Full Name is set? false
Full Name: [none]
Hey Stranger!скопировать код

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

Optional< String > firstName = Optional.of( "Tom" );
System.out.println( "First Name is set? " + firstName.isPresent() );        
System.out.println( "First Name: " + firstName.orElseGet( () -> "[none]" ) ); 
System.out.println( firstName.map( s -> "Hey " + s + "!" ).orElse( "Hey Stranger!" ) );
System.out.println();скопировать код

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

First Name is set? true
First Name: Tom
Hey Tom!скопировать код

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

4.2 Streams

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

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

public class Streams  {
    private enum Status {
        OPEN, CLOSED
    };

    private static final class Task {
        private final Status status;
        private final Integer points;

        Task( final Status status, final Integer points ) {
            this.status = status;
            this.points = points;
        }

        public Integer getPoints() {
            return points;
        }

        public Status getStatus() {
            return status;
        }

        @Override
        public String toString() {
            return String.format( "[%s, %d]", status, points );
        }
    }
}скопировать код

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

final Collection< Task > tasks = Arrays.asList(
    new Task( Status.OPEN, 5 ),
    new Task( Status.OPEN, 13 ),
    new Task( Status.CLOSED, 8 ) 
);скопировать код

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

// Calculate total points of all active tasks using sum()
final long totalPointsOfOpenTasks = tasks
    .stream()
    .filter( task -> task.getStatus() == Status.OPEN )
    .mapToInt( Task::getPoints )
    .sum();

System.out.println( "Total points: " + totalPointsOfOpenTasks );скопировать код

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

Total points: 18скопировать код

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

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

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

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

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

// Calculate total points of all tasks
final double totalPoints = tasks
   .stream()
   .parallel()
   .map( task -> task.getPoints() ) // or map( Task::getPoints ) 
   .reduce( 0, Integer::sum );

System.out.println( "Total points (all tasks): " + totalPoints );скопировать код

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

Total points(all tasks): 26.0скопировать код

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

// Group tasks by their status
final Map< Status, List< Task > > map = tasks
    .stream()
    .collect( Collectors.groupingBy( Task::getStatus ) );
System.out.println( map );скопировать код

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

{CLOSED=[[CLOSED, 8]], OPEN=[[OPEN, 5], [OPEN, 13]]}скопировать код

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

// Calculate the weight of each tasks (as percent of total points) 
final Collection< String > result = tasks
    .stream()                                        // Stream< String >
    .mapToInt( Task::getPoints )                     // IntStream
    .asLongStream()                                  // LongStream
    .mapToDouble( points -> points / totalPoints )   // DoubleStream
    .boxed()                                         // Stream< Double >
    .mapToLong( weigth -> ( long )( weigth * 100 ) ) // LongStream
    .mapToObj( percentage -> percentage + "%" )      // Stream< String> 
    .collect( Collectors.toList() );                 // List< String > 

System.out.println( result );скопировать код

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

[19%, 50%, 30%]скопировать код

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

final Path path = new File( filename ).toPath();
try( Stream< String > lines = Files.lines( path, StandardCharsets.UTF_8 ) ) {
    lines.onClose( () -> System.out.println("Done!") ).forEach( System.out::println );
}скопировать код

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

4.3 Date/Time API(JSR 310)

Представлена ​​Java 8Новый API даты и времени (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().

// Get the system clock as UTC offset 
final Clock clock = Clock.systemUTC();
System.out.println( clock.instant() );
System.out.println( clock.millis() );скопировать код

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

2014-04-12T15:19:29.282Z
1397315969360скопировать код

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

// Get the local date and local time
final LocalDate date = LocalDate.now();
final LocalDate dateFromClock = LocalDate.now( clock );

System.out.println( date );
System.out.println( dateFromClock );

// Get the local date and local time
final LocalTime time = LocalTime.now();
final LocalTime timeFromClock = LocalTime.now( clock );

System.out.println( time );
System.out.println( timeFromClock );скопировать код

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

2014-04-12
2014-04-12
11:25:54.568
15:25:54.568скопировать код

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

// Get the local date/time
final LocalDateTime datetime = LocalDateTime.now();
final LocalDateTime datetimeFromClock = LocalDateTime.now( clock );

System.out.println( datetime );
System.out.println( datetimeFromClock );скопировать код

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

2014-04-12T11:37:52.309
2014-04-12T15:37:52.309скопировать код

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

// Get the zoned date/time
final ZonedDateTime zonedDatetime = ZonedDateTime.now();
final ZonedDateTime zonedDatetimeFromClock = ZonedDateTime.now( clock );
final ZonedDateTime zonedDatetimeFromZone = ZonedDateTime.now( ZoneId.of( "America/Los_Angeles" ) );

System.out.println( zonedDatetime );
System.out.println( zonedDatetimeFromClock );
System.out.println( zonedDatetimeFromZone );скопировать код

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

2014-04-12T11:47:01.017-04:00[America/New_York]
2014-04-12T15:47:01.017Z
2014-04-12T08:47:01.017-07:00[America/Los_Angeles]скопировать код

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

// Get duration between two dates
final LocalDateTime from = LocalDateTime.of( 2014, Month.APRIL, 16, 0, 0, 0 );
final LocalDateTime to = LocalDateTime.of( 2015, Month.APRIL, 16, 23, 59, 59 );

final Duration duration = Duration.between( from, to );
System.out.println( "Duration in days: " + duration.toDays() );
System.out.println( "Duration in hours: " + duration.toHours() );скопировать код

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

Duration in days: 365
Duration in hours: 8783скопировать код

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

4.4 Движок JavaScript Nashorn

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

ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName( "JavaScript" );

System.out.println( engine.getClass().getName() );
System.out.println( "Result:" + engine.eval( "function f() { return 1; }; f() + 1;" ) );скопировать код

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

jdk.nashorn.api.scripting.NashornScriptEngine
Result: 2скопировать код

4.5 Base64

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

package com.javacodegeeks.java8.base64;

import java.nio.charset.StandardCharsets;
import java.util.Base64;

public class Base64s {
    public static void main(String[] args) {
        final String text = "Base64 finally in Java 8!";

        final String encoded = Base64
            .getEncoder()
            .encodeToString( text.getBytes( StandardCharsets.UTF_8 ) );
        System.out.println( encoded );

        final String decoded = new String( 
            Base64.getDecoder().decode( encoded ),
            StandardCharsets.UTF_8 );
        System.out.println( decoded );
    }
}скопировать код

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

QmFzZTY0IGZpbmFsbHkgaW4gSmF2YSA4IQ==
Base64 finally in Java 8!скопировать код

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

BASE64不是用来加密的,是BASE64编码后的字符串,全部都是由标准键盘上面的常规字符组成,这样编码后的字符串在网关之间传递不会产生UNICODE字符串不能识别或者丢失的现象。你再仔细研究下EMAIL就会发现其实EMAIL就是用base64编码过后再发送的。然后接收的时候再还原。

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

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

package com.javacodegeeks.java8.parallel.arrays;

import java.util.Arrays;
import java.util.concurrent.ThreadLocalRandom;

public class ParallelArrays {
    public static void main( String[] args ) {
        long[] arrayOfLong = new long [ 20000 ];        

        Arrays.parallelSetAll( arrayOfLong, 
            index -> ThreadLocalRandom.current().nextInt( 1000000 ) );
        Arrays.stream( arrayOfLong ).limit( 10 ).forEach( 
            i -> System.out.print( i + " " ) );
        System.out.println();

        Arrays.parallelSort( arrayOfLong );        
        Arrays.stream( arrayOfLong ).limit( 10 ).forEach( 
            i -> System.out.print( i + " " ) );
        System.out.println();
    }
}скопировать код

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

Unsorted: 591217 891976 443951 424479 766825 351964 242997 642839 119108 552378 
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файл со следующим содержимым:

function f() { 
     return 1; 
}; 

print( f() + 1 );скопировать код

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

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

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

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

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

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

jdeps org.springframework.core-3.0.5.RELEASE.jarскопировать код

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

org.springframework.core-3.0.5.RELEASE.jar -> C:\Program Files\Java\jdk1.8.0\jre\lib\rt.jar
   org.springframework.core (org.springframework.core-3.0.5.RELEASE.jar)
      -> java.io                                            
      -> java.lang                                          
      -> java.lang.annotation                               
      -> java.lang.ref                                      
      -> java.lang.reflect                                  
      -> java.util                                          
      -> java.util.concurrent                               
      -> org.apache.commons.logging                         not found
      -> org.springframework.asm                            not found
      -> org.springframework.asm.commons                    not found
   org.springframework.core.annotation (org.springframework.core-3.0.5.RELEASE.jar)
      -> java.lang                                          
      -> java.lang.annotation                               
      -> java.lang.reflect                                  
      -> 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: Для разработки на уровне предприятия мы также должны обратить внимание на поддержку Java 8 сообществом Spring, вы можете обратиться к этой статье —Список новых функций Java 8, поддерживаемых Spring 4

8. Ссылки