Автор: Ду Ци [перевод]
Оригинальная ссылка: 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 ушло много времени и усилий сообщества, и, наконец, была найдена компромиссная реализация, позволяющая добиться лаконичной и компактной языковой структуры. Самое простое лямбда-выражение состоит из списка параметров, разделенных запятыми.->Символы и блоки операторов, например:
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::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Тип заявления:
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.TYPEUSERиElementType.TYPEPARAMETERЭто две новые аннотации, добавленные в Java 8 для описания сценариев использования аннотаций. Соответствующие изменения также были внесены в язык Java для распознавания этих новых аннотаций.
3. Новые возможности компилятора Java
3.1 Название параметра
Чтобы получить имена параметров метода в программе Java во время выполнения, программисты старшего поколения должны были использовать другой метод, например, библиотеку Paranamer. 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Проверяйте наличие загрязнения, чтобы разработчики могли писать более чистый код. Java 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, позволяющее разработчикам писать более эффективный, лаконичный и компактный код.
Stream API значительно упрощает операции с коллекциями (позже мы увидим больше, чем просто коллекции).Давайте сначала посмотрим на этот класс под названием 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 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().
// 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. Механизм Nashorn 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
В официальную библиотеку Java 8 добавлена поддержка кодировки Base64, так что кодирование 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()).
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-файл в качестве входных данных и выводит зависимости на консоль.
Мы можем использовать jedps для анализа библиотеки 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: Для разработки на уровне предприятия мы также должны обратить внимание на поддержку сообщества Spring для Java 8, вы можете обратиться к этой статье — список новых функций Java 8, поддерживаемых Spring 4.
БОЛЬШЕ | Другие интересные статьи