JavaRush /Blogue Java /Random-PT /De 8 a 13: uma visão completa das versões Java. Parte 1

De 8 a 13: uma visão completa das versões Java. Parte 1

Publicado no grupo Random-PT
Gatinhos, olá a todos)) Então, hoje estamos em 2020 e falta muito pouco para o lançamento do Java 14. Você deve esperar a versão finalizada em 17 de março, analisaremos o que há de novo e interessante lá depois do fato, mas hoje gostaria de refrescar minha memória nas versões anteriores do Java. Que novidades eles nos trouxeram? Vamos dar uma olhada. Vamos começar a revisão com Java 8, pois ele ainda é bastante relevante e é utilizado na maioria dos projetos. De 8 a 13: uma visão completa das versões Java.  Parte 1 - 1Anteriormente, novas versões eram lançadas a cada 3-5 anos, mas recentemente a Oracle adotou uma abordagem diferente - “novo Java a cada seis meses”. E assim, a cada seis meses vemos o lançamento de recursos. Quer seja bom ou ruim, todo mundo vê isso de forma diferente. Por exemplo, não gosto muito disso, pois as novas versões não trazem muitas novidades, mas ao mesmo tempo, as versões estão crescendo como cogumelos depois da chuva. Pisquei algumas vezes em um projeto com Java 8, e o Java 16 já foi lançado (mas quando raramente sai, novos recursos se acumulam, e no final esse evento é tão esperado, como um feriado: todo mundo está discutindo o novidades e você não pode deixar de lado). Então vamos começar!

Java 8

Interface Funcional

O que é isso? Uma interface funcional é uma interface que contém um método não implementado (abstrato). @FunctionalInterface é uma anotação opcional colocada acima dessa interface. Necessário verificar se atende aos requisitos de uma interface funcional (possuindo apenas um método abstrato). Mas, como sempre, temos algumas ressalvas: os métodos padrão e estáticos não se enquadram nesses requisitos. Portanto, pode haver vários desses métodos + um abstrato, e a interface será funcional. Também pode conter métodos da classe Object que não afetam a definição da interface como funcional. Acrescentarei algumas palavras sobre métodos padrão e estáticos:
  1. Os métodos com o modificador padrão permitem adicionar novos métodos às interfaces sem interromper a implementação existente.

    public interface Something {
      default void someMethod {
          System.out.println("Some text......");
      }
    }

    Sim, sim, adicionamos o método implementado à interface e, ao implementar esse método, você não pode substituí-lo, mas usá-lo como herdado. Mas se uma classe implementar duas interfaces com um determinado método, teremos um erro de compilação, e se ela implementar interfaces e herdar uma classe com um determinado método idêntico, o método da classe pai irá se sobrepor aos métodos da interface e a exceção não funcionará.

  2. métodos estáticos em uma interface funcionam da mesma forma que métodos estáticos em uma classe. Não se esqueça: você não pode herdar métodos estáticos , assim como não pode chamar um método estático de uma classe descendente.

Então, mais algumas palavras sobre interfaces funcionais e vamos em frente. Aqui estão as principais listas de IFs (o restante são suas variedades):

    Predicado - aceita algum valor T como argumento e retorna booleano.

    Exemplo:boolean someMethod(T t);

  • Consumidor - recebe um argumento do tipo T, não retorna nada (void).

    Exemplo:void someMethod(T t);

  • Fornecedor - não recebe nada como entrada, mas retorna algum valor T.

    Exemplo:T someMethod();

  • Função - recebe um parâmetro do tipo T como entrada e retorna um valor do tipo R.

    Exemplo:R someMethod(T t);

  • UnaryOperator - recebe um argumento T e retorna um valor do tipo T.

    Exemplo:T someMethod(T t);

Fluxo

Streams são uma forma de lidar com estruturas de dados em um estilo funcional. Normalmente são coleções (mas você pode usá-las em outras situações menos comuns). Em uma linguagem mais compreensível, Stream é um fluxo de dados que processamos como se estivéssemos trabalhando com todos os dados ao mesmo tempo, e não com força bruta, como acontece com for-each. Vejamos um pequeno exemplo. Digamos que temos um conjunto de números que queremos filtrar (menos de 50), aumentar em 5 e enviar os primeiros 4 números dos restantes para o console. Como teríamos feito isso antes:
List<Integer> list = Arrays.asList(46, 34, 24, 93, 91, 1, 34, 94);

int count = 0;

for (int x : list) {

  if (x >= 50) continue;

  x += 5;

  count++;

  if (count > 4) break;

  System.out.print(x);

}
Não parece haver muito código e a lógica já é um pouco confusa. Vamos ver como ficará usando o stream:
Stream.of(46, 34, 24, 93, 91, 1, 34, 94)

      .filter(x -> x < 50)

      .map(x -> x + 5)

      .limit(4)

      .forEach(System.out::print);
Os streams simplificam muito a vida, reduzindo a quantidade de código e tornando-o mais legível. Para quem quiser se aprofundar neste tema, aqui está um bom (diria até excelente) artigo sobre este tema .

lambda

Talvez a característica mais importante e esperada seja o aparecimento de lambdas. O que é lambda? Este é um bloco de código que pode ser repassado para diferentes locais para que possa ser executado posteriormente quantas vezes forem necessárias. Parece muito confuso, não é? Simplificando, usando lambdas, você pode implementar um método de interface funcional (uma espécie de implementação de uma classe anônima):
Runnable runnable = () -> { System.out.println("I'm running !");};

new Thread(runnable).start();
Implementamos o método run() rapidamente e sem burocracia desnecessária. E sim: Runnable é uma interface funcional. Também uso lambdas ao trabalhar com streams (como nos exemplos com streams acima). Não iremos muito fundo, já que podemos mergulhar bem fundo, vou deixar alguns links para que os caras que ainda são escavadores de coração possam cavar mais fundo:

para cada

Java 8 possui um novo foreach, que funciona com um fluxo de dados como um fluxo. Aqui está um exemplo:
List<Integer> someList = Arrays.asList(1, 3, 5, 7, 9);

someList.forEach(x -> System.out.println(x));
(análogo a someList.stream().foreach(…))

Referência de método

Métodos de referência são uma sintaxe nova e útil projetada para fazer referência a métodos existentes ou construtores de classes ou objetos Java por meio de :: As referências de método vêm em quatro tipos:
  1. Link para o designer:

    SomeObject obj = SomeObject::new

  2. Referência de método estático:

    SomeObject::someStaticMethod

  3. Uma referência a um método não estático de um objeto de um determinado tipo:

    SomeObject::someMethod

  4. Uma referência a um método regular (não estático) de um objeto específico

    obj::someMethod

Muitas vezes, as referências de métodos são usadas em fluxos em vez de lambdas (os métodos de referência são mais rápidos que os lambdas, mas são inferiores em legibilidade).
someList.stream()

        .map(String::toUpperCase)

      .forEach(System.out::println);
Para quem deseja mais informações sobre métodos de referência:

Tempo da API

Existe uma nova biblioteca para trabalhar com datas e horas - java.time. De 8 a 13: uma visão completa das versões Java.  Parte 1 - 2A nova API é semelhante a qualquer Joda-Time. As seções mais significativas desta API são:
  • LocalDate é uma data específica, por exemplo - 09/01/2010;
  • LocalTime - horário levando em consideração o fuso horário - 19:45:55 (análogo ao LocalDate);
  • LocalDateTime - combinação LocalDate + LocalTime - 2020-01-04 15:37:47;
  • ZoneId – representa fusos horários;
  • Relógio - usando este tipo você pode acessar a hora e a data atuais.
Aqui estão alguns artigos realmente interessantes sobre este tópico:

Opcional

Esta é uma nova classe no pacote java.util , um wrapper de valor cujo truque é que ele também pode conter null com segurança . Recebendo opcional: Se passarmos nullOptional<String> someOptional = Optional.of("Something"); em Optional.of , obteremos nosso NullPointerException favorito . Para tais casos eles usam: - neste método você não precisa ter medo de nulo. A seguir, crie um Opcional inicialmente vazio: Para verificar se está vazio, use: retornará verdadeiro ou falso para nós. Execute uma determinada ação se houver um valor e não faça nada se não houver valor: Um método reverso que retorna o valor passado se Opcional estiver vazio (uma espécie de plano de backup): Você pode continuar por muito, muito tempo ( felizmente, Opcional adicionou métodos com ambas as mãos generosas), mas não vamos nos alongar sobre isso. É melhor deixar alguns links para começar: Optional<String> someOptional = Optional.ofNullable("Something");Optional<String> someOptional = Optional.empty();someOptional.isPresent();someOptional.ifPresent(System.out::println);System.out.println(someOptional.orElse("Some default content")); Examinamos as inovações mais famosas do Java 8 - isso não é tudo. Se quiser saber mais, deixei isso para você:

Java 9

Assim, em 21 de setembro de 2017, o mundo viu o JDK 9. Este Java 9 vem com um rico conjunto de recursos. Embora não existam novos conceitos de linguagem, as novas APIs e comandos de diagnóstico certamente serão do interesse dos desenvolvedores. De 8 a 13: uma visão completa das versões Java.  Parte 1 - 4

JShell (REPL - loop de leitura-avaliação-impressão)

Esta é uma implementação Java de um console interativo que é usado para testar funcionalidades e usar diferentes construções no console, como interfaces, classes, enums, operadores, etc. Para iniciar o JShell, você só precisa escrever jshell no terminal. Então podemos escrever o que nossa imaginação permitir: De 8 a 13: uma visão completa das versões Java.  Parte 1 - 5Usando JShell, você pode criar métodos de nível superior e usá-los na mesma sessão. Os métodos funcionarão como métodos estáticos, exceto que a palavra-chave estática pode ser omitida.Leia mais no manual Java 9 REPL (JShell) .

Privado

A partir da versão 9 do Java, temos a oportunidade de usar métodos privados em interfaces (métodos padrão e estáticos, pois simplesmente não podemos substituir outros devido ao acesso insuficiente). private static void someMethod(){} try-with-resources A capacidade de lidar com exceções Try-With-Resources foi atualizada:
BufferedReader reader = new BufferedReader(new FileReader("....."));
  try (reader2) {
  ....
}

Modularidade ( Jigsaw )

Um módulo é um grupo de pacotes e recursos relacionados junto com um novo arquivo descritor de módulo. Essa abordagem é usada para afrouxar o acoplamento do código. O acoplamento fraco é um fator chave para a manutenção e extensibilidade do código. A modularidade é implementada em diferentes níveis:
  1. Linguagem de programação.
  2. Máquina virtual.
  3. API java padrão.
JDK 9 vem com 92 módulos: podemos usá-los ou criar os nossos próprios. Aqui estão alguns links para uma visão mais aprofundada:

Coleção Imutável

No Java 9, tornou-se possível criar e preencher uma coleção com uma linha, ao mesmo tempo que a tornava imutável (anteriormente, para criar uma coleção imutável, precisávamos criar uma coleção, preenchê-la com dados e chamar um método, por exemplo, Collections.unmodifiableList). Um exemplo de tal criação: List someList = List.of("first","second","third");

Outras inovações:

  • Opcional expandido (novos métodos adicionados);
  • as interfaces ProcessHandle e ProcessHandle surgiram para controlar as ações do sistema operacional;
  • G1 - coletor de lixo padrão;
  • Cliente HTTP com suporte para protocolos HTTP/2 e WebSocket;
  • Fluxo expandido;
  • adicionada estrutura de API Reactive Streams (para programação reativa);
Para uma imersão mais completa em Java 9, aconselho a leitura:

Java 10

Então, seis meses após o lançamento do Java 9, em março de 2018 (lembro-me como se fosse ontem), o Java 10 entrou em cena. De 8 a 13: uma visão completa das versões Java.  Parte 1 - 6

var

Agora não precisamos fornecer um tipo de dados. Marcamos a mensagem como var e o compilador determina o tipo da mensagem pelo tipo do inicializador presente à direita. Este recurso está disponível apenas para variáveis ​​locais com inicializador: não pode ser utilizado para argumentos de métodos, tipos de retorno, etc., pois não existe inicializador para poder definir o tipo. Exemplo var (para o tipo String):
var message = "Some message…..";
System.out.println(message);
var não é uma palavra-chave: é essencialmente um nome de tipo reservado, assim como int . O benefício do var é grande: as declarações de tipo ocupam muita atenção sem trazer nenhum benefício, e esse recurso economizará tempo. Mas, ao mesmo tempo, se uma variável for obtida a partir de uma longa cadeia de métodos, o código se tornará menos legível, pois não fica imediatamente claro que tipo de objeto está ali. Dedicado a quem deseja se familiarizar mais com esta funcionalidade:

Compilador JIT (GraalVM)

Sem mais delongas, deixe-me lembrá-lo de que quando você executa o comando javac, o aplicativo Java é compilado do código Java no bytecode JVM, que é a representação binária do aplicativo. Mas um processador de computador normal não pode simplesmente executar o bytecode JVM. Para que seu programa JVM funcione, você precisa de outro compilador para esse bytecode, que é convertido em código de máquina que o processador já é capaz de utilizar. Comparado ao javac, este compilador é muito mais complexo, mas também produz código de máquina de maior qualidade. Atualmente, o OpenJDK contém a máquina virtual HotSpot, que por sua vez possui dois compiladores JIT principais. O primeiro, C1 ( compilador cliente ), é projetado para operação em maior velocidade, mas a otimização do código sofre. O segundo é C2 (compilador de servidor). A velocidade de execução é prejudicada, mas o código fica mais otimizado. Quando qual deles é usado? C1 é ótimo para aplicativos de desktop onde longas pausas JIT são indesejáveis, e C2 é ótimo para programas de servidor de longa execução, onde gastar mais tempo na compilação é bastante suportável. Compilação multinível é quando a compilação passa primeiro por C1 e o resultado passa por C2 (usado para maior otimização). GraalVM é um projeto criado para substituir completamente o HotSpot. Podemos pensar no Graal como vários projetos relacionados: um novo compilador JIT para HotSpot e uma nova máquina virtual poliglota. A peculiaridade deste compilador JIT é que ele é escrito em Java. A vantagem do compilador Graal é a segurança, ou seja, não travamentos, mas exceções, e não vazamentos de memória. Também teremos um bom suporte IDE e poderemos usar depuradores, criadores de perfil ou outras ferramentas convenientes. Além disso, o compilador pode muito bem ser independente do HotSpot e será capaz de criar uma versão mais rápida de si mesmo, compilada em JIT. Para escavadores:

Paralelo G1

O coletor de lixo G1 é certamente legal, sem dúvida, mas também tem um ponto fraco: ele executa um ciclo de GC completo de thread único. No momento em que você precisa de todo o poder de hardware possível para encontrar objetos não utilizados, estamos limitados a um único thread. Isso foi corrigido no Java 10. Agora o GC funciona com todos os recursos que adicionamos a ele (ou seja, ele se torna multithread). Para conseguir isso, os desenvolvedores da linguagem melhoraram o isolamento das principais fontes do GC, criando uma interface limpa e agradável para o GC. Os desenvolvedores dessa fofura, OpenJDK, tiveram que limpar especificamente o dump no código, não apenas para simplificar ao máximo a criação de novos GCs, mas também para possibilitar a desativação rápida de GCs desnecessários do assembly. Um dos principais critérios para o sucesso é a ausência de redução na velocidade operacional após todas essas melhorias. Vejamos também: Outras inovações:
  1. Uma interface limpa do coletor de lixo é introduzida. Isso melhora o isolamento do código-fonte de diferentes coletores de lixo, possibilitando a integração de coletores alternativos de forma rápida e fácil;
  2. Combinando fontes JDK em um repositório;
  3. As coleções receberam um novo método - copyOf (Collection) , que retorna uma cópia imutável desta coleção;
  4. Opcional (e suas variantes) possui um novo método .orElseThrow() ;
  5. A partir de agora, as JVMs estão cientes de que estão sendo executadas em um contêiner Docker e recuperarão a configuração específica do contêiner em vez de consultar o próprio sistema operacional.
Aqui estão mais alguns materiais para uma introdução mais detalhada ao Java 10: Eu costumava ficar muito confuso com o fato de algumas versões do Java serem chamadas de 1.x. Gostaria de deixar claro: as versões Java anteriores à 9 simplesmente tinham um esquema de nomenclatura diferente. Por exemplo, Java 8 também pode ser chamado de 1.8 , Java 5 - 1.5 , etc. E agora vemos que com a transição para as versões do Java 9, o esquema de nomenclatura também mudou e as versões Java não são mais prefixadas com 1.x . Este é o fim da primeira parte: examinamos os novos recursos interessantes do Java 8-10. Vamos continuar conhecendo as novidades no próximo post .
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION