JavaRush /Blogue Java /Random-PT /Recursos do Java 8 – O guia definitivo (parte 2)
0xFF
Nível 9
Донецк

Recursos do Java 8 – O guia definitivo (parte 2)

Publicado no grupo Random-PT
A segunda parte da tradução do artigo Java 8 Features – The ULTIMATE Guide . A primeira parte está aqui (o link pode mudar). Recursos do Java 8 – O guia definitivo (Parte 2) - 1

5. Novos recursos nas bibliotecas Java 8

Java 8 adicionou muitas novas classes e estendeu as existentes para melhor suportar a simultaneidade moderna, programação funcional, data/hora e muito mais.

5.1. Classe Opcional

A famosa NullPointerException é de longe a causa mais comum de falhas em aplicativos Java. Há muito tempo, o excelente projeto Guava do Google foi apresentado Optionalcomo uma solução NullPointerException, evitando assim que o código fosse poluído por verificações nulas e, como resultado, incentivando a escrita de código mais limpo. A classe Guava, inspirada no Google, Optionalagora faz parte do Java 8. OptionalÉ apenas um contêiner: pode conter um valor ou algum tipo Т, ou simplesmente ser nulo. Ele fornece muitos métodos úteis para que verificações nulas explícitas não sejam mais justificadas. Consulte a documentação oficial para obter informações mais detalhadas. Vejamos dois pequenos exemplos de uso Optional: com e sem nulo.
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!" ) );
O método isPresent()retorna verdadeiro se a instância Optionalcontém um valor não nulo e falso caso contrário. O método orElseGet()contém um mecanismo de fallback para o resultado se Optionalcontiver nulo, aceitando funções para gerar um valor padrão. O método map () transforma o valor atual Optionale retorna uma nova instância Optional. O método orElse()é semelhante a orElseGet(), mas em vez de uma função ele assume um valor padrão. Aqui está o resultado deste programa:
Full Name is set? false
Full Name: [none]
Hey Stranger!
Vamos dar uma olhada rápida em outro exemplo:
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();
O resultado será assim:
First Name is set? true
First Name: Tom
Hey Tom!
Para informações mais detalhadas, consulte a documentação oficial .

5.2. Fluxos

A recém- adicionada Stream API ( java.util.stream) apresenta programação de estilo funcional real em Java. É de longe a adição mais abrangente à biblioteca Java e permite que os desenvolvedores Java sejam significativamente mais eficientes e também criem código eficiente, limpo e conciso. A API Stream facilita muito o processamento de coleções (mas não se limitando a elas, como veremos mais adiante). Vamos pegar uma aula simples como exemplo 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 );
        }
    }
}
A tarefa tem algum sentido de pontos (ou pseudo-dificuldades) e pode ser OPEN ou CLOSE . Vamos apresentar uma pequena coleção de problemas para brincar.
final Collection<Task> tasks = Arrays.asList(
    new Task( Status.OPEN, 5 ),
    new Task( Status.OPEN, 13 ),
    new Task( Status.CLOSED, 8 )
);
A primeira questão que pretendemos descobrir é quantos pontos as tarefas OPEN contêm atualmente? Antes do Java 8, a solução usual para isso seria usar um iterador foreach. Mas no Java 8, a resposta são fluxos: uma sequência de elementos que suportam operações agregadas sequenciais e paralelas.
// Подсчет общего количества очков всех активных задач с использованием sum()
final long totalPointsOfOpenTasks = tasks
    .stream()
    .filter( task -> task.getStatus() == Status.OPEN )
    .mapToInt( Task::getPoints )
    .sum();

System.out.println( "Total points: " + totalPointsOfOpenTasks );
E a saída do console será semelhante a:
Total points: 18
Vejamos o que está acontecendo aqui. Primeiro, a coleção de tarefas é convertida em uma representação de streaming. A operação então filterfiltra todas as tarefas com status CLOSED . Na próxima etapa, a operação mapToIntconverte stream Tasks em stream Integers usando um método Task::getPointspara cada instância Task. Por fim, todos os pontos são somados utilizando o método sum, que fornece o resultado final. Antes de passar para os próximos exemplos, há algumas notas sobre threads que você deve ter em mente (mais detalhes aqui ). As operações streamsão divididas em operações intermediárias e finais . As operações intermediárias retornam um novo fluxo. Eles são sempre preguiçosos; ao executar operações intermediárias como filter, eles não realizam realmente a filtragem, mas em vez disso criam um novo fluxo, que, quando concluído, contém os elementos do fluxo original que correspondem ao predicado fornecido. Operações finitas , como forEache sum, podem ser passadas por um fluxo para produzir um resultado ou efeito colateral. Assim que a operação final for concluída, o fluxo será considerado usado e não poderá ser usado novamente. Em quase todos os casos, as operações finais tendem a completar a sua travessia através da fonte de dados subjacente. Outro recurso valioso dos threads é o suporte imediato para processos paralelos. Vejamos este exemplo, que encontra a soma das pontuações de todos os problemas.
// 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 );
Isso é muito semelhante ao primeiro exemplo, exceto que estamos tentando processar todas as tarefas em paralelo e calcular o resultado final usando o método reduce. Aqui está a saída do console:
Total points (all tasks): 26.0
Muitas vezes é necessário agrupar elementos de acordo com um determinado critério. O exemplo demonstra como os threads podem ajudar nisso.
// Группировка задач по их статусу
final Map<Status, List<Task>> map = tasks
    .stream()
    .collect( Collectors.groupingBy( Task::getStatus ) );
System.out.println( map );
A saída do console será a seguinte:
{CLOSED=[[CLOSED, 8]], OPEN=[[OPEN, 5], [OPEN, 13]]}
Para finalizar os exemplos de problemas, vamos calcular a porcentagem geral (ou peso) de cada problema na coleção com base no total de pontos:
// Подсчет веса каждой задачи (How процент от общего количества очков)
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 );
A saída do console será assim:
[19%, 50%, 30%]
Finalmente, como observamos anteriormente, a API Stream não serve apenas para coleções Java. Uma operação de E/S típica, como a leitura de arquivos de texto linha por linha, é uma ótima candidata para usar o processamento de fluxo. Aqui está um pequeno exemplo para provar isso.
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 );
}
O método onConsole, que é chamado em um thread, retorna um thread equivalente com um manipulador privado adicional. O manipulador privado é chamado quando um método close()é chamado em um thread. A API Stream junto com lambdas e métodos de referência junto com métodos padrão e estáticos em Java 8 são a resposta aos paradigmas modernos de desenvolvimento de software. Para informações mais detalhadas, consulte a documentação oficial .

5.3. API de data/hora (JSR 310)

Java 8 traz uma nova aparência ao gerenciamento de data e hora, fornecendo uma nova API de data e hora (JSR 310) . A manipulação de data e hora é um dos piores problemas para desenvolvedores Java. O java.util.Datecumprimento dos padrões java.util.Calendargeralmente não melhorou a situação (talvez até a tenha tornado mais confusa). Foi assim que nasceu o Joda-Time : uma ótima alternativa de API de data/hora para Java . A nova API Date/Time em Java 8 (JSR 310) é fortemente influenciada pelo Joda-Time e tira o melhor proveito dele. O novo pacote java.timecontém todas as classes de data, hora, data/hora, fusos horários, durações e manipulação de tempo . O design da API levou a imutabilidade muito a sério: alterações não são permitidas (uma dura lição aprendida com java.util.Calendar). Se for necessária modificação, uma nova instância da classe correspondente será retornada. Vejamos as principais classes e exemplos de seu uso. A primeira classe Clock, que fornece acesso ao instante, data e hora atuais usando um fuso horário. Clockpode ser usado em vez de System.currentTimeMillis()e TimeZone.getDefault().
// Получить системное время How смещение UTC
final Clock clock = Clock.systemUTC();
System.out.println( clock.instant() );
System.out.println( clock.millis() );
Exemplo de saída do console:
2014-04-12T15:19:29.282Z
1397315969360
Outras novas classes que veremos são LocaleDatee LocalTime. LocaleDatecontém apenas a parte da data sem o fuso horário no sistema de calendário ISO-8601. Conseqüentemente, LocalTimeele contém apenas parte do timecode>.
// получить местную date и время время
final LocalDate date = LocalDate.now();
final LocalDate dateFromClock = LocalDate.now( clock );

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

// получить местную date и время время
final LocalTime time = LocalTime.now();
final LocalTime timeFromClock = LocalTime.now( clock );

System.out.println( time );
System.out.println( timeFromClock );
Exemplo de saída do console:
2014-04-12
2014-04-12
11:25:54.568
15:25:54.568
LocalDateTimeconcatena LocaleDatee LocalTimecontém data e hora, mas sem fuso horário, no sistema de calendário ISO-8601. Um exemplo simples é dado abaixo.
// Get the local date/time
final LocalDateTime datetime = LocalDateTime.now();
final LocalDateTime datetimeFromClock = LocalDateTime.now( clock );

System.out.println( datetime );
System.out.println( datetimeFromClock );
Exemplo de saída do console:
2014-04-12T11:37:52.309
2014-04-12T15:37:52.309
Caso você precise de data/hora para um fuso horário específico, ZonedDateTime. Ele contém a data e a hora no sistema de calendário ISO-8601. Aqui estão alguns exemplos para diferentes fusos horários.
// Получение даты/времени для временной зоны
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 );
Exemplo de saída do console:
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]
E finalmente, vamos dar uma olhada na classe Duration: intervalo de tempo em segundos e nanossegundos. Isto torna o cálculo entre duas datas muito simples. Vamos ver como fazer isso:
// Получаем разницу между двумя датами
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() );
O exemplo acima calcula a duração (em dias e horas) entre duas datas, 16 de abril de 2014 e 16 de abril de 2015 . Aqui está um exemplo da saída do console:
Duration in days: 365
Duration in hours: 8783
A impressão geral da nova data/hora no Java 8 é muito, muito positiva. Em parte porque as mudanças são baseadas em uma base testada em batalha (Joda-Time), em parte porque desta vez a questão foi seriamente reconsiderada e as vozes dos desenvolvedores foram ouvidas. Para obter detalhes, consulte a documentação oficial .

5.4. Mecanismo JavaScript Nashorn

Java 8 vem com o novo mecanismo Nashorn JavaScript , que permite desenvolver e executar certos tipos de aplicativos JavaScript na JVM. O mecanismo Nashorn JavaScript é simplesmente outra implementação de javax.script.ScriptEngine que segue o mesmo conjunto de regras para permitir a interação de Java e JavaScript. Aqui está um pequeno exemplo.
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;" ) );
Exemplo de saída do console:
jdk.nashorn.api.scripting.NashornScriptEngine
Result: 2

5.5. Base64

Finalmente, o suporte para codificação Base64 chegou à biblioteca padrão Java com o lançamento do Java 8. É muito fácil de usar, o exemplo demonstra isso.
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 );
    }
}
A saída do console do programa mostra texto codificado e decodificado:
QmFzZTY0IGZpbmFsbHkgaW4gSmF2YSA4IQ==
Base64 finally in Java 8!
Existem também classes para codificadores/decodificadores compatíveis com URL, bem como codificadores/decodificadores compatíveis com MIME ( Base64.getUrlEncoder()/ Base64.getUrlDecoder(), Base64.getMimeEncoder()/ Base64.getMimeDecoder()).

5.6. Matrizes Paralelas

A versão Java 8 adiciona muitos novos métodos para processamento paralelo de arrays. Talvez o mais importante deles seja o parallelSort(), que pode acelerar bastante a classificação em máquinas com vários núcleos. O pequeno exemplo abaixo demonstra a nova família de métodos ( parallelXxx) em ação.
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();
    }
}
Este pequeno trecho de código usa um método parallelSetAll()para preencher um array com 20.000 valores aleatórios. Depois disso é aplicado parallelSort(). O programa imprime os primeiros 10 elementos antes e depois da classificação para mostrar que o array está realmente classificado. Um exemplo de saída de programa pode ser assim (observe que os elementos do array são aleatórios).
Unsorted: 591217 891976 443951 424479 766825 351964 242997 642839 119108 552378
Sorted: 39 220 263 268 325 607 655 678 723 793

5.7. Paralelismo

Novos métodos foram adicionados à classe java.util.concurrent.ConcurrentHashMappara oferecer suporte a operações agregadas com base nos objetos de fluxo e expressões lambda recém-adicionados. Além disso, novos métodos foram adicionados à classe java.util.concurrent.ForkJoinPoolpara suportar pooling compartilhado (veja também nosso curso gratuito sobre simultaneidade Java ). Uma nova classe java.util.concurrent.locks.StampedLockfoi adicionada para fornecer bloqueio baseado em capacidade com três modos de acesso para controle de leitura/gravação (pode ser considerada uma alternativa melhor à não tão boa java.util.concurrent.locks.ReadWriteLock). Novas classes que foram adicionadas ao pacote java.util.concurrent.atomic:
  • Acumulador Duplo
  • DoubleAdder
  • Acumulador Longo
  • LongAdder

6. Novos recursos no Java Runtime Environment (JVM)

A área PermGenfoi desativada e substituída pelo Metaspace (JEP 122). Opções de JVM -XX:PermSizee -XX:MaxPermSizeforam substituídas por -XX:MetaSpaceSizee -XX:MaxMetaspaceSizerespectivamente.

7. Conclusão

O futuro está aqui: Java 8 avançou sua plataforma ao fornecer recursos que permitem que os desenvolvedores sejam mais produtivos. Ainda é muito cedo para migrar os sistemas de produção para o Java 8, mas a adoção deve começar a crescer lentamente nos próximos meses. No entanto, agora é a hora de começar a preparar sua base de código para compatibilidade com o Java 8 e estar pronto para incorporar as alterações do Java 8 quando ele estiver seguro e estável o suficiente. Como prova da aceitação do Java 8 pela comunidade, a Pivotal lançou recentemente o Spring Framework com suporte de produção para Java 8 . Você pode fornecer sua opinião sobre os novos recursos interessantes do Java 8 nos comentários.

8. Fontes

Alguns recursos adicionais que discutem vários aspectos dos recursos do Java 8 em profundidade:
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION