Prefácio: Java 8 foi lançado há algum tempo e todos os sinais indicam que o Java 8 é uma grande mudança na distribuição. Já existem muitos artigos sobre os geeks de código Java que introduzem novos recursos do Java 8, como brincar com Java 8 Lambdas e concorrência, Java 8 Data Time Tutorial: LocalDateTime e Classe Abstract versus Interface na ERA JDK 8. Este artigo também se refere a algumas outras informações, como: 15 devem ler os tutoriais do Java 8 e o lado sombrio do Java 8. Este artigo compilou as informações acima e as compilou em um livro de referência sobre os novos recursos do Java 8. Espero que você ganhe algo.
1. Introdução
Não há dúvida de que o Java 8 é a versão mais importante do Java desde o Java 5 (lançado em 2004). Esta versão contém mais de uma dúzia de novos recursos em idiomas, compiladores, bibliotecas, ferramentas e JVM. Neste artigo, aprenderemos esses novos recursos e usaremos exemplos práticos para ilustrar quais cenários são adequados para uso.
Este tutorial contém vários tipos de problemas que os desenvolvedores de Java geralmente enfrentam:
linguagem
Compilador
Biblioteca
ferramenta
Runtime (JVM)
2. Novas características da linguagem Java
Java 8 é uma versão importante do Java. Algumas pessoas acreditam que, embora esses novos recursos sejam esperados pelos desenvolvedores de Java, isso também exige muito esforço para aprender. Nesta seção, apresentaremos a maioria dos novos recursos do Java 8.
2.1 Expressões lambda e interfaces funcionais
As expressões Lambda (também conhecidas como fechamentos) são as maiores e mais esperadas mudanças de idioma no Java 8. Ele nos permite passar funções como parâmetros para um método ou processar o código em si como dados: os desenvolvedores funcionais estão muito familiarizados com esses conceitos. Muitos idiomas nas plataformas JVM (Groovy, Scala, etc.) suportam expressões Lambda desde o seu nascimento, mas os desenvolvedores de Java não têm escolha a não ser usar classes internas anônimas em vez de expressões de lambda.
O design da Lambda levou muito tempo e muitos esforços da comunidade e, finalmente, encontrou uma solução de implementação de compromisso que poderia obter uma estrutura de linguagem simples e compacta. A expressão mais simples de lambda pode ser composta por uma lista de parâmetros separada por vírgula, -> símbolo e bloco de declaração, por exemplo:
Arrays.asList ("A", "B", "D") .ForEach (e -> System.out.println (e));
No código acima, o tipo de parâmetro E é derivado pelo raciocínio do compilador e você também pode especificar explicitamente o tipo de parâmetro, por exemplo:
Arrays.asList ("A", "B", "D") .ForEach ((String e) -> System.out.println (e));
Se uma expressão lambda requer um bloco de declaração mais complexo, você poderá incluir o bloco de instrução com aparelho encaracolado, semelhante ao corpo de uma função em Java, por exemplo:
Arrays.asList ("A", "B", "D") .foreach (e -> {System.out.print (e); System.out.print (e);});As expressões Lambda podem se referir a membros da classe e variáveis locais (que converterão implicitamente essas variáveis em final), por exemplo, os dois blocos de código a seguir têm exatamente o mesmo efeito:
String separator = ","; Arrays.asList ("A", "B", "D") .ForEach ((String e) -> System.out.print (E + Separator));e
Final String Separator = ","; Arrays.asList ("A", "B", "D") .ForEach ((String e) -> System.out.print (E + Separator));As expressões lambda têm valores de retorno e o tipo de valores de retorno também é derivado pela inferência do compilador. Se o bloco de instrução na expressão lambda tiver apenas uma linha, você não precisará usar a instrução RETURN. Os dois trechos de código a seguir têm o mesmo efeito:
Arrays.asList ("A", "B", "D") .Sort ((E1, E2) -> E1.comPARETO (E2));e
Arrays.asList ("A", "B", "D") .Sort ((E1, E2) -> {int Result = e1.compareto (e2); resultado de retorno;});Para tornar as funções existentes bem compatíveis com as expressões Lambda, os designers da Lambda consideraram muitos métodos, então eles criaram o conceito de interface de função. A interface da função refere -se a uma interface com apenas uma função, e essa interface pode ser implicitamente convertida em uma expressão de lambda. java.lang.runnable e java.util.Concurrent.Callable são os melhores exemplos de interfaces funcionais. Na prática, as interfaces funcionais são muito frágeis: desde que um desenvolvedor adicione uma função à interface, a interface não é mais uma interface funcional, resultando em falha de compilação. Para superar essa vulnerabilidade ao nível de código e afirmar explicitamente que uma interface é uma interface funcional, o Java 8 fornece uma anotação especial @FunctionalInterface (todas as interfaces relacionadas na biblioteca Java já têm essa anotação), para fornecer uma definição simples de uma interface funcional:
@FunctionAlInterface Public Interface funcional {void Method ();}No entanto, uma coisa a observar é que o método padrão e o método estático não destruirão a definição da interface funcional; portanto, o código a seguir é legal.
@FunctionAlInterface Public Interface FunctionalDefaultMethods {void Method (); void padrão defaultMethod () {}}A Lambda Expressions, como o maior ponto de venda do Java 8, tem o potencial de atrair mais desenvolvedores para ingressar na plataforma JVM e usar o conceito de programação funcional na programação pura de Java. Se você precisar saber mais sobre expressões Lambda, pode consultar a documentação oficial.
2.2 Métodos padrão e estáticos de interfaces
O Java 8 usa dois novos conceitos para estender o significado de uma interface: o método padrão e o método estático. O método padrão torna a interface um pouco semelhante às características, mas os objetivos a serem alcançados são diferentes. O método padrão permite que os desenvolvedores adicionem novos métodos às interfaces existentes sem quebrar a compatibilidade binária, ou seja, não forçando as classes que implementam a interface a implementar o método recém -adicionado ao mesmo tempo.
A diferença entre um método padrão e um método abstrato é que um método abstrato precisa ser implementado, enquanto um método padrão não. Os métodos padrão fornecidos pela interface serão herdados ou substituídos pela classe de implementação da interface. O código de exemplo é o seguinte:
Interface privada… // interfaces agora permitem métodos padrão, o implementador pode ou // não pode implementar (substituí -los). String padrão NotRequired () {return "Implementação padrão"; }} classe estática privada defaultableImpl implementa a classe estática privada {} private static straticableImpl implementa defaulable {@Override public String NotRequired () {return "Substitued Implementation";}}A interface inadimplente usa o padrão da palavra -chave para definir um método padrão notreabired (). A classe PadrafactableImpl implementa essa interface e herda os métodos padrão nesta interface por padrão; A classe OvertiDableImpl também implementa essa interface, mas substitui os métodos padrão da interface e fornece uma implementação diferente.
Outro recurso interessante trazido por Java 8 é que os métodos estáticos podem ser definidos em interfaces. O código de exemplo é o seguinte:
Interface privada defaulablefactory {// interfaces agora permitem métodos estáticos criados com defeito estático (fornecedor <faulable> fornecedor) {return fornecedor.get ();}}O snippet de código a seguir integra os cenários de uso dos métodos padrão e métodos estáticos:
public static void main (string [] args) {defaulaable default = defaulablefactory.create (defaultableImpl :: new); system.out.println (padrão.NotReQuired ()); Defaulaable = DefaulableFactory.create (substituirImpl ::);A saída deste código é a seguinte:
Implementação padrão
Implementação substituída
Como a implementação do método padrão na JVM fornece suporte no nível do bytecode, é muito eficiente. O método padrão permite interfaces aprimoradas sem quebrar o sistema de herança existente. A aplicação desse recurso na biblioteca oficial é: Adicione novos métodos à interface java.util.Collection, como Stream (), ParallelStream (), foreach () e Remof (), etc.
Embora os métodos padrão tenham muitos benefícios, eles devem ser usados com cautela no desenvolvimento real: em sistemas de herança complexos, os métodos de inadimplência podem causar erros de ambiguidade e compilação. Se você quiser saber mais detalhes, consulte a documentação oficial.
2.3 Referência do método
A referência do método permite que os desenvolvedores faça referência diretamente aos métodos existentes, construtores de classe Java ou objetos de instância. As referências de método e expressões de lambda são usadas em conjunto, tornando o construtor da classe Java compacta e concisa, sem muito código de modelo complexo.
No exemplo de Simon, a classe de carros é um exemplo de referências de método diferentes, que podem ajudar os leitores a distinguir entre quatro tipos de referências de método.
Classe estática pública Carro {Public Static Car Create (fornecedor final <ar> fornecedor) {Return Supplier.get ();} public static void colégue (carro final carro) {System.out.println ("colidido" + car.toString ());} public a seguir (outro carro final. {System.out.println ("reparado" + this.toString ());}}O tipo de referência para o primeiro método é uma referência do construtor, a sintaxe é classe :: novo ou mais forma geral: classe <t> :: new. Nota: Este construtor não possui parâmetros.
carro final carro = car.create (carro :: novo); Lista final <ar> cars = Arrays.asList (carro);
O tipo de referência do segundo método é uma referência estática do método e a sintaxe é classe :: static_method. Nota: Este método aceita um parâmetro de tipo de carro.
Cars.ForEach (Car :: Collide);
O terceiro método faz referência ao tipo é uma referência a um método de membro de uma determinada classe, e a sintaxe é o método Class ::. Observe que este método não define o parâmetro:
Cars.ForEach (Car :: Reparo);
O quarto método referenciado pelo tipo é uma referência a um método de membro de um objeto de instância, e a sintaxe é o método da instância ::. Nota: Este método aceita um parâmetro de tipo de carro:
Polícia final do carro = car.create (carro :: novo); caroneio (policial :: siga);
Execute o exemplo acima e você poderá ver a seguinte saída no console (a instância do carro pode ser diferente):
Colidido com.javacodegeeks.java8.method.references.methodReferences$car@7a81197d reparado com.javacodegeeks.java8.method.references.methodReferences$car@7A81197D
Se você quiser entender e aprender conteúdo mais detalhado, pode consultar a documentação oficial
2.4 Repita comentários
Desde a introdução de anotações no Java 5, esse recurso se tornou muito popular e tem sido amplamente utilizado em várias estruturas e projetos. No entanto, as anotações têm uma grande limitação: a mesma anotação não pode ser usada várias vezes no mesmo local. Java 8 quebra essa limitação e apresenta o conceito de anotações repetidas, permitindo que a mesma anotação seja usada várias vezes no mesmo local.
Definir anotações repetidas no Java 8 usando a anotação @Repeatable na verdade não é uma melhoria no nível do idioma, mas um truque feito pelo compilador, e a tecnologia subjacente ainda é a mesma. Você pode usar o seguinte código para explicar:
pacote com.javacodegeeks.java8.repeatable.annotações; importar java.lang.annotation.ElementType; importar java.lang.annotation.Repeatable; importar java.lang.annotation.retention; importar java.lang.annotation.retEntionPolicy; importar java.lang.annotation.target; classe pública RepetingAnnotações {@target (elementType.Type) @retention (retentionPolicy.Runtime) public @Interface filters {filter [] value ();} @target (elementType.type) @retentntion (retentionPolicy.Runtime) @Repeatable (Filters.Claster) Public Public) @Filter ("filtro1") @Filter ("filtro2") interface pública Filtrable {} public static void main (string [] args) {for (filtro filtro: filtrable.class.getannotationsbytype (filter.class)) {System.out.println (filter.value ());}}Como podemos ver, a classe Filter aqui usa a anotação @RePeatable (Filters.class) e o Filters é um contêiner que armazena anotações de filtro. O compilador tenta bloquear esses detalhes dos desenvolvedores. Dessa forma, a interface filtrável pode ser anotada com duas anotações de filtro (nenhuma informação sobre filtros é mencionada aqui).
Além disso, a API de reflexão fornece um novo método: getAnnoTationsByType (), que pode retornar anotações duplicadas de um determinado tipo, por exemplo
Filterable.class.getannoation (filters.class) retornará duas instâncias de filtro, e a saída de conteúdo do console é a seguinte:
filtro1
filtro2
Se você quiser saber mais, pode consultar a documentação oficial.
2.5 melhor inferência de tipo
O compilador Java 8 fez grandes melhorias na inferência do tipo. Em muitos cenários, o compilador pode deduzir o tipo de dados de um determinado parâmetro, tornando o código mais conciso. O código de exemplo é o seguinte:
pacote com.javacodegeeks.java8.type.inference; classe pública Valor <T> {public static <t> t defaultValue () {return null; } public t getorDefault (valor t, t defaultValue) {return (value! = null)? Valor: DefaultValue;}}O código a seguir é um aplicativo do valor de tipo <String>:
pacote com.javacodegeeks.java8.type.inference; public class TypeInference {public static void main (string [] args) {value final <string> value = new Value <> (); value.getorDefault ("22", value.defaultValue ());}}O tipo de valor do parâmetro.DefaultValue () é derivado pelo compilador e não precisa ser explicitamente especificado. No Java 7, este código terá um erro de compilação, a menos que o valor. <string> defaultValue () seja usado.
2.6 Ampliar os cenários de aplicação de anotações
Java 8 amplia os cenários de aplicação de anotações. Agora, as anotações podem ser usadas em quase qualquer elemento: variáveis locais, tipos de interface, superclasses e classes de implementação de interface e até em definições de exceção de funções. Aqui estão alguns exemplos:
pacote com.javacodegeeks.java8.annotações; importar java.lang.annotation.ElementType; importar java.lang.annotation.retention; importar java.lang.annotation.retEntionPolicy; importar java.lang.annotation.target; importar java.util.arraylist; importar java.util.Collection; public class Anotaations {@retention (retentionpolicy.runtime) @target ({elementType.type_use, elementType.type_parameter}) public @interface não -opty {} public class. "não utilizado") public static void main (string [] args) {holder final <string> titular = new @NonEmpty Holder <String> (); @NonEmpty Collection <@NonEmpty String> strings = new ArrayList <> (); }}ElementType.Type_User e ElementType.Type_Parameter são duas novas anotações adicionadas ao Java 8 para descrever os cenários de uso das anotações. Idioma java
Yan também fez alterações correspondentes para identificar essas notas recém -adicionadas.
3. Novas características do compilador Java
3.1 Nome do parâmetro
Para obter os nomes de parâmetros dos métodos nos programas Java em tempo de execução, as gerações mais velhas de programadores Java devem usar métodos diferentes, como o paranâmero Liberário. O Java 8 finalmente normaliza esse recurso, com suporte no nível do idioma (usando a API de reflexão e o parameter.getName () Método) e o nível de bytecode (usando o novo compilador Javac e o parâmetro -parameters).
pacote com.javacodegeeks.java8.parameter.names; importar java.lang.reflect.method; importar java.lang.reflect.parameter; classe pública ParameterNames {public static void main (string [] args) lança exceção {método método = parameterNames.class.getMethod ("main", string []. class); for (parâmetro final parâmetro: métod.getParameters ()) {System.out.println ("Parameter:" + Parameter.getName ());}}}No Java 8, esse recurso é desligado por padrão; portanto, se você compilar o código acima sem o parâmetro -parameters e executá -lo, os seguintes resultados serão emitidos:
Parâmetro: Arg0
Se o parâmetro -parameters for usado, o seguinte resultado será emitido (resultado correto):
Parâmetro: Args
Se você usa o MAVEN para gerenciamento de projetos, poderá configurar o parâmetro -Parameters no MAVEN-COMPILER-PLUGIN COMPILER ITEME
<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. Novos recursos da biblioteca oficial Java
O Java 8 adicionou muitas novas classes de ferramentas (aulas de data/hora) e estendeu as classes de ferramentas existentes para apoiar a programação simultânea moderna, a programação funcional etc.
4.1 Opcional
O bug mais comum nos aplicativos Java são as exceções de valor nulo. Antes do Java 8, o Google Guava introduziu a classe Opcional para resolver o NullPointerException, evitando assim o código -fonte sendo contaminado por várias verificações nulas, para que os desenvolvedores possam escrever código mais limpo. O Java 8 também adiciona opcional à biblioteca oficial.
Opcional é apenas uma coisa fácil: armazene um valor do tipo T ou nulo. Ele fornece algumas interfaces úteis para evitar a verificação nula explícita e você pode consultar a documentação oficial do Java 8 para obter mais detalhes.
Em seguida, vamos dar uma olhada em um exemplo de uso opcional: um valor que pode estar vazio ou um valor de um determinado tipo:
Opcional <string> fullName = opcional.ofnullable (null); System.out.println ("Nome completo está definido?" + FullName.ispresent ()); System.out.println ("Nome completo:" + fullname.orelseget (() -> "[nenhum]")); System.out.println (fullname.map (s -> "hey" + s + "!") .Orelse ("Hey Stranger!"));Se a instância opcional mantém um valor não nulo, o método ispresent () retornará true, caso contrário, ele retornará false; Orelseget () Método e a instância opcional mantém nulo, ele pode aceitar o valor padrão gerado por uma expressão Lambda; O método map () pode converter o valor da instância opcional existente em um novo valor; O método Orelse () é semelhante ao método Orelseget (), mas retorna o valor padrão passado ao manter o NULL.
Os resultados da saída do código acima são os seguintes:
O nome completo está definido? Nome completo falso: [nenhum] Ei, estranho!
Vamos dar uma olhada em outro exemplo simples:
Opcional <string> primeironame = opcional.of ("tom"); System.out.println ("O primeiro nome está definido?" + FirstName.ispresent ()); System.out.println ("primeiro nome:" + primeironame.orelseget (() -> "[nenhum]")); System.out.println (FirstName.map (S -> "Hey" + S + "!") .Orelse ("Hey Stranger!")); System.out.println ();A saída deste exemplo é:
O primeiro nome está definido? Verdadeiro Nome: Tom Hey Tom!
Se você quiser saber mais detalhes, consulte a documentação oficial.
4.2 fluxos
A API de fluxo recém -adicionada (java.util.stream) introduz a programação funcional do ambiente gerado na biblioteca Java. Esta é de longe a maior melhoria das bibliotecas Java para que os desenvolvedores possam escrever um código mais eficiente, conciso e compacto.
A API do Steam simplifica bastante as operações de coleta (veremos mais do que apenas coleções mais tarde). Primeiro, vamos dar uma olhada nesta aula chamada Tarefa:
public class Streams {status de enum privado {aberto, fechado}; Tarefa de classe final estática privada {status final privado de status; Pontos do número inteiro final privado; tarefa (status final do status, pontos inteiros finais) {this.status = status; this.points = pontos;} public integer getPoints () {Return Points;} status público getStatus () {retorna status;} @Override public string tostring () {return string.format ("[ %s, %d]", status, pontos);}}}}}}}}}}}}}}}}}A classe de tarefas tem um conceito de fração (ou pseudo-complexidade) e existem outros dois estados: abertos ou fechados. Agora, suponha que haja uma coleção de tarefas:
Coleção Final <Task> tarefas = Arrays.asList (nova tarefa (status.open, 5), nova tarefa (status.open, 13), nova tarefa (status.closed, 8));
Primeiro, vejamos uma pergunta: quantos pontos de estado aberto existem nesta coleção de tarefas? Antes do Java 8, para resolver esse problema, você precisa usar o forach para fazer um loop sobre a coleta de tarefas; No entanto, no Java 8, você pode usar Steams para resolvê -lo: inclua uma lista de uma série de elementos e suporta processamento sequencial e paralelo.
// Calcule o total de pontos de todas as tarefas ativas usando SUM () TotalPointSoFoPentasks final = Tasks.Stream (). Filter (Task -> Task.getStatus () == status.open) .Maptoint (Task :: getPointSOPENTASKS);
A saída do console para executar este método é:
Pontos totais: 18
Há muitos pontos de conhecimento que valem a pena falar aqui. Primeiro, o conjunto de tarefas é convertido em uma representação de vapor; Segundo, a operação de filtro nos filtros de vapor fora de todas as tarefas fechadas; Terceiro, a operação do Maptoint converte o fluxo de tarefas em uma coleção inteira com base no método da tarefa :: getPoints de cada instância de tarefa; Finalmente, a soma é calculada pelo método da soma para obter o resultado final.
Antes de aprender o próximo exemplo, você precisa se lembrar de alguns pontos de conhecimento sobre vapor (clique aqui para obter mais detalhes). As operações acima do vapor podem ser divididas em operações intermediárias e operações tardias.
As operações intermediárias retornarão um novo vapor - executando uma operação intermediária (como filtro) não executará a operação de filtragem real, mas criará um novo vapor e colocará os elementos que atendem às condições no vapor original no vapor recém -criado.
Operações tardias (como foreach ou soma) atravessarão o vapor e obterão resultados ou resultados acompanhantes; Depois de executar as operações tardias, a linha de processamento de vapor foi processada e não pode ser usada. Em quase todos os casos, as operações tardias estão atravessando o vapor imediatamente.
Outro valor do vapor é o suporte criativo para o processamento paralelo. Para a coleção de tarefas acima, podemos usar o seguinte código para calcular a soma dos pontos de todas as tarefas:
// Calcule pontos totais de todas as tarefas TotalPoints duplas finais = tarefas.Stream (). Parallel (). Map (Task -> Task.getPoints ()) // ou map (Task :: getPoints) .Reduce (0, Integer :: sum); System.out.println ("Total Points (todos os Tasks):" + Totalpoints);Aqui, usamos o método paralelo para processar todas as tarefas em paralelo e calcular o resultado final usando o método de redução. A saída do console é a seguinte:
Pontos totais (todas as tarefas): 26.0
Para uma coleção, geralmente é necessário agrupar elementos nela de acordo com certas condições. Esse tipo de tarefa pode ser concluído rapidamente usando a API fornecida pelo Steam. O código é o seguinte:
// Grupo Tarefas por seu status Mapa final <Status, List <Teck>> map = tasks.Stream (). COLLECT (COLLECORS.GROUPINGBY (Task :: getStatus)); System.out.println (map);
A saída do console é a seguinte:
{Fechado = [[fechado, 8]], aberto = [[aberto, 5], [aberto, 13]]}
A última pergunta de exemplo sobre a coleção de tarefas é: como calcular a proporção de pontos de cada tarefa na coleção da coleção. O código de processamento específico é o seguinte:
// 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( weight -> ( long )( weight * 100 ) ) // longstream .Maptoobj (porcentagem -> porcentagem + "%") // Stream <String> .Collect (collectors.tolist ()); // list <string> System.out.println (resultado);
Os resultados da saída do console são os seguintes:
[19%, 50%, 30%]
Finalmente, como mencionado anteriormente, a API do Steam pode não apenas atuar nas coleções Java, mas também as operações tradicionais de IO (leia dados de um arquivo ou de uma linha por linha) podem se beneficiar do processamento de vapor. Aqui está um pequeno exemplo:
caminho final caminho = novo arquivo (nome do arquivo) .topath (); tente (stream <string> lines = files.lines (path, standardcharsets.utf_8)) {lines.onclose (() -> system.out.println ("feito!") .ForEach (System.out :: println);}O método de fluxo Oncloke retorna um fluxo equivalente com uma alça extra. Esse identificador será executado quando o método Close () do fluxo for chamado. A API do fluxo, expressões de lambda e referências de método suportadas por métodos padrão da interface e métodos estáticos são a resposta do Java 8 ao paradigma moderno do desenvolvimento de software.
4.3 Data/hora API (JSR 310)
O Java 8 apresenta uma nova API de data de data (JSR 310) para melhorar o processamento de tempo e datas. O gerenciamento de tempo e data sempre foi o problema mais doloroso para os desenvolvedores de Java. java.util.date e posterior java.util.calendar não resolveram esse problema (ainda mais confuso com os desenvolvedores).
Por causa das razões acima, nasceu a biblioteca de terceiros Joda-Time, que pode substituir a API de gerenciamento de tempo de Java. A nova API de gerenciamento de horas e data em Java 8 é profundamente influenciada pelo Joda-Time e absorveu grande parte da essência do Joda-Time. O novo pacote Java.Time contém todas as classes sobre data, horário, fuso horário, instantâneo (semelhante à data, mas preciso às operações de nanossegundos), duração (duração) e relógio. A API recém -projetada considera seriamente a invariância dessas classes (palestras aprendidas com java.util.calendar) e retorna um novo objeto se uma instância precisar ser modificada.
Vamos dar uma olhada nas principais classes e em seus respectivos exemplos de uso no pacote Java.Time. Primeiro, a aula do relógio usa um fuso horário para devolver a atual hora e data de nanossegundos. O relógio pode substituir o System.currenttimemillis () e o timezone.getDefault ().
// Obtenha o relógio do sistema como UTC Offset Final Clock = Clock.systemutc (); System.out.println (clock.instant ()); System.out.println (clock.millis ());
A saída deste exemplo é:
2014-04-12T15: 19: 29.282Z 1397315969360
Segundo, concentre -se nas aulas LocalDate e Localtime. LocalDate contém apenas a parte da data no sistema de calendário ISO-8601; O LocalTime contém apenas a parte do tempo no sistema de calendário. Objetos de ambas as classes podem ser criados usando objetos de relógio.
// Obtenha a data local e a hora local finalDate Date = localDate.now (); final DateFromClock = localDate.now (relógio); System.out.println (DateFromClock); // obtenha a data local e a hora local final horário local = localTime.now (); final de tempo local, tempo de tempo de cronograma = localTime.now (relógio); system.out.println (time); system.out.println (timefromClock);
Os resultados da saída do exemplo acima são os seguintes:
2014-04-12 2014-04-12 11: 25: 54.568 15: 25: 54.568
A classe LocalDateTime contém informações sobre LocalDate e Localtime, mas não contém informações sobre funções horárias no sistema de calendário ISO-8601. Aqui estão alguns exemplos sobre LocalDate e Localtime:
// Obtenha a data local/hora finalDateTime DateTime = localDateTime.now (); Final LocalDateTime DateTimeFromClock = LocalDateTime.now (relógio); System.out.println (DateTime); System.out.println (DateTimeFromClock);
Os resultados da saída do exemplo acima são os seguintes:
2014-04-12T11: 37: 52.309 2014-04-12T15: 37: 52.309
Se você precisar de informações de dados/tempo para um fuso horário específico, poderá usar o ZoneDateTime, que mantém a data e a hora do sistema ISO-8601 Data e possui informações sobre funções horárias. Aqui estão alguns exemplos de uso de fusos horários diferentes:
// Obtenha a data/hora final zonedDateTime ZonedDateTime = zonedDateTime.now (); final zonedDateTime ZonedDateTimeFromClock = zonedDateTime.now (relógio); final zonedDateTime ZonedDateTimeFromZone = zonedDateTime.now (zoneid.of ("America/Los_angeles")); System.out.println (ZonedDateTime); System.out.println (zonedDateTimeFromClock); System.out.println (zonedDateTimeFromClock); System.out.println (zonedDateTimeFromzone);A saída deste exemplo é:
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_angueles]
Por fim, vejamos a classe de duração, que mantém a hora de segundos e nanossegundos. Isso facilita calcular a diferença entre duas datas, o código de exemplo é o seguinte:
// obtenha duração entre duas datas Final LocalDeTime de = localDateTime.Of (2014, Month.April, 16, 0, 0, 0); final DatateTime para = LocalDateTime.Of (2015, Month.April, 16, 23, 59, 59); duração final da duração = dura (de). duração.Todays ()); System.out.println ("Duração em horas:" + duração.tohours ());Este exemplo é usado para calcular o número de dias e horas entre 16 de abril de 2014 e 16 de abril de 2015, e a saída é a seguinte:
Duração em dias: 365 duração em horas: 8783
A impressão geral da nova data e hora do Java 8 é relativamente positiva, em parte por causa do impacto positivo do tempo de Joda e em parte porque o funcionário finalmente ouviu as necessidades dos desenvolvedores. Se você quiser saber mais detalhes, pode consultar a documentação oficial.
4.4 Motor JavaScript de Nashorn
O Java 8 fornece o novo mecanismo JavaScript de Nashorn, permitindo -nos desenvolver e executar aplicativos JS na JVM. O Nashorn JavaScript Engine é outra versão de implementação do javax.script.scriptengine. Esse tipo de mecanismo de script segue as mesmas regras e permite que Java e JavaScript sejam usados de forma interativa. O código de exemplo é o seguinte:
ScriptEngineManager gerente = new scriptEngineManager (); scriptEngine mecan = gerenciador.getEnginebyName ("javascript"); system.out.println (mecany.getclass (). GetName ();); system.out.println ("resultado:" + mecanismo.eval ("function f () {{) 1;A saída deste código é a seguinte:
jdk.nashorn.api.scripting.nashornscriptEngine Resultados: 2
4.5 Base64
O suporte à codificação Base64 foi adicionado à biblioteca Java 8 oficial, para que a codificação BASE64 possa ser realizada sem usar uma biblioteca de terceiros. O código de exemplo é o seguinte:
pacote com.javacodegeeks.java8.base64; importar java.nio.charset.standardSetSets; importar java.util.base64; public class base64s {public static void main (string [] args) {final string text = "base64 finalmente em java 8!"; Final String coded = base64.getEncoder (). codetoString (text.getBytes (standardcharsets.utf_8)); system.out.println (codificado); Final String Decoded = new String (base64.getDecoder (). Decode (codificado), StandardCharsets.utf_8); System.out.println (decodificado);}}A saída deste exemplo é a seguinte:
QMFZZTY0IGZPBMFSBHKGAW4GSMF2YSA4IQ ==
Base64 Finalmente em Java 8!
O novo Base64API também suporta codificação e decodificação de URLs e minas.
(Base64.geturlencoder () / base64.geturldecoder (), base64.getMimeEncoder () / base64.getMimEdecoder ()).
4.6 Matrizes paralelas
A versão Java8 adicionou muitos novos métodos para suportar o processamento de matriz paralelo. O método mais importante é o Paralelsort (), que pode acelerar significativamente a classificação da matriz em máquinas multi-core. O exemplo a seguir demonstra o método da série Parallelexxxx:
pacote com.javacodegeeks.java8.parallel.arrays; import java.util.arrays; importar java.util.concurrent.threadlocalrandom; classe pública paralelArrays {public static void main (string [] args) {long [] Arrayoflong = new Long [20000]; Arrays.parallelLeSetall (Arrayoflong, Índice -> Threadlocalrandom.Current (). NextInt (1000000)); Arrays.Stream (Arrayoflong) .limit (10) .FOREACH (I -System.out.Print (i + "")); ).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 并发性
基于新增的lambda表达式和steam特性,为Java 8中为java.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
这个命令会输出很多结果,我们仅看下其中的一部分:依赖关系按照包分组,如果在classpath上找不到依赖,则显示”not found”.
org.springframework.core-3.0.5.RELEASE.jar -> C:/Program Files/Java/jdk1.8.0/jre/lib/rt.jarorg.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 foundorg.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 space)。在JVM参数方面,使用-XX:MetaSpaceSize和-XX:MaxMetaspaceSize代替原来的-XX:PermSize和-XX:MaxPermSize。
7. Conclusão
通过为开发者提供很多能够提高生产力的特性,Java 8使得Java平台前进了一大步。现在还不太适合将Java 8应用在生产系统中,但是在之后的几个月中Java 8的应用率一定会逐步提高(PS:原文时间是2014年5月9日,现在在很多公司Java 8已经成为主流,我司由于体量太大,现在也在一点点上Java 8,虽然慢但是好歹在升级了)。作为开发者,现在应该学习一些Java 8的知识,为升级做好准备。