De uma maravilhosa entrevista sobre Habré: “Simon Ritter é uma pessoa que trabalhou com Java desde o início e continua fazendo isso como diretor técnico adjunto da Azul, uma empresa que trabalha na máquina virtual Zing JVM e um dos melhores coletores de lixo, C4 (Continuously Concurrent Compacting Colecionador) »
Abaixo está uma tradução de seu artigo sobre os novos recursos do JDK 12 e algumas dificuldades que você pode encontrar ao migrar para uma nova compilação.
Escrevi várias postagens no blog que listam todas as alterações para cada uma das versões mais recentes do Java ( JDK 10 , JDK 11 ). Agora explorarei o lado obscuro do JDK 12, focando em algumas das armadilhas que podem causar problemas se você quiser portar o aplicativo para esta versão.

O JDK 12 tem o menor número de novos recursos de todos os lançamentos de Java até o momento (contei 109 no JDK 10 e 90 no JDK 11). Isso não é ruim - devido aos ciclos de lançamento, algumas versões conterão mais alterações e outras menos.
Vou dividir novas funções em áreas lógicas óbvias: Java, bibliotecas, JVMs e outras funções JDK.
Alterações de idioma
A função que eu (e presumo que muitas outras pessoas) considerará a mais visível no JDK 12 é a nova instrução switch ( JEP 325 ). Essa também é a primeira alteração de idioma a ser usada como uma função para "visualizar". A ideia de "pré-visualização" foi introduzida no início de 2018 como parte do JEP 12 . Essa é essencialmente uma maneira de habilitar versões beta de novos recursos usando opções de linha de comando. Usando a visualização, ainda é possível fazer alterações com base no feedback do usuário e, na pior das hipóteses, remover completamente uma função se não tiver sido recebida corretamente. A chave para visualizar funções é que elas não estão incluídas na especificação Java SE. Sobre a nova opção, há uma tradução muito boa em Habré.
No JDK 12, uma opção se tornou uma expressão que avalia seu "conteúdo" para produzir um resultado. Explicarei imediatamente que isso não afeta a compatibilidade com versões anteriores. Portanto, você não precisa alterar nenhum código que use o switch como operador.
Vou usar o exemplo do JEP, pois é simples e claro:
Interruptor antigoint numLetters; switch (day) { case MONDAY: case FRIDAY: case SUNDAY: numLetters = 6; break; case TUESDAY: numLetters = 7; break; case THURSDAY: case SATURDAY: numLetters = 8; break; case WEDNESDAY: numLetters = 9; break; default: throw new IllegalStateException("Huh? " + day); }
Como você pode ver, numLetters
o dia da semana para o nome da variável day
, em seguida, atribuímos o valor numLetters
. Agora que switch é um operador, podemos fazer a atribuição uma vez (reduzindo significativamente a probabilidade de um código incorreto) usando o resultado da instrução switch:
int numLetters = switch (day) { case MONDAY, FRIDAY, SUNDAY -> 6; case TUESDAY -> 7; case THURSDAY, SATURDAY -> 8; case WEDNESDAY -> 9; default -> throw new IllegalStateException("Huh? " + day); };
Você notará rapidamente duas alterações de sintaxe. Os desenvolvedores do OpenJDK encontraram uma função de sintaxe pouco conhecida chamada lista separada por vírgula. Além disso, o operador de expressão lambda ->
facilita o retorno do valor. Você ainda pode usar a break
com um valor se você realmente deseja isso. Existem vários outros detalhes sobre esse recurso, mas provavelmente é mais fácil ler o JEP.
Bibliotecas
Há uma mudança que eu acho muito útil. Há também vários secundários.
colecionador
A API do Streams, como sempre, possui um novo Collector, fornecido pela classe de utilitário Collectors. Um novo coletor pode ser obtido usando o método teeing()
. O colecionador de tee usa três argumentos: dois colecionadores e uma bifuncional. Para entender o trabalho deste colecionador, recomendo este artigo sobre Habré .
Para entender como ele faz isso, desenhei um diagrama:

Todos os valores do fluxo de entrada são passados para cada coletor. O resultado de cada coletor é passado como argumento para o BiFunction e gerando o resultado final.
Um exemplo simples é calcular o valor médio (sim, eu sei que já existem coletores para isso, como averagingInt()
, mas este é um exemplo simples para ajudar a entender o conceito).
double average = Stream.of(1, 4, 2, 7, 4, 6, 5) .collect(teeing( summingDouble(i -> i), counting(), (sum, n) -> sum / n) );
O primeiro coletor calcula a soma do fluxo de entrada e o segundo - o número de elementos. BiFunction divide a soma pelo número de elementos para obter o valor médio.
java.io
InputStream skipNBytes(long n)
- ignora e descarta exatamente n bytes do fluxo de entrada InputStream. Se n for zero ou menos, os bytes não serão ignorados.
java.lang
Um novo pacote apareceu, java.lang.constant, que faz parte da constante JVM API, JEP 334 .
Cada arquivo de classe Java possui um conjunto persistente que armazena operandos para instruções de bytecode na classe. É difícil para os desenvolvedores manipular arquivos de classe devido a problemas ao carregar as classes. A API da JVM constante fornece tipos de referência simbólicos para descrever cada forma de uma constante (classe, constante carregável, MethodHandle
, constante MethodType
, constante MethodType
).
Também influenciou várias outras classes. Todas as seguintes classes agora têm um método describeConstable()
:
- Class
- Duplo
- Enum
- Flutuar
- Inteiro
- Longo
- String
- Methodhandle
- MethodType
- Varhandle
Como britânico, acho muito engraçado. O termo Constable, describeConstable
usado desde o século 11, e é assim que frequentemente nos referimos a policiais. É também o nome do famoso artista do século XVIII, John Constable. Isso me faz pensar se o método describeTurner()
estará em uma versão futura. Obviamente, nesse caso, é uma abreviação da Constant Table
, não relacionada a um policial ou pintor de paisagens.
As seguintes classes agora incluem o método resolveConstantDesc()
:
- Duplo
- Enum.EnumDesc
- Flutuar
- Inteiro
- Longo
- String
java.lang.Character
As classes internas foram atualizadas para incluir novos blocos Unicode. Eu sempre gosto de ver o que as pessoas encontraram para adicionar ao Unicode, aqui estão alguns exemplos:
- Símbolos de xadrez
- Números maias
- Sogdian é uma língua iraniana oriental que não era mais usada no século 11.
- O velho Sogdian é uma versão mais antiga (e, eu suspeito, ainda mais limitada) do Sogdian
java.lang.Class
arrayType()
retorna Class
para o tipo da matriz cujo tipo de componente é descrito por esta Class
. Isso pode ser verificado usando jshell
:
jshell> (new String[2]).getClass().getName() $11 ==> "[Ljava.lang.String;" jshell> (new String[2]).getClass().arrayType() $12 ==> class [[Ljava.lang.String; jshell> "foo".getClass().arrayType() $15 ==> class [Ljava.lang.String;
Não sei ao certo qual é o significado desse método, pois tudo o que faz é adicionar uma Class
ao tipo que essa classe representa.
componentType()
, o mesmo que getComponentType()
. A pergunta implora - por que adicionar um método redundante?
descriptorString()
- novamente, retorna o mesmo resultado que getName()
. No entanto, é necessário porque agora a Class
implementa a interface TypeDescriptor
associada à nova API constante da JVM.
lava.lang.String
indent()
- adiciona uma série de espaços à esquerda em uma string. Se o parâmetro for negativo, esse número de espaços à esquerda será removido (se possível).
transform()
- aplica a função fornecida a uma string. O resultado pode não ser uma sequência.
java.lang.invoke
VarHandle
agora tem toString()
para retornar uma descrição compacta.
java.net.SecureCacheResponse
e java.net.ssl.HttpsConnection
têm um novo método, getSSLSession()
que retorna Optional
contendo o SSLSession
usado na conexão.
java.nio.files
A classe Files
possui um novo método, mismatch()
, que localiza e retorna a posição do primeiro byte de incompatibilidade no conteúdo de dois arquivos, ou -1L, se não houver incompatibilidade.
java.text
Há uma nova classe CompactNumberFormat
. Esta é uma subclasse de NumberFormat
que formata um número decimal no formato compacto. Um exemplo de um formato compacto - 1M
vez de 1000000
, portanto - requer dois em vez de nove caracteres. NumberFormat
e java.text.spi.NumberFormatProvider
foram estendidos para incluir o novo método getCompactNumberInstance()
. Há também uma nova enumeração NumberFormatStyle
que tem dois significados: LONG e SHORT.
java.util.concurrent
O CompletionStage agora inclui vários formulários sobrecarregados com três métodos:
- excepcionalmenteAsync
- excepcionalmenteCompor
- exceptionallyComposeAsync
Esses métodos expandem as possibilidades de criar um novo CompletionStage
partir de um existente, CompletionStage
, se o atual terminar com uma exceção. Verifique a documentação da API para obter detalhes.
javax.crypto
A classe Cipher
possui um novo toString()
que retorna uma sequência que contém a transformação, modo e provedor de Cipher
.
javax.naming.ldap.spi
Este é um novo pacote no JDK 12 e contém duas classes: LdapDnsProvider
, que é a classe de provedor para pesquisas de DNS durante operações LDAP, e LdapDnsProviderResults
que encapsula o resultado da pesquisa de DNS para o URL LDAP.
Swing
Swing ainda está sendo atualizado! Sim, o filechooser.FileSystemView
agora possui um novo método getChooserShortcutPanelFiles()
. Ele retorna uma matriz de arquivos representando os valores a serem exibidos por padrão na barra de atalhos de seleção de arquivos.
Mudanças na JVM
JEP 189: Shenandoah : Coletor de Lixo com Pouco Tempo de Pausa
Shenandoah é um projeto de pesquisa anunciado pela Red Hat em 2014 que se concentra nos requisitos de aplicativos de baixa latência para gerenciamento de memória na JVM. Seus objetivos são um tempo de pausa máximo de 1 a 10 ms para um monte de mais de 20 GB ( portanto, não se destina a pequenas aplicações - como um dos desenvolvedores do Shenandoah respondeu , não é assim e faz um excelente trabalho com aplicativos pequenos). Esse coletor foi projetado para funcionar em paralelo com os encadeamentos de aplicativos, para evitar os problemas que vemos na maioria dos coletores de lixo.
Essa alteração visa melhorar o comportamento do coletor G1 quando atingir a meta de atraso definida. G1 divide o espaço de heap (antigo e antigo) em regiões. A ideia é que, na geração antiga, você não precise coletar lixo em uma operação. Quando o G1 precisa coletar lixo, ele seleciona as regiões que define. Isso é chamado de kit de coleta. Antes do JDK 12, quando o trabalho começava no set, todo o trabalho precisava ser concluído, em essência, como uma operação atômica. O problema era que, às vezes, devido a alterações no uso do espaço de heap do aplicativo, o conjunto de coleta era muito grande e levava muito tempo para ser coletado, o que resultava no tempo de pausa não atingido.
No JDK 12, se G1 identificar essa situação, ele interromperá a coleta de dados até a metade, se isso não afetar a capacidade do aplicativo de continuar alocando espaço para novos objetos. O efeito líquido de G1 será melhor quando um curto período de pausa for alcançado.
Essa é outra melhoria de desempenho para o G1, mas outra está relacionada à maneira como a JVM interage com o restante do sistema. Obviamente, é necessária memória para o heap da JVM e, na inicialização, solicita memória do alocador de memória virtual do sistema operacional. Quando o aplicativo é iniciado, pode haver momentos em que a quantidade de memória necessária para o heap cai e parte da memória alocada pode ser retornada ao sistema operacional para uso por outros aplicativos.
G1 já faz isso, mas só pode fazer isso em um de dois lugares. Em primeiro lugar, durante uma coleção completa e, em segundo lugar, durante um dos ciclos paralelos. G1 tenta não fazer uma coleção completa e, com pouco uso de memória, pode haver períodos significativos entre os ciclos de coleção. Isso leva ao fato de que o G1 pode manter uma memória fixa por um longo tempo.
No JDK 12, o G1 tentará periodicamente continuar ou executar um loop paralelo enquanto o aplicativo estiver inativo para determinar o uso geral do heap Java. A memória não utilizada pode ser devolvida ao sistema operacional de maneira mais oportuna e previsível.
O novo sinalizador de linha de comando -XX:G1PeriodicGCInterval
pode ser usado para definir o número de milissegundos entre as verificações.
Esse recurso levará a um uso mais conservador da memória da JVM para aplicativos que ficaram inativos por longos períodos de tempo.
Outros novos recursos do JDK
O JMH (Java Microbenchmarking Harness) foi desenvolvido por Alexey Shipilev quando ele trabalhou na Oracle e fornece uma plataforma extensa para o desenvolvimento de testes de desempenho para aplicativos Java. Alexey fez um excelente trabalho ajudando as pessoas a evitar muitos erros simples que cometem ao tentar analisar o desempenho do aplicativo: aquecer, evitar exceções etc.
Agora o JMH pode ser incluído no OpenJDK. Qualquer pessoa interessada em trabalhar no próprio JDK e alterar o código pode usá-lo para comparar o desempenho antes e depois de suas alterações, bem como comparar o desempenho em diferentes releases. Vários testes estão incluídos para permitir o teste; O design do JMH é tal que é fácil adicionar novos testes para onde é necessário.
O OpenJDK possui duas portas para a arquitetura Arm64, uma fornecida pela Oracle e a outra pela Red Hat. Como isso não era necessário, e a Oracle parou de dar suporte ao Arm para seus binários JDK, decidiu-se usar apenas a porta Red Hat, que ainda é suportada e desenvolvida.
A classe Data Sharing (CDS) costumava ser um recurso comercial no Oracle JDK. Com uma transição recente feita no JDK 11 para eliminar todas as diferenças funcionais entre o Oracle JDK e o OpenJDK, ele foi incluído no OpenJDK.
Para usar o CDS, você precisa de um arquivo morto criado para classes que são carregadas quando o aplicativo é iniciado. O JDK 12 para plataformas de 64 bits agora possui o arquivo classes.jsa
no diretório lib/server
. Este é o arquivo CDS para "classes padrão". Suponho que isso significa todas as classes públicas nos módulos JDK; Não consegui encontrar uma maneira de descompactá-lo para verificar. Como o CDS é ativado por padrão, o que equivale à opção -Xshare:auto
na linha de comando, os usuários se beneficiarão com os melhores tempos de inicialização do aplicativo.
Conclusões
O JDK 12 fornece um pequeno número de novas funções e APIs, com a switch
sendo a mais interessante para os desenvolvedores. Os usuários do G1 certamente apreciarão as melhorias de desempenho.
Com a nova versão do release, aconselho todos os usuários a testar seus aplicativos neste release. Manter o controle de alterações incrementais ajudará a evitar surpresas se você decidir avançar para a próxima versão do suporte de longo prazo.
Temos compilações gratuitas do JDK 12 para o Zulu Community Edition para ajudá-lo em seus testes. Não deixe de experimentá-los.