Java6 To Java8 PDF
Java6 To Java8 PDF
Java6 To Java8 PDF
Contribuidores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
Começando. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
Introdução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
Objetivos. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
Language Enhancements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
Objetos Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
Localization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
Localização (Locale) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
Data e Hora . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
Fusos Horários . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
Lambda . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
Concurrency . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
Locks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
Paths . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182
Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192
WatchService . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222
Cenário 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241
Cenário 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242
Cenário 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242
Gratuito . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 244
Pagos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 244
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
associated documentation files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial
portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1
Contribuidores
Por ser um livro de código aberto, recebemos várias mudanças de conteúdo e erratas ao longo de
seu desenvolvimento. Aqui estão todas as pessoas que contribuíram para a versão em português do
livro como um projeto de código aberto. Obrigado a todos por ajudarem a tornar este livro melhor
para todos.
Esse texto é praticamente o mesmo utilizado no Pro Git 2. Acesse o texto original aqui.
2
Começando
Introdução
Este projeto serve como material de apoio na realização do exame 1Z0-813 que atualiza qualquer
profissional com certificação Java 6 ou inferior, para a versão 8. No momento desta documentação,
o voucher do exame custa R$ 597,00 no Brasil.
Objetivos
Como guia para criação deste livro foi utilizado todos os objetivos citados na seção "Review Exam
Topics", de acordo com o site da certificação.
A missão deste livro é criar o maior número de exemplos práticos possíveis para ajudar você a
colocar a mão na massa. Quanto maior for seu contato com os códigos da versão 8 do Java, mais
confiante estará na hora do exame, e também para lidar com projetos em Java 8 no mercado de
trabalho.
3
Language Enhancements
Objetos Strings
Objetivo
Develop code that uses String objects in the switch statement, binary literals, and
numeric literals, including underscores in literals.
-
Desenvolver código que utilize objetos String em instruções Switch, binários literais,
e numéricos literais, incluindo underscore (_) em literais.
É esperado que o candidato saiba compreender e analisar o uso de Strings em instruções switch,
como no seguinte exemplo.
src/org/j6toj8/languageenhancements/stringinswitch/StringInSwitch_Complete.java
switch (mes) {
case "jan":
System.out.println("Janeiro");
break;
case "fev":
System.out.println("Fevereiro");
break;
case "mar":
System.out.println("Março");
break;
default:
break;
}
}
Apesar da certificação ter foco nas atualizações trazidas pelo Java 7 e 8, é esperado que o candidato
entenda também conceitos de versões anteriores do Java. Por isso, serão apresentadas algumas
regras que talvez você já conheça sobre switch, mas utilizando String no switch.
4
src/org/j6toj8/languageenhancements/stringinswitch/StringInSwitch_Default.java
switch (mes) {
case "jan":
System.out.println("Janeiro");
break;
default: // COMPILA - O default pode estar em qualquer posição
break;
case "jan": // NÃO COMPILA - Já existe o case "jan"
System.out.println("Janeiro2");
break;
case "mar":
System.out.println("Março");
break;
}
}
◦ int e Integer
◦ byte e Byte
◦ short e Short
◦ char e Character
◦ String
◦ valores de Enums
5
src/org/j6toj8/languageenhancements/stringinswitch/StringInSwitch_Type.java
src/org/j6toj8/languageenhancements/stringinswitch/StringInSwitch_Break.java
switch (mes) {
case "jan":
System.out.println("Janeiro");
default:
System.out.println("Não é um mês");
case "fev":
System.out.println("Fevereiro");
break;
case "mar":
System.out.println("Março");
break;
}
}
saída no console
Janeiro
Não é um mês
Fevereiro
Nesse caso a execução inicia no case "jan", passar pelo default e pelo case "fev" até parar no
break, por isso as 3 strings aparecem no console.
6
6. Um switch vazio é válido, mesmo que não tenha utilidade.
src/org/j6toj8/languageenhancements/stringinswitch/StringInSwitch_Empty.java
7. Todos os valores de case precisam ser constantes, ou seja, variáveis finais em tempo de
compilação. Se o valor do case puder mudar em tempo de execução, o código não compila.
src/org/j6toj8/languageenhancements/stringinswitch/StringInSwitch_ConstantOnly.java
switch (mes) {
case jan: // NÃO COMPILA - jan é um atributo comum, pode mudar em tempo de
execução
System.out.println("Janeiro");
break;
case FEV: // COMPILA - FEV é uma constante em tempo de compilação, seu valor
nunca muda
System.out.println("Fevereiro");
break;
case mar: // COMPILA - mar é uma constante em tempo de compilação, seu valor
nunca muda
System.out.println("Março");
break;
case abr: // NÃO COMPILA - abr é uma variável comum, pode mudar em tempo de
execução
System.out.println("Abril");
break;
case mai: // NÃO COMPILA - mai é final, mas não é constante, pode mudar em tempo
de execução
System.out.println("Maio");
break;
}
}
7
Pronto, essas são as regras de switch. Você provavelmente já conheçe algumas referentes à versões
anteriores do Java, mas agora você as viu em switch que utilizam Strings. Isso não era possível
antes do Java 7.
É esperado que o candidato saiba compreender e analisar o uso de literais binários e numéricos,
como no seguinte exemplo.
src/org/j6toj8/languageenhancements/literals/Literals_Complete.java
int i1 = 1; // int
int i2 = 1_000_000; // int com underscore
int i3 = 0567; // octadecimal
int i4 = 0xFA1; // hexadecimal
int i5 = 0b0101; // binário
Apesar da certificação ter foco nas atualizações trazidas pelo Java 7 e 8, é esperado que o candidato
entenda também conceitos de versões anteriores do Java. Por isso, serão apresentadas algumas
regras que talvez você já conheça sobre literais.
1. No Java, Literal é qualquer número escrito diretamente no código, como todos do exemplo
acima.
2. Por padrão, o Java interpreta literais como int. Ou seja, se não houver um sufixo no número
para mudar seu tipo, ele é um int.
src/org/j6toj8/languageenhancements/literals/Literals_Suffix.java
3. Por padrão, o Java interpreta literais como sendo decimais. Existem prefixos que mudam o
sistema numérico do literal.
8
src/org/j6toj8/languageenhancements/literals/Literals_Prefix.java
long l1 = 0xABCL; // long também pode ser hexadecimal - começa com 0x e termina com
L
4. A partir do Java 7, é possível utilizar underscore (_) para separar visualmente um número. Isso
não muda o valor do número, e serve apenas para tornar o código mais legível.
src/org/j6toj8/languageenhancements/literals/Literals_Underscore.java
Referências
Strings em Switch
• Boyarsky, Jeanne; Selikoff, Scott. OCP: Oracle Certified Professional Java SE 8 Programmer
II Study Guide (p. 598). Wiley. Edição do Kindle.
Literais
• Boyarsky, Jeanne; Selikoff, Scott. OCP: Oracle Certified Professional Java SE 8 Programmer
II Study Guide (p. 597). Wiley. Edição do Kindle.
• Java/Literais. Wikibooks.
9
Try com recursos
Objetivo
Develop code that uses try-with-resources statements, including using classes that
implement the AutoCloseable interface.
-
Desenvolver código que utilize instruções try-with-resources, incluindo o uso de
classes que implementam a interface AutoCloseable.
Antes de continuar, com base no exemplo a seguir, entenda a execução do método main e o que é
apresentado no console após sua execução.
src/org/j6toj8/languageenhancements/trywithresources/TryWithResouces_Complete.java
10
O código acima utiliza dois arquivos, e faz leitura e escrita neles. A grande novidade desse código é
a declaração de variáveis dentro de parênteses após a instrução try. Isso é a sintaxe chamada try-
with-resources, e ela chama automaticamente o método close() dos recursos que estão sendo
utilizados. Até o Java 6, seria necessário chamar o close() manualmente, como no exemplo abaixo.
src/org/j6toj8/languageenhancements/trywithresources/TryWithResouces_Java6.java
src/org/j6toj8/languageenhancements/trywithresources/TryWithResouces_AutoCloseable.java
Saída no console
try
Porta fechada.
3. A instrução try-with-resources ainda pode ter catch e finally, apesar de não ser necessário.
Nesse caso, os recursos são fechados depois do try e antes de qualquer catch ou finally.
4. O método close pode lançar uma exceção sendo capturada pelo catch, caso exista.
11
src/org/j6toj8/languageenhancements/trywithresources/TryWithResouces_WithCatchFinally.java
Saída no console
try
Porta fechada.
catch
finally
Ou seja, primeiro o try é chamado. Logo depois é chamado o método close() que ao final lança
uma exceção. O catch captura essa exceção. E finalmente o finally é chamado.
12
src/org/j6toj8/languageenhancements/trywithresources/TryWithResouces_Order.java
Saída no console
Olá.
Gaveta fechada.
Porta fechada.
Ou seja, como a ordem de declaração dentro do try-with-resources foi Porta e depois Gaveta, a
ordem de chamada do método close é inversa: Gaveta e depois Porta.
13
src/org/j6toj8/languageenhancements/trywithresources/TryWithResouces_ResourceInsideTry.java
7. Somente classes que implementam AutoCloseable podem ser utilizadas dentro do try-with-
resources.
src/org/j6toj8/languageenhancements/trywithresources/TryWithResouces_NoAutoCloseable.java
8. Caso o método close() lance uma exceção checada (ou seja, que herda de Exception), o código só
compila se existir um catch que capture aquela exceção, ou o método declare o throws.
14
src/org/j6toj8/languageenhancements/trywithresources/TryWithResouces_CloseException.java
void try1() {
try (Porta porta = new Porta()) { // NÃO COMPILA - exceção do close() não é
capturada nem declarada
System.out.println("Olá 1");
}
}
void try2() {
try (Porta porta = new Porta()) {
System.out.println("Olá 2");
} catch (Exception e) { // COMPILA - exceção capturada
}
}
9. O Java 5 já possuía uma interface chamada Closeable, porém ela permite lançar apenas
IOException. A nova interface AutoCloseable permite lançar qualquer exceção. Como Closeable
atende a implementação de AutoCloseable, ela agora estende AutoCloseable. Logo, todas as
classes que já implementavam Closeable podem ser utilizadas dentro do try-with-resources. Veja
abaixo como era a interface Closeable antes e a partir do Java 7:
Antes do Java 7
A partir do Java 7
10. Um comportamento novo são as exceções suprimidas (suppressed). Se ambos o bloco try e o
15
método close lançam exceção, a do close fica suprimida, pois a do try é lançada primeiro.
src/org/j6toj8/languageenhancements/trywithresources/TryWithResouces_Suppressed.java
Saída no console
try
close
erro no try
erro no close
Ou seja, a exceção que de fato foi capturada foi a do bloco try, pois foi lançada primeiro. A
exceção lançada pelo método close ficou suprimida, e fica disponível em um array no método
getSuppressed() da exceção.
11. E por fim, é necessário lembrar que a instrução try "comum" ainda precisa obrigatoriamente de
um catch ou finally.
16
src/org/j6toj8/languageenhancements/trywithresources/TryWithResouces_CommonTry.java
try {
System.out.println("try");
} catch (Exception e) {
} // COMPILA - try "comum" só com catch
try {
System.out.println("try");
} finally {
} // COMPILA - try "comum" só com finally
try {
System.out.println("try");
} catch (Exception e) {
} finally {
} // COMPILA - try "comum" com catch e finally
}
Referências
• Using Try-With-Resources
Boyarsky, Jeanne; Selikoff, Scott. OCP: Oracle Certified Professional Java SE 8 Programmer
II Study Guide (p. 296). Wiley. Edição do Kindle.
17
Múltiplas Exception no mesmo catch
Objetivo
Develop code that handles multiple Exception types in a single catch block.
-
Desenvolver código que lide com múltiplos tipos de Exception em um único bloco catch.
É esperado que o candidato saiba compreender e analisar o uso da instrução try-catch com
múltiplos tipos de Exception no mesmo bloco catch.
Antes de continuar, com base no exemplo a seguir, entenda a execução do método main e o que é
apresentado no console após sua execução.
src/org/j6toj8/languageenhancements/multipleexception/MultipleException_Complete.java
try {
throw new NullPointerException();
} catch (NullPointerException | IllegalArgumentException | IllegalStateException e)
{
System.out.println("Exceção capturada: " + e);
} catch (Exception e) {
System.out.println("Exceção capturada: " + e);
}
}
O código anterior possui um bloco try-catch que você provavelmente já conhece. A novidade neste
código está no primeiro bloco catch, onde várias exceções são declaradas e capturadas ao mesmo
tempo.
Saída no console
2. Apenas uma variável é permitida para um bloco catch, e deve estar localizada no final.
18
src/org/j6toj8/languageenhancements/multipleexception/MultipleException_MultipleSameCatch.java
try {
throw new NullPointerException();
} catch (NullPointerException | IllegalArgumentException e) { // COMPILA -
múltiplas exceções no mesmo catch, só uma variável no final
System.out.println("Exceção capturada: " + e);
} catch (IllegalStateException ise | UnsupportedOperationException uoe) { // NÃO
COMPILA - mais de uma variável declarada
System.out.println("Exceção capturada: " + ise);
} catch (ClassCastException cce | ConcurrentModificationException) { // NÃO
COMPILA - só uma variável, mas no lugar errado
System.out.println("Exceção capturada: " + cce);
}
}
3. Não é permitido declarar exceções diferentes, mas que seriam redundantes levando em
consideração a herança.
src/org/j6toj8/languageenhancements/multipleexception/MultipleException_Redundant.java
try {
throw new NullPointerException();
} catch (RuntimeException | IllegalArgumentException e) { // NÃO COMPILA -
IllegalArgumentException herda de RuntimeException, logo seria redundante
System.out.println("Exceção capturada: " + e);
}
}
4. Ao fazer catch de múltiplas Exception, não é permitido sobrescrever a variável da exceção. Mas
é possível se for apenas uma Exception no catch.
19
src/org/j6toj8/languageenhancements/multipleexception/MultipleException_OverrideVar.java
try {
throw new NullPointerException();
} catch (NullPointerException | IllegalArgumentException e) {
e = new IllegalStateException(); // NÃO COMPILA - a variável não pode ser
sobrescrita quando está em um multi-catch
} catch (Exception e) {
e = new IllegalStateException(); // COMPILA - ainda é possível sobrescrever a
variável quando não é um multi-catch
}
}
5. Assim como nas versões anteriores, tipos mais genéricos de Exception devem vir depois, mais
abaixo nos catch’s.
src/org/j6toj8/languageenhancements/multipleexception/MultipleException_GenericsLower.java
try {
throw new NullPointerException();
} catch (Exception e) {
System.out.println("Exceção capturada: " + e);
} catch (NullPointerException | IllegalArgumentException e) { // NÃO COMPILA -
NullPointerException é mais específico que Exception, logo deveria ser capturada
antes de Exception
System.out.println("Exceção capturada: " + e);
}
}
6. Assim como nas versões anteriores, Exceções repetidas ainda são proibidas.
src/org/j6toj8/languageenhancements/multipleexception/MultipleException_RepeatException.java
try {
throw new NullPointerException();
} catch (NullPointerException | IllegalArgumentException e) {
System.out.println("Exceção capturada: " + e);
} catch (IllegalStateException | NullPointerException e) { // NÃO COMPILA -
NullPointerException já foi capturada no catch anterior
System.out.println("Exceção capturada: " + e);
}
}
20
7. Assim como nas versões anterior, Exceções checadas (aquelas que herdam de Exception) só
podem estar em um catch caso algo no try lance elas.
src/org/j6toj8/languageenhancements/multipleexception/MultipleException_CheckedException.java
try {
throw new NullPointerException();
} catch (NullPointerException | IOException e) { // NÃO COMPILA - IOException não
é lançada dentro do bloco try
System.out.println("Exceção capturada: " + e);
}
}
Referências
• Boyarsky, Jeanne; Selikoff, Scott. OCP: Oracle Certified Professional Java SE 8 Programmer
II Study Guide (p. 291). Wiley. Edição do Kindle.
• Catching Multiple Exception Types and Rethrowing Exceptions with Improved Type
Checking. Java Documentation.
Use static and default methods of an interface including inheritance rules for a
default method.
-
Usar métodos static e default de uma interface, incluindo regras de herança para um
método default.
É esperado que o candidato saiba compreender e analisar o uso da dos modificadores static e
default em métodos de interfaces.
Antes de continuar, com base no exemplo a seguir, entenda a execução do método main e o que é
apresentado no console após sua execução.
21
src/org/j6toj8/languageenhancements/staticdefaultininterfaces/StaticDefaultInInterfaces_Complete.java
interface Corredor {
static double calculeVelocidade(int distancia, int tempo) {
return distancia / tempo;
}
String correrRapido();
}
Saída no console
10.0
Correndo
Pessoa Correndo Rápido
O código anterior possui dois modificadores novos para interfaces, possíveis desde o Java 8: default
e static. É possível perceber que esses dois métodos possuem corpo, algo que não era possível
antes em uma interface. Então, vamos entender quais são as novas possibilidades.
2. Métodos com o modificador static em interfaces são chamados iguais aos de uma classe
comum, ou seja, não fazem parte da API da interface. Dessa forma, não são herdados pelas
classes que implementam essa interface.
22
src/org/j6toj8/languageenhancements/staticdefaultininterfaces/StaticDefaultInInterfaces_Static.java
interface Corredor {
static double calculeVelocidade(int distancia, int tempo) {
return distancia / tempo;
}
}
src/org/j6toj8/languageenhancements/staticdefaultininterfaces/StaticDefaultInInterfaces_Default.java
interface Corredor {
default String correr() {
return "Correndo";
}
}
Veja que a classe Pessoa não sobrescreve o método correr(), mantendo o comportamento
padrão da implementação feita na interface Corredor.
A classe Cavalo, por outro lado, sobrescreve o método correr() para ter sua própria
implementação.
23
Saída no console
Correndo
Galopando
5. Assim como os outros método de uma interface, os métodos static e default são sempre public,
e não podem ser modificados para private ou protected.
src/org/j6toj8/languageenhancements/staticdefaultininterfaces/StaticDefaultInInterfaces_AccessModifie
rs.java
interface Corredor {
default String correr() { // COMPILA - não há modificador de acesso declarado, é
automaticamente público
return "Correndo";
}
public default String correrRapido() { // COMPILA - modificador de acesso público
explícito
return "Correndo Rápido";
}
protected default String correrDevagar() { // NÃO COMPILA - o método deve ser
obrigatoriamente público
return "Correndo Devagar";
}
private default String correrExtremo() { // NÃO COMPILA - o método deve ser
obrigatoriamente público
return "Correndo ao Extremo";
}
6. Diferente dos outros método de uma interface, os métodos static e default não são abstract, e
também não podem ser. Afinal, eles possuem implementação. Apenas métodos sem
implementação são abstract.
24
src/org/j6toj8/languageenhancements/staticdefaultininterfaces/StaticDefaultInInterfaces_Abstract.java
interface Corredor {
default String correr() { // COMPILA - método default não é abstract
return "Correndo";
}
abstract default String correrRapido() { // NÃO COMPILA - método default não pode
ser declarado abstract
return "Correndo Rápido";
}
7. Se uma classe implementa duas interfaces que possuem métodos default repetidos, ela
obrigatoriamente deve implementar o seu próprio.
25
src/org/j6toj8/languageenhancements/staticdefaultininterfaces/StaticDefaultInInterfaces_RepeatedDefa
ult.java
interface Corredor {
default String correr() {
return "Correndo";
}
}
interface Piloto {
default String correr() {
return "Piloto Correndo";
}
}
static class Pessoa implements Corredor, Piloto { // NÃO COMPILA - implementa duas
interfaces com métodos repetidos e não sobrescreve
26
src/org/j6toj8/languageenhancements/staticdefaultininterfaces/StaticDefaultInInterfaces_RepeatedDefa
ultSuper.java
interface Corredor {
default String correr() {
return "Correndo";
}
}
interface Piloto {
default String correr() {
return "Piloto Correndo";
}
}
Saída no console
Correndo
9. Quando uma interface herda de outra interface métodos default, estes podem ser mantidos,
transformados em abstract ou redefinidos.
27
src/org/j6toj8/languageenhancements/staticdefaultininterfaces/StaticDefaultInInterfaces_InterfaceInher
itance.java
interface Corredor {
default String correr() {
return "Correndo";
}
default String correrRapido() {
return "Correndo Rápido";
}
default String correrDevagar() {
return "Correndo Devagar";
}
}
◦ Altera o método correrRapido() para que seja abstract, fazendo com que qualquer classe
que implemente a interface Piloto tenha que implementar esse método;
Referências
• Designing an Interface
Boyarsky, Jeanne; Selikoff, Scott. OCP: Oracle Certified Professional Java SE 8 Programmer
II Study Guide (p. 48). Wiley. Edição do Kindle.
28
Localization
Localização (Locale)
Objetivo
Describe the advantages of localizing an application and developing code that defines,
reads, and sets the locale with a Locale object.
-
Descrever as vantagens de localizar uma aplicação e desenvolver código que defina,
leia e aplique a localidade em um objeto Locale.
Alguns aspectos de uma aplicação podem depender do país e da linguagem. Por exemplo:
◦ O dia 6 de Agosto de 2019 seria representado no Brasil por 06/08/2019, porém nos EUA seria
08/06/2019.
◦ Dez Reais no Brasil seriam representados por R$ 10, enquanto na França Dez Euros seriam
10 €.
◦ No Brasil utiliza-se vírgula para separar decimais e pontos para milhares: 100.245,03. Nos
EUA, utiliza-se o inverso: 100,245.03.
Por isso, para que sua aplicação lide corretamente com esses cenários, é necessário compreender
dois aspectos: Internacionalização (Internationalization) e Localização (Localization).
A Internacionalização, também chamada de i18n, é o ato de projetar sua aplicação para que seja
possível facilmente adaptá-la para utilizar novos formatos e idiomas.
A Localização, também chamada de l10n, é o ato de adaptar sua aplicação para de fato utilizar um
novo formato específico.
Antes de continuar, entenda a execução do método main no exemplo a seguir e o que é apresentado
no console após sua execução.
29
src/org/j6toj8/localization/locale/Locale_Complete.java
30
Saída no console
- Constantes -
en_CA
en_GB
- Construtor -
pt_BR
pt_PT
ca_ES_VALENCIA
- Language Tags -
en_CA
pt_BR
pt_PT
ca_ES
gsw__#u-sd-chzh
- Builder -
pt_BR
az_AZ_#Latn
bs_BA_POSIX_#Latn_c-cc
No Java, a classe Locale no pacote java.util nos ajuda a lidar com esses aspectos.
src/org/j6toj8/localization/locale/Locale_LocaleLanguageCountry.java
31
src/org/j6toj8/localization/locale/Locale_VarScriptExtension.java
3. É possível construir um Locale com o Builder, com construtores, ou por uma Language Tag.
src/org/j6toj8/localization/locale/Locale_LocaleInstantiation.java
◦ Com os construtores é possível passar apenas o idioma, a região (país) e uma variante.
◦ Com language tags é possível passar uma String no padrão IETF BCP 47.
◦ Com o builder é possível criar da forma mais específica possível: idioma, região, variante,
script e extensões.
32
src/org/j6toj8/localization/locale/Locale_LocaleCase.java
System.out.println(Locale.forLanguageTag("PT-br"));
System.out.println(localePtBR);
}
Saída no console
pt_BR
pt_BR
pt_BR
Mesmo assim, o Locale é criado corretamente. Veja que isso é um código ruim. O ideal é
sempre escrever respeitando maiúsculas e minúsculas.
src/org/j6toj8/localization/locale/Locale_LocaleCommons.java
33
Saída no console
en_CA
fr_CA
zh_CN
zh
en
it_IT
zh_CN
zh_TW
src/org/j6toj8/localization/locale/Locale_LocaleDefault.java
Saída no console
pt_BR
ko_KR
7. É possível verificar os Locale disponíveis, pois eles variam de acordo com a JVM sendo
executada.
src/org/j6toj8/localization/locale/Locale_LocaleAvailable.java
34
Saída no console
nn
ar_JO
bg
kea
nds
zu
am_ET
fr_DZ
ti_ET
src/org/j6toj8/localization/locale/Locale_LocaleLanguageOnly.java
Saída no console
pt
en
es
fr
Referências
Boyarsky, Jeanne; Selikoff, Scott. OCP: Oracle Certified Professional Java SE 8 Programmer
II Study Guide (p. 255). Wiley. Edição do Kindle.
35
Objetivo
Build a resource bundle for a locale and call a resource bundle from an application.
-
Construa um resource bundle (conjunto de recursos) para um Locale e invoque um
resource bundle a partir de uma aplicação.
É esperado que o candidato saiba compreender e analisar o uso de resource bundles e sua relação
com a classe Locale.
Antes de continuar, entenda a execução do método main no exemplo a seguir e o que é apresentado
no console após sua execução.
src/org/j6toj8/localization/resourcebundle/ResourceBundle_Complete.java
36
../../../resources/Text.properties
phone=phone
tripod tripod
pen:pen
keyboard=keyboard
glass=glass
sticker sticker
paper:paper
rubber rubber
../../../resources/Text_pt.properties
paper = papel
../../../resources/Text_pt_BR.properties
../../../resources/Text_es.properties
keyboard=tec\
lado
../../../resources/Text_es_ES.properties
37
Saída no console
1. O nome do Locale é o sufixo do nome do arquivo, e o resource bundle padrão não tem sufixo.
Exemplos:
◦ Text_pt.properties → Locale pt
◦ Text_es.properties → Locale es
2. O arquivo .properties pode ser expresso com 3 separadores diferentes: = (igual), : (dois pontos)
ou um espaço em branco.
38
../../../resources/Text.properties
phone=phone
tripod tripod
pen:pen
keyboard=keyboard
glass=glass
sticker sticker
paper:paper
rubber rubber
../../../resources/Text_pt_BR.properties
../../../resources/Text_es_ES.properties
../../../resources/Text_pt.properties
paper = papel
Neste exemplo, não é possível ver, mas existem 3 espaços no final da linha. O resultado é o
mesmo que escrever paper=papel{sp}{sp}{sp}.
5. Em arquivos .properties, se você terminar a linha com uma contrabarra, pode quebrar a linha.
../../../resources/Text_es.properties
keyboard=tec\
lado
Neste exemplo, seria o mesmo que escrever em uma única linha: keyboard=teclado.
6. Em arquivos .properties, você também pode usar os caracteres java como \t e \n.
39
../../../resources/Text_es_ES.properties
Neste exemplo, existe uma tabulação antes da palavra vaso. Você pode perceber no primeiro
exemplo deste capítulo que a palavra vaso foi impressa no console com uma tabulação à
esquerda.
src/org/j6toj8/localization/resourcebundle/ResourceBundle_KeysProgrammatically.java
Saída no console
tripod - tripod
keyboard - keyboard
glass - glass
paper - papel
phone - phone
rubber - rubber
pen - caneta
sticker - sticker
40
resource/Text_fr_CA.java
import java.util.ListResourceBundle;
@Override
protected Object[][] getContents() {
return new Object[][] {
{"pen", "stylo"},
{"glass", "verre"},
{"keyboard", "clavier"}
};
}
}
src/org/j6toj8/localization/resourcebundle/ResourceBundle_JavaBundle.java
Saída no console
verre
9. Ao utilizar classes Java, uma vantagem é poder armazenar valores que não sejam apenas
String.
resource/Text_fr_FR.java
import java.math.BigDecimal;
import java.util.ListResourceBundle;
@Override
protected Object[][] getContents() {
return new Object[][] {
{"ten", new BigDecimal("10")}
};
}
}
10. A nomenclatura do arquivo é a mesma para classes Java e arquivos .properties, mudando
apenas a extensão.
41
11. Se houver um arquivo .properties e uma classe Java para o mesmo Locale, a classe Java é
utilizada.
resource/Text_fr_CA.java
import java.util.ListResourceBundle;
@Override
protected Object[][] getContents() {
return new Object[][] {
{"pen", "stylo"},
{"glass", "verre"},
{"keyboard", "clavier"}
};
}
}
resource/Text_fr_CA.properties
pen=stylo-in-ignored-properties
glass=stylo-in-ignored-properties
keyboard=stylo-in-ignored-properties
src/org/j6toj8/localization/resourcebundle/ResourceBundle_JavaClassTakesPrecedence.java
/*
* Recupera o Bundle do arquivo "Text_fr_CA.java",
* pois tem precedência sobre o arquivo "Text_fr_CA.properties"
*/
ResourceBundle bundle = ResourceBundle.getBundle("Text", new Locale("fr", "CA"));
System.out.println(bundle.getString("pen"));
System.out.println(bundle.getString("glass"));
System.out.println(bundle.getString("keyboard"));
Saída no console
stylo
verre
clavier
Veja que os valores apresentados no console são aqueles definidos no arquivo Text_fr_CA.java,
mostrando que a classe Java tem precedência sobre um arquivo .properties para o mesmo
Locale.
12. Ao buscar por um resource bundle, o Java tenta encontrar um arquivo com o Locale exato. Se
não encontrar, busca na seguinte ordem:
42
a. Um arquivo do mesmo idioma, mas sem o país;
Por exemplo, ao executar a aplicação com o Locale padrão en_US, e solicitar um Locale pt_BR,
a ordem de busca do resource bundle seria a seguinte:
src/org/j6toj8/localization/resourcebundle/ResourceBundle_NotExactLocale.java
Saída no console
pt_BR
it
Veja que o Locale padrão é pt_BR. Por isso ele foi utilizado ao solicitar um resource bundle
para zh_CN, pois não existe um bundle para esse Locale.
Por outro lado, ao solicitar um resource bundle para o Locale it_CH, ele encontrou o mais
próximo, que seria o Locale it, mas sem um país específico.
13. Os arquivos mais específicos herdam as chaves e valores de arquivos mais genéricos, caso eles
não as tenham.
43
src/org/j6toj8/localization/resourcebundle/ResourceBundle_Inheritance.java
../../../resources/Text.properties
phone=phone
tripod tripod
pen:pen
keyboard=keyboard
glass=glass
sticker sticker
paper:paper
rubber rubber
../../../resources/Text_pt.properties
paper = papel
../../../resources/Text_pt_BR.properties
Saída no console
Locale: pt_BR
caneta
papel
keyboard
Veja que nesse exemplo foi localizado um resource bundle com o Locale exato pt_BR. Porém, nem
todas as chaves foram encontradas nesse arquivo:
44
Referências
Boyarsky, Jeanne; Selikoff, Scott. OCP: Oracle Certified Professional Java SE 8 Programmer
II Study Guide (p. 258). Wiley. Edição do Kindle.
Data e Hora
Objetivo
Create and manage date- and time-based events by using LocalDate, LocalTime,
LocalDateTime, Instant, Period, and Duration, including a combination of date and time
in a single object.
-
Crie e gerencie eventos baseados em data e hora utilizando LocalDate, LocalTime,
LocalDateTime, Instant, Period, e Duration, incluindo combinação de data e hora em um
único objeto.
O Java 8 possui uma forma totalmente nova de lidar com data e hora. Várias classes novas foram
introduzidas no pacote java.time.*. Vejamos a seguir alguns exemplos no formado ISO-8601.
• LocalDateTime → Uma data com hora sem fuso horário, como 2007-12-03T10:15:30.125.
• ZonedDateTime → Uma data com hora e com fuso horário, como 2007-12-03T10:15:30.125+01:00
Europe/Paris.
• Period → Uma quantidade de tempo baseado em data, como "2 anos, 3 meses and 4 dias".
Essas novas classes foram melhor projetadas para lidar com os conceitos de data, hora e tempo. As
classes antigas, java.util.Date e java.util.Calendar, não caem na prova de certificação.
Quase todas essas classes serão apresentadas nessa seção. A única exceção é ZonedDateTime, que será
apresentada na seção de fusos horários e horário de verão.
Todas as novas classes são imutáveis e thread safe. Ou seja, não é necessário se preocupar com
concorrência.
45
LocalDate
src/org/j6toj8/localization/datetime/localdate/LocalDate_Now.java
System.out.println(LocalDate.now());
Saída no console
2019-05-20
5. Diferente das APIs antigas do Java, o mês agora começa no número 1, que é Janeiro.
src/org/j6toj8/localization/datetime/localdate/LocalDate_Of.java
System.out.println(LocalDate.of(2019, 5, 20));
System.out.println(LocalDate.of(2019, Month.MAY, 20));
Saída no console
2019-05-20
2019-05-20
6. Assim como todas a novas classes de data e hora, não é possível criar uma instância de
LocalDate utilizando o construtor.
src/org/j6toj8/localization/datetime/localdate/LocalDate_Constructor.java
src/org/j6toj8/localization/datetime/localdate/LocalDate_Invalid.java
46
Saída no console
src/org/j6toj8/localization/datetime/localdate/LocalDate_Manipulate.java
Saída no console
2019-05-20
+2 dias: 2019-05-22
+2 semanas: 2019-06-03
+2 meses: 2019-07-20
+2 anos: 2021-05-20
+2 décadas: 2039-05-20
-2 dias: 2019-05-18
-2 semanas: 2019-05-06
-2 meses: 2019-03-20
-2 anos: 2017-05-20
-2 décadas: 1999-05-20
47
src/org/j6toj8/localization/datetime/localdate/LocalDate_Immutability.java
Saída no console
2019-05-20
2019-05-20
2019-05-21
src/org/j6toj8/localization/datetime/localdate/LocalDate_Chaining.java
Saída no console
2019-05-20
2020-06-21
11. Ao manipular a data, o LocalDate irá manipular os meses e anos conforme necessário.
src/org/j6toj8/localization/datetime/localdate/LocalDate_AdjustDifferentUnit.java
Saída no console
2019-11-30
2019-12-01
2020-01-01
2020-01-30
48
LocalTime
Um LocalTime representa uma hora sem fuso horário e sem data, como 10:15:30.125.
src/org/j6toj8/localization/datetime/localtime/LocalTime_Now.java
System.out.println(LocalTime.now());
Saída no console
09:15:23.197
src/org/j6toj8/localization/datetime/localtime/LocalTime_Of.java
Saída no console
09:20:01.135
09:20:01.000000135
09:20:01
09:20
4. Assim como todas a novas classes de data e hora, não é possível criar uma instância de
LocalTime utilizando o construtor.
src/org/j6toj8/localization/datetime/localtime/LocalTime_Constructor.java
49
src/org/j6toj8/localization/datetime/localtime/LocalTime_Invalid.java
Saída no console
src/org/j6toj8/localization/datetime/localtime/LocalTime_Manipulate.java
50
Saída no console
09:26:12
+2 horas: 11:26:12
+2 minutos: 09:28:12
+2 segundos: 09:26:14
+2 nanosegundos: 09:26:12.000000002
+2 microssegundos: 09:26:12.000002
+2 milissegundos: 09:26:12.002
-2 horas: 07:26:12
-2 minutos: 09:24:12
-2 segundos: 09:26:10
-2 nanosegundos: 09:26:11.999999998
-2 microssegundos: 09:26:11.999998
-2 milissegundos: 09:26:11.998
src/org/j6toj8/localization/datetime/localtime/LocalTime_Immutability.java
Saída no console
09:31:05
09:31:05
10:31:05
src/org/j6toj8/localization/datetime/localtime/LocalTime_Chaining.java
Saída no console
09:32:05
10:33:06.001
51
9. Ao manipular a hora, o LocalTime irá manipular as horas, minutos e segundos conforme
necessário.
src/org/j6toj8/localization/datetime/localtime/LocalTime_AdjustDifferentUnit.java
Saída no console
09:59:59
10:00:01
10:01:01
10:01:59
09:59:58
LocalDateTime
Um LocalDateTime representa uma data com hora, mas sem fuso horário, como 2007-12-
03T10:15:30.125.
As regras para o LocalDateTime são basicamente a junção daquelas para LocalDate e LocalTime.
src/org/j6toj8/localization/datetime/localdatetime/LocalDateTime_Now.java
System.out.println(LocalDateTime.now());
Saída no console
2019-05-24T10:13:58.370
52
src/org/j6toj8/localization/datetime/localdatetime/LocalDateTime_Of.java
Saída no console
2019-05-20T09:20:12
2019-05-20T09:20
2019-05-20T09:20
2019-05-20T09:20:12
2019-05-20T09:20:12
2019-05-20T09:20:12.000000135
2019-05-20T09:20:12.000000135
4. Assim como todas a novas classes de data e hora, não é possível criar uma instância de
LocalDateTime utilizando o construtor.
src/org/j6toj8/localization/datetime/localdatetime/LocalDateTime_Constructor.java
5. Será lançada a exceção DateTimeException ao tentar criar uma data ou hora inválida.
src/org/j6toj8/localization/datetime/localdatetime/LocalDateTime_Invalid.java
Saída no console
6. Existem vários métodos para somar e subtrair de LocalDateTime. São basicamente todos os
53
disponíveis para LocalDate e LocalTime.
src/org/j6toj8/localization/datetime/localdatetime/LocalDateTime_Manipulate.java
54
Saída no console
2019-05-20T09:20:12
+2 horas: 2019-05-20T11:20:12
+2 minutos: 2019-05-20T09:22:12
+2 segundos: 2019-05-20T09:20:14
+2 nanosegundos: 2019-05-20T09:20:12.000000002
+2 microssegundos: 2019-05-20T09:20:12.000002
+2 milissegundos: 2019-05-20T09:20:12.002
-2 horas: 2019-05-20T07:20:12
-2 minutos: 2019-05-20T09:18:12
-2 segundos: 2019-05-20T09:20:10
-2 nanosegundos: 2019-05-20T09:20:11.999999998
-2 microssegundos: 2019-05-20T09:20:11.999998
-2 milissegundos: 2019-05-20T09:20:11.998
+2 dias: 2019-05-22T09:20:12
+2 semanas: 2019-06-03T09:20:12
+2 meses: 2019-07-20T09:20:12
+2 anos: 2021-05-20T09:20:12
+2 décadas: 2039-05-20T09:20:12
-2 dias: 2019-05-18T09:20:12
-2 semanas: 2019-05-06T09:20:12
-2 meses: 2019-03-20T09:20:12
-2 anos: 2017-05-20T09:20:12
-2 décadas: 1999-05-20T09:20:12
src/org/j6toj8/localization/datetime/localdatetime/LocalDateTime_Immutability.java
Saída no console
2019-05-20T09:20
2019-05-20T09:20
2019-05-20T10:20
55
src/org/j6toj8/localization/datetime/localdatetime/LocalDateTime_Chaining.java
Saída no console
2019-05-20T09:20
2020-05-21T10:20
src/org/j6toj8/localization/datetime/localdatetime/LocalDateTime_AdjustDifferentUnit.java
Saída no console
2019-12-31T23:59:59
2020-01-01T00:00:01
Instant
Um Instant representa um momento na linha do tempo, no fuso horário do UTC (às vezes chamado
de GMT), como 2007-12-03T10:15:30.125Z.
O Instant difere de todos os tipos apresentados anteriormente, pois possui um fuso horário (UTC) e
representa o mesmo momento na linha do tempo em qualquer lugar do mundo.
src/org/j6toj8/localization/datetime/instant/Instant_Now.java
System.out.println(Instant.now());
Saída no console
2019-05-27T21:13:10.450Z
A saída no console irá apresentar a data e hora atual no fuso horário UTC.
3. Também é possível criar um Instant através dos métodos static chamados ofEpochMilli ou
56
ofEpochSecond. O parâmetro é a quantidade de milissegundos, ou segundos, desde 1970-01-
01T00:00:00Z.
src/org/j6toj8/localization/datetime/instant/Instant_Of.java
System.out.println(Instant.ofEpochMilli(1000000000000L));
System.out.println(Instant.ofEpochSecond(1000000000));
System.out.println(Instant.ofEpochSecond(1000000000, 123000000)); // com
nanossegundos
Saída no console
2001-09-09T01:46:40Z
2001-09-09T01:46:40Z
2001-09-09T01:46:40.123Z
4. Assim como todas a novas classes de data e hora, não é possível criar uma instância de Instant
utilizando o construtor.
src/org/j6toj8/localization/datetime/instant/Instant_Constructor.java
Instant instant = new Instant(); // NÃO COMPILA! - não possui construtor padrão
System.out.println(instant);
5. Existem vários métodos para somar e subtrair de Instant. Porém, não suporta operações com
unidades maiores do que dias.
src/org/j6toj8/localization/datetime/instant/Instant_Manipulate.java
57
Saída no console
2001-09-09T01:46:40Z
+2 segundos: 2001-09-09T01:46:42Z
+2 nanosegundos: 2001-09-09T01:46:40.000000002Z
+2 microssegundos: 2001-09-09T01:46:40.000002Z
+2 milissegundos: 2001-09-09T01:46:40.002Z
-2 segundos: 2001-09-09T01:46:38Z
-2 nanosegundos: 2001-09-09T01:46:39.999999998Z
-2 microssegundos: 2001-09-09T01:46:39.999998Z
-2 milissegundos: 2001-09-09T01:46:39.998Z
+2 dias: 2001-09-11T01:46:40Z
Exception in thread "main" java.time.temporal.UnsupportedTemporalTypeException:
Unsupported unit: Weeks
at java.time.Instant.plus(Instant.java:862)
at
org.j6toj8.localization.datetime.instant.Instant_Manipulate.main(Instant_Manipulate
.java:21)
6. Instant é imutável, então é necessário armazenar o retorno de uma alteração em uma variável.
7. Assim como as outras classes de data e hora, é comum utilizar o encadeamento de chamadas
com esses métodos.
8. Assim como as outras classes de data e hora, ao manipular o Instant irá manipular os outros
campos conforme necessário.
src/org/j6toj8/localization/datetime/instant/Instant_Immutability.java
Saída no console
2001-09-09T01:46:40Z
2001-09-09T01:46:40Z
2001-09-09T01:47:40Z
58
src/org/j6toj8/localization/datetime/instant/Instant_Convert.java
Saída no console
LocalDateTime: 2019-05-27T13:01:01
2019-05-27T13:01:01Z
2019-05-27T16:01:01Z
Instant: 2019-05-27T13:01:01Z
2019-05-27T13:01:01
2019-05-27T10:01:01
Period
Um Period é uma quantidade de tempo baseado em datas, como anos, meses ou dias. Por exemplo: 2
anos, 3 meses e 4 dias.
2. Ao imprimir um Period, apenas os campos com valor são apresentados. Campos zerados são
omitidos.
src/org/j6toj8/localization/datetime/period/Period_Of.java
59
Saída no console
P2D
P2M
P14D
P2Y
P2Y1M3D
Perceba que o período de 2 semanas é apresentado como 14D, pois Period não armazena
semanas. O método ofWeeks é apenas um facilitador, onde cada semana é o equivalente a 7 dias.
src/org/j6toj8/localization/datetime/period/Period_Between.java
Saída no console
P28Y9M22D
No exemplo anterior podemos ver que uma pessoa nascida no dia 6 de agosto de 1990 possuía
28 anos, 9 meses e 22 dias de idade no dia em que esse código foi executado: 28/05/2019.
5. Period também armazena uma quantidade de dias maior que um mês, e de meses maior que
um ano.
src/org/j6toj8/localization/datetime/period/Period_BiggerValues.java
Saída no console
P60M2D
P30M50D
P5Y200M1000D
60
Curiosidades!
É possível normalizar um Period que não está com o intervalo padrão através
do método normalized. Mas serão considerados apenas os anos e meses,
deixando os dias intactos.
src/org/j6toj8/localization/datetime/period/Period_Curiosities.java
src/org/j6toj8/localization/datetime/period/Period_Curiosities.java
src/org/j6toj8/localization/datetime/period/Period_Compatibility.java
61
Saída no console
Period: P10D
Instant: 2019-05-27T13:01:01Z
Instant + Period: 2019-06-06T13:01:01Z
LocalDate: 2018-05-27
LocalDate + Period: 2018-06-06
LocalDateTime: 2018-05-27T13:01:01
LocalDateTime + Period: 2018-06-06T13:01:01
7. Não é possível somar um Period a um LocalTime, pois o primeiro representa tempo baseado em
datas, e o segundo armazena apenas horários.
src/org/j6toj8/localization/datetime/period/Period_LocalTime.java
Saída no console
Period: P13D
LocalTime: 13:01:01
Exception in thread "main" java.time.temporal.UnsupportedTemporalTypeException:
Unsupported unit: Days
at java.time.LocalTime.plus(LocalTime.java:1055)
at java.time.LocalTime.plus(LocalTime.java:125)
at java.time.Period.addTo(Period.java:906)
at java.time.LocalTime.plus(LocalTime.java:988)
at
org.j6toj8.localization.datetime.period.Period_LocalTime.main(Period_LocalTime.java
:15)
8. Não é possível somar um Period que contém meses ou anos a um Instant. Como vimos na seção
anterior sobre Instant, ele não suporta operações com valores maiores que dias.
62
src/org/j6toj8/localization/datetime/period/Period_Instant.java
Saída no console
Period: P1M
Instant: 2019-05-27T13:01:01Z
Exception in thread "main" java.time.temporal.UnsupportedTemporalTypeException:
Unsupported unit: Months
at java.time.Instant.plus(Instant.java:862)
at java.time.Instant.plus(Instant.java:207)
at java.time.Period.addTo(Period.java:902)
at java.time.Instant.plus(Instant.java:788)
at
org.j6toj8.localization.datetime.period.Period_Instant.main(Period_Instant.java:15)
Duration
Uma Duration é uma quantidade de tempo baseado em hora, como segundos, minutos ou horas. Por
exemplo: 2 horas, 3 minutos e 4 segundos.
2. Assim como um Period, ao imprimir uma Duration apenas os campos com valor são
apresentados. Campos zerados são omitidos.
3. É possível criar uma Duration através dos métodos static chamados of*.
63
src/org/j6toj8/localization/datetime/duration/Duration_Of.java
System.out.println(Duration.ofNanos(2)); // 2 nanossegundos
System.out.println(Duration.ofMillis(2)); // 2 milissegundos
System.out.println(Duration.ofSeconds(2)); // 2 segundos
System.out.println(Duration.ofMinutes(2)); // 2 minutos
System.out.println(Duration.ofHours(2)); // 2 horas
System.out.println(Duration.ofDays(2)); // 2 dias (48 horas)
System.out.println(Duration.ofSeconds(2, 200)); // 2,0000002 segundos (2 segundos e
200 nanossegundos)
System.out.println(Duration.of(2, ChronoUnit.MICROS)); // 2 microssegundos
Saída no console
PT0.000000002S
PT0.002S
PT2S
PT2M
PT2H
PT48H
PT2.0000002S
PT0.000002S
Perceba que 2 dias é armazenado como 48 horas, pois Duration não armazena dias.
src/org/j6toj8/localization/datetime/duration/Duration_Between.java
System.out.println(Duration.between(meiaNoite, meioDia));
System.out.println(Duration.between(meioDia, meiaNoite));
System.out.println(Duration.between(meioDia, meioDia));
Saída no console
PT12H
PT-12H
PT0S
64
src/org/j6toj8/localization/datetime/duration/Duration_BiggerValues.java
Saída no console
PT2H
PT2H1M
PT1H59M
PT2H46M40S
src/org/j6toj8/localization/datetime/duration/Duration_Compatibility.java
Saída no console
Duration: PT2H
Instant: 2019-05-27T13:01:01Z
Instant + Duration: 2019-05-27T15:01:01Z
LocalTime: 12:05:02
LocalTime + Duration: 14:05:02
LocalDateTime: 2018-05-27T13:01:01
LocalDateTime + Duration: 2018-05-27T15:01:01
7. Não é possível somar uma Duration a uma LocalDate, pois o primeiro representa tempo baseado
em hora, enquanto o outro armazena apenas datas.
65
src/org/j6toj8/localization/datetime/duration/Duration_LocalDate.java
Saída no console
Duration: PT2H
LocalTime: 1990-08-06
Exception in thread "main" java.time.temporal.UnsupportedTemporalTypeException:
Unsupported unit: Seconds
at java.time.LocalDate.plus(LocalDate.java:1247)
at java.time.LocalDate.plus(LocalDate.java:137)
at java.time.Duration.addTo(Duration.java:1070)
at java.time.LocalDate.plus(LocalDate.java:1149)
at
org.j6toj8.localization.datetime.duration.Duration_LocalDate.main(Duration_LocalDat
e.java:15)
Veja que ocorreu exceção ao tentar somar uma Duration a uma LocalDate.
Referências
Boyarsky, Jeanne; Selikoff, Scott. OCP: Oracle Certified Professional Java SE 8 Programmer
II Study Guide (p. 234). Wiley. Edição do Kindle.
Format dates, numbers, and currency values for localization with the NumberFormat and
DateFormat classes, including number and date format patterns.
-
Formatar datas, números e valores monetários para localização utilizando as classes
NumberFormat e DateFormat, incluindo padrões de formato de número e data.
66
Ainda dentro do assunto de Localização e Internacionalização, é comum a necessidade de
apresentar datas, números e valores monetários em diferentes formatos.
NumberFormat
1. É possível obter uma instância de NumberFormat a partir de vários métodos estáticos, dependendo
da necessidade.
src/org/j6toj8/localization/formats/numberformat/NumberFormat_Instance.java
// sem Locale
NumberFormat instance1 = NumberFormat.getInstance();
NumberFormat instance2 = NumberFormat.getNumberInstance(); // igual a getInstance()
NumberFormat instance3 = NumberFormat.getCurrencyInstance();
NumberFormat instance4 = NumberFormat.getPercentInstance();
// com Locale
NumberFormat instance5 = NumberFormat.getInstance(new Locale("pt", "BR"));
NumberFormat instance6 = NumberFormat.getNumberInstance(new Locale("pt", "BR"));
NumberFormat instance7 = NumberFormat.getCurrencyInstance(new Locale("pt", "BR"));
NumberFormat instance8 = NumberFormat.getPercentInstance(new Locale("pt", "BR"));
Lembre-se que, se não for informado o Locale, será utilizado o padrão. O ideal é sempre
informar o Locale.
67
src/org/j6toj8/localization/formats/numberformat/NumberFormat_NumberToString.java
double d = 1000.05;
Saída no console
pt_BR: 1.000,05
en_US: 1,000.05
fr_FR: 1 000,05
src/org/j6toj8/localization/formats/numberformat/NumberFormat_StringToNumber.java
String s = "1000,05";
try {
Number parsePtBR = numberFormatPtBR.parse(s);
Number parseEnUS = numberFormatEnUS.parse(s);
Number parseFrFR = numberFormatFrFR.parse(s);
Saída no console
pt_BR: 1000.05
en_US: 100005
fr_FR: 1000.05
Perceba que dependendo do Locale estamos representando um número diferente, e isso muda o
68
resultado do parse.
4. O NumberFormat pode ser utilizado para transformar Strings em valores monetários, e vice-versa.
src/org/j6toj8/localization/formats/numberformat/NumberFormat_Currency.java
try {
System.out.println("pt_BR: " + currencyFormatPtBR.parse(s));
} catch (ParseException e) {
System.out.println(e.getMessage());
}
try {
System.out.println("en_US: " + currencyFormatEnUS.parse(s));
} catch (ParseException e) {
System.out.println(e.getMessage());
}
try {
System.out.println("fr_FR: " + currencyFormatFrFR.parse(s));
} catch (ParseException e) {
System.out.println(e.getMessage());
}
Saída no console
pt_BR: R$ 1.000,05
en_US: $1,000.05
fr_FR: 1 000,05 €
pt_BR: 1000.05
Unparseable number: "R$ 1000,05"
Unparseable number: "R$ 1000,05"
69
Perceba que novamente o resultado muda de acordo com o Locale. Além disso, não é possível
converter a representação da moeda brasileira com um Locale en_US ou fr_FR.
src/org/j6toj8/localization/formats/numberformat/NumberFormat_Percent.java
try {
System.out.println("pt_BR: " + percentFormatPtBR.parse(s));
System.out.println("en_US: " + percentFormatEnUS.parse(s));
System.out.println("fr_FR: " + percentFormatFrFR.parse(s));
} catch (ParseException e) {
System.out.println(e.getMessage());
}
Saída no console
pt_BR: 90%
en_US: 90%
fr_FR: 90 %
pt_BR: 0.8
en_US: 0.8
Unparseable number: "80%"
Veja que, ao formatar, 100% é 1, logo 80% é 0,8. Além disso, no Locale fr_FR a representação 80%
não é válida.
70
src/org/j6toj8/localization/formats/numberformat/NumberFormat_Percent2.java
try {
System.out.println("pt_BR: " + percentFormatPtBR.parse(s));
System.out.println("en_US: " + percentFormatEnUS.parse(s));
} catch (ParseException e) {
// trate a exceção de parse
}
Saída no console
pt_BR: 0.802
en_US: 8.02
No Locale pt_BR temos o resultado esperado. Porém, no Locale en_US o 80,2% se torna 802% pois a
vírgula não é usada como separador de decimal.
DecimalFormat
71
src/org/j6toj8/localization/formats/decimalformat/DecimalFormat_Instance.java
double d = 12345.67;
// omite todas as posições vazias, não utiliza separador e não apresenta casas
decimais
DecimalFormat instance7 = new DecimalFormat("###");
System.out.println("###: " + instance7.format(d));
Saída no console
###,###.###: 12.345,67
000,000.###: 012.345,67
###,###.000: 12.345,670
000,000.000: 012.345,670
###.##: 12345,67
000000.000: 012345,670
###: 12346
Estou executando o código onde o Locale padrão é pt_BR, por isso a saída no console apresenta
vírgulas para separar grupos e pontos para separar os decimais.
2. Para utilizar um Locale específico é necessário instanciar um NumberFormat e fazer um cast para
DecimalFormat.
72
src/org/j6toj8/localization/formats/decimalformat/DecimalFormat_Locale.java
double d = 12345.67;
Saída no console
12,345.67
src/org/j6toj8/localization/formats/decimalformat/DecimalFormat_Strings.java
double d = 12345.67;
Saída no console
DateFormat
Para formatar Data e Hora antes do Java 8, são utilizadas as classes de DateFormat e
SimpleDateFormat. Essas classes trabalham em geral com a classe Date e fazem parte do pacote
java.text, diferente de DateTimeFormatter que é do novo pacote java.time.format.
2. É possível definir o formato das instâncias como SHORT, MEDIUM, LONG ou FULL.
73
src/org/j6toj8/localization/formats/dateformat/DateFormat_Instance.java
System.out.println(dateInstance.format(date));
System.out.println(timeInstance.format(date));
System.out.println(dateTimeInstance.format(date));
System.out.println(dateTimeLongInstance.format(date));
System.out.println(dateTimeUSInstance.format(date));
Saída no console
08/09/2001
22:46:40
08/09/2001 22:46:40
8 de Setembro de 2001 22h46min40s BRT
September 8, 2001 10:46:40 PM BRT
src/org/j6toj8/localization/formats/dateformat/DateFormat_Parse.java
try {
System.out.println(dateInstance.parse(date));
System.out.println(timeInstance.parse(time));
System.out.println(dateTimeInstance.parse(dateTime));
System.out.println(dateTimeInstance.parse(date)); // exceção, pois date não tem
hora
} catch (ParseException e) {
System.out.println(e.getMessage());
}
74
Saída no console
Perceba que ocorreu uma exceção ao tentar converter uma data utilizando um formatar que
espera Data e Hora.
SimpleDateFormat
A classe SimpleDateFormat permite criar formatos mais personalizados para apresentar Data e Hora,
como dd/MM/yyyy HH:mm:ss. A seguir, as letras mais importantes utilizadas na formatação para o
exame:
• d → Dia (06)
• m → Minutos
• s → Segundos
Em geral (existem exceções), quanto mais letras forem utilizadas, mais extenso é o formato
apresentado. Por exemplo:
• M→8
• MM → 08
• MMM → Ago
• MMMM → Agosto
75
src/org/j6toj8/localization/formats/simpledateformat/SimpleDateFormat_Instance.java
System.out.println(simpleDateTime.format(date));
System.out.println(simpleDate.format(date));
System.out.println(simpleTime.format(date));
Saída no console
08 09 01 - 22 46 40
08 09 01
22 46 40
src/org/j6toj8/localization/formats/simpledateformat/SimpleDateFormat_Parse.java
try {
System.out.println(simpleDateTime.parse(dateTime));
System.out.println(simpleDate.parse(date));
System.out.println(simpleTime.parse(time));
System.out.println(simpleDateTime.parse(time)); // exceção, pois time não tem
data
} catch (ParseException e) {
System.out.println(e.getMessage());
}
Saída no console
76
DateTimeFormatter
O Java 8 traz a classe DateTimeFormatter que possui várias formas de formatar e transformar
Data/Hora em String, e vice-versa.
src/org/j6toj8/localization/formats/datetimeformatter/DateTimeFormatter_Predefined.java
Saída no console
2019-08-06
11:40:00
2019-08-06T11:40:00
2019-218
2019-W32-2
src/org/j6toj8/localization/formats/datetimeformatter/DateTimeFormatter_FormatStyle.java
System.out.println(localDT.format(dateTimeShortFormatter));
System.out.println(localDT.format(dateTimeMediumFormatter));
System.out.println(localDT.format(dateShortFormatter));
System.out.println(localDT.format(timeShortFormatter));
77
Saída no console
06/08/19 11:40
06/08/2019 11:40:00
06/08/19
11:40
O resultado depende de onde o código está sendo executado. Este código foi executado no Locale
padrão pt_BR.
src/org/j6toj8/localization/formats/datetimeformatter/DateTimeFormatter_Locale.java
shortFormatter = shortFormatter.withLocale(Locale.US);
mediumFormatter = mediumFormatter.withLocale(Locale.US);
System.out.println(localDT.format(shortFormatter));
System.out.println(localDT.format(mediumFormatter));
Saída no console
8/6/19 11:40 AM
Aug 6, 2019 11:40:00 AM
src/org/j6toj8/localization/formats/datetimeformatter/DateTimeFormatter_Custom.java
System.out.println(localDT.format(customFormatter));
Saída no console
06 08 19 - 11 40 00
78
formato representa. Volte na seção de SimpleDateFormat caso não se lembre.
5. Não é possível formatar uma Data/Hora caso o objeto fornecido não tenha os campos
necessários. Um exemplo seria tentar apresentar a Data e fornecer um LocalTime.
src/org/j6toj8/localization/formats/datetimeformatter/DateTimeFormatter_Error.java
System.out.println(localDate.format(DateTimeFormatter.ISO_LOCAL_DATE));
System.out.println(localDT.format(DateTimeFormatter.ISO_LOCAL_DATE));
Saída no console
2019-08-06
2019-08-06
Exception in thread "main" java.time.temporal.UnsupportedTemporalTypeException:
Unsupported field: Year
at java.time.LocalTime.get0(LocalTime.java:679)
at java.time.LocalTime.getLong(LocalTime.java:656)
at java.time.format.DateTimePrintContext$1.getLong(DateTimePrintContext.java:205)
at java.time.format.DateTimePrintContext.getValue(DateTimePrintContext.java:298)
at
java.time.format.DateTimeFormatterBuilder$NumberPrinterParser.format(DateTimeFormat
terBuilder.java:2551)
at
java.time.format.DateTimeFormatterBuilder$CompositePrinterParser.format(DateTimeFor
matterBuilder.java:2190)
at java.time.format.DateTimeFormatter.formatTo(DateTimeFormatter.java:1746)
at java.time.format.DateTimeFormatter.format(DateTimeFormatter.java:1720)
at java.time.LocalTime.format(LocalTime.java:1413)
at
org.j6toj8.localization.formats.datetimeformatter.DateTimeFormatter_Error.main(Date
TimeFormatter_Error.java:18)
79
src/org/j6toj8/localization/formats/datetimeformatter/DateTimeFormatter_ErrorCustom.java
System.out.println(localDT.format(formatter));
System.out.println(localTime.format(formatter));
System.out.println(localDate.format(formatter)); // lança exceção pois não possui
campos de hora
Saída no console
11 40 00
11 40 00
Exception in thread "main" java.time.temporal.UnsupportedTemporalTypeException:
Unsupported field: HourOfDay
at java.time.LocalDate.get0(LocalDate.java:680)
at java.time.LocalDate.getLong(LocalDate.java:659)
at java.time.format.DateTimePrintContext.getValue(DateTimePrintContext.java:298)
at
java.time.format.DateTimeFormatterBuilder$NumberPrinterParser.format(DateTimeFormat
terBuilder.java:2551)
at
java.time.format.DateTimeFormatterBuilder$CompositePrinterParser.format(DateTimeFor
matterBuilder.java:2190)
at java.time.format.DateTimeFormatter.formatTo(DateTimeFormatter.java:1746)
at java.time.format.DateTimeFormatter.format(DateTimeFormatter.java:1720)
at java.time.LocalDate.format(LocalDate.java:1691)
at
org.j6toj8.localization.formats.datetimeformatter.DateTimeFormatter_ErrorCustom.mai
n(DateTimeFormatter_ErrorCustom.java:20)
Nesse caso é lançada exceção porque LocalDate não possui hora, minuto ou segundo.
7. Também é possível fazer o oposto: converter uma String em uma classe de Data/Hora. Para isso
existem os métodos parse.
80
src/org/j6toj8/localization/formats/datetimeformatter/DateTimeFormatter_Parse.java
System.out.println(parseCustom);
System.out.println(parseIso);
System.out.println(parseShort);
Saída no console
2019-08-06T11:40
2019-08-06T11:40
2019-08-06T11:40
8. Todos os métodos utilizados para format e parse também podem ser invocados diretamente na
instância do DateTimeFormatter.
src/org/j6toj8/localization/formats/datetimeformatter/DateTimeFormatter_Inverted.java
Saída no console
2019-08-06T11:40
2019-08-06T11:40:00
{},ISO resolved to 2019-08-06T11:40
2019-08-06T11:40
81
Referências
Boyarsky, Jeanne; Selikoff, Scott. OCP: Oracle Certified Professional Java SE 8 Programmer
II Study Guide (p. 255). Wiley. Edição do Kindle.
Boyarsky, Jeanne; Selikoff, Scott. OCP: Oracle Certified Professional Java SE 8 Programmer
II Study Guide (p. 603). Wiley. Edição do Kindle.
• Guide to DateTimeFormatter.
• A Guide to SimpleDateFormat.
Fusos Horários
Objetivo
Work with dates and times across time zones and manage changes resulting from daylight
savings.
-
Trabalhe com datas e horas entre fusos horários e gerencie alterações resultantes de
horários de verão.
O exame de certificação espera que você consiga lidar com diferentes fusos horários, além de
conseguir entender o que ocorre ao realizar operações com datas e horas que passam por um
horário de verão.
Nessa seção será apresenta a classe ZonedDateTime, que armazena Data, Hora e um fuso horário. Ou
seja, é muito parecida com a classe LocalDateTime, porém possui um fuso horário.
src/org/j6toj8/localization/timezones/ZonedDateTime_Now.java
System.out.println(ZonedDateTime.now());
82
Saída no console
2019-06-09T18:10:08.863-03:00[America/Sao_Paulo]
A saída no console irá apresentar a data e hora atual, e o fuso horário de onde o código está
sendo executado.
src/org/j6toj8/localization/timezones/ZonedDateTime_Of.java
Saída no console
2019-06-09T13:20:03.000001-03:00[America/Sao_Paulo]
2019-06-09T13:20:03.000001-03:00[America/Sao_Paulo]
2019-06-09T13:20:03.000001-03:00[America/Sao_Paulo]
3. Assim como todas a novas classes de data e hora, não é possível criar uma instância de
ZonedDateTime utilizando o construtor.
src/org/j6toj8/localization/timezones/ZonedDateTime_Constructor.java
src/org/j6toj8/localization/timezones/ZonedDateTime_Invalid.java
83
Saída no console
src/org/j6toj8/localization/timezones/ZonedDateTime_Manipulate.java
84
Saída no console
2019-05-20T09:20:12.000001-03:00[America/Sao_Paulo]
+2 horas: 2019-05-20T11:20:12.000001-03:00[America/Sao_Paulo]
+2 minutos: 2019-05-20T09:22:12.000001-03:00[America/Sao_Paulo]
+2 segundos: 2019-05-20T09:20:14.000001-03:00[America/Sao_Paulo]
+2 nanosegundos: 2019-05-20T09:20:12.000001002-03:00[America/Sao_Paulo]
+2 microssegundos: 2019-05-20T09:20:12.000003-03:00[America/Sao_Paulo]
+2 milissegundos: 2019-05-20T09:20:12.002001-03:00[America/Sao_Paulo]
-2 horas: 2019-05-20T07:20:12.000001-03:00[America/Sao_Paulo]
-2 minutos: 2019-05-20T09:18:12.000001-03:00[America/Sao_Paulo]
-2 segundos: 2019-05-20T09:20:10.000001-03:00[America/Sao_Paulo]
-2 nanosegundos: 2019-05-20T09:20:12.000000998-03:00[America/Sao_Paulo]
-2 microssegundos: 2019-05-20T09:20:11.999999-03:00[America/Sao_Paulo]
-2 milissegundos: 2019-05-20T09:20:11.998001-03:00[America/Sao_Paulo]
+2 dias: 2019-05-22T09:20:12.000001-03:00[America/Sao_Paulo]
+2 semanas: 2019-06-03T09:20:12.000001-03:00[America/Sao_Paulo]
+2 meses: 2019-07-20T09:20:12.000001-03:00[America/Sao_Paulo]
+2 anos: 2021-05-20T09:20:12.000001-03:00[America/Sao_Paulo]
+2 décadas: 2039-05-20T09:20:12.000001-03:00[America/Sao_Paulo]
-2 dias: 2019-05-18T09:20:12.000001-03:00[America/Sao_Paulo]
-2 semanas: 2019-05-06T09:20:12.000001-03:00[America/Sao_Paulo]
-2 meses: 2019-03-20T09:20:12.000001-03:00[America/Sao_Paulo]
-2 anos: 2017-05-20T09:20:12.000001-03:00[America/Sao_Paulo]
-2 décadas: 1999-05-20T09:20:12.000001-03:00[America/Sao_Paulo]
src/org/j6toj8/localization/timezones/ZonedDateTime_Immutability.java
Saída no console
2019-05-20T09:20:03.000000300-03:00[America/Sao_Paulo]
2019-05-20T09:20:03.000000300-03:00[America/Sao_Paulo]
2019-05-20T10:20:03.000000300-03:00[America/Sao_Paulo]
85
src/org/j6toj8/localization/timezones/ZonedDateTime_Chaining.java
Saída no console
2019-05-20T09:20:04.000000300-03:00[America/Sao_Paulo]
2020-05-21T10:20:04.000000300-03:00[America/Sao_Paulo]
8. Ao manipular um ZonedDateTime, será levado em conta o horário de verão daquele fuso horário.
src/org/j6toj8/localization/timezones/ZonedDateTime_DaylightSavings.java
Saída no console
2018-11-03T23:30-03:00[America/Sao_Paulo]
+2 horas: 2018-11-04T02:30-02:00[America/Sao_Paulo]
Neste exemplo o fuso horário que era -03:00 virou -02:00, pois esse foi o dia em que teve início o
horário de verão no Brasil. Outro detalhe é que por conta do horário de verão, ao somar 2 horas
às 23:30 resultou em 02:30 do dia seguinte. Se não houvesse horário de verão, o resultado seria
01:30.
src/org/j6toj8/localization/timezones/ZonedDateTime_Zones.java
86
Saída no console (parcial)
Asia/Aden
America/Cuiaba
Etc/GMT+9
Etc/GMT+8
Africa/Nairobi
America/Marigot
Asia/Aqtau
Pacific/Kwajalein
America/El_Salvador
Asia/Pontianak
Africa/Cairo
Pacific/Pago_Pago
Africa/Mbabane
Asia/Kuching
Pacific/Honolulu
Pacific/Rarotonga
America/Guatemala
...
A lista do console irá apresentar todos os ZoneId disponíveis. O exemplo acima contempla
apenas parte dos ZoneId.
Além disso, existem muitos ZoneId duplicados, pois representam o mesmo fuso horário, como
por exemplo America/Sao_Paulo e Brazil/East.
Referências
Boyarsky, Jeanne; Selikoff, Scott. OCP: Oracle Certified Professional Java SE 8 Programmer
II Study Guide (p. 234). Wiley. Edição do Kindle.
87
Lambda
Interfaces Funcionais (Functional Interfaces)
Objetivo
Define and write functional interfaces and describe the interfaces of the
java.util.function package.
-
Definir e escrever interfaces funcionais e descrever as interfaces do pacote
java.util.function.
Interfaces funcionais são um novo tipo de Interface do Java. Nesta seção serão apresentados os
conceitos e na seção de Expressões Lambda será possível ver como utilizá-las.
1. Interfaces Funcionais são aquelas que possuem apenas um método abstrato, chamado de
"método funcional".
src/org/j6toj8/lambda/functionalinterfaces/FunctionalInterfaces_Basic.java
3. Métodos adicionais que sejam default ou static não fazem com que a interface deixe de ser
funcional.
88
src/org/j6toj8/lambda/functionalinterfaces/FunctionalInterfaces_DefaultStatic.java
@FunctionalInterface
interface Executavel { // interface funcional
void execute(); // método funcional
Lembre-se que os métodos static em interfaces podem ser chamados diretamente, como
Executavel.execute(…). Dessa forma, não há interferência no fato da interface ser funcional.
Por outro lado, os métodos default só podem ser chamados caso você possua uma instância da
interface, porém eles já possuem uma implementação padrão.
Em caso de dúvidas sobre static ou default em interfaces, volte na seção de "Métodos static e
default em Interfaces".
4. Sobrescrever na interface um método público de java.lang.Object também não faz com que ela
deixe de ser funcional.
src/org/j6toj8/lambda/functionalinterfaces/FunctionalInterfaces_OverrideObject.java
@FunctionalInterface
interface Executavel { // interface funcional
void execute(); // método funcional
5. Uma interface que estende outra sem acrescentar métodos abstratos também pode ser
funcional.
89
src/org/j6toj8/lambda/functionalinterfaces/FunctionalInterfaces_Extends.java
@FunctionalInterface
interface Executavel { // interface funcional
void execute(); // método funcional
}
@FunctionalInterface
interface Aplicacao extends Executavel {
// também é uma interface funcional
}
6. Se uma interface estende outra que é funcional, porém acrescenta novos métodos abstratos, ela
não é funcional.
src/org/j6toj8/lambda/functionalinterfaces/FunctionalInterfaces_ExtendsNewMethod.java
@FunctionalInterface
interface Executavel { // interface funcional
void execute(); // método funcional
}
src/org/j6toj8/lambda/functionalinterfaces/FunctionalInterfaces_InterfaceCompilationError.java
@FunctionalInterface
interface Executavel { // interface funcional
void execute(); // método funcional
}
@FunctionalInterface
interface Aplicacao extends Executavel { // NÃO COMPILA!
// não pode ser anotada como funcional, pois possui 2 métodos abstratos
void inicie();
}
8. Utilizar a anotaçao @FunctionalInterface em qualquer tipo que não seja uma interface causa um
erro de compilação.
90
src/org/j6toj8/lambda/functionalinterfaces/FunctionalInterfaces_ClassCompilationError.java
@FunctionalInterface
interface Executavel { // interface funcional
void execute(); // método funcional
}
@FunctionalInterface
class Piloto { // NÃO COMPILA!
// apenas interfaces podem ser anotadas com @FunctionalInterface
}
src/org/j6toj8/lambda/functionalinterfaces/FunctionalInterfaces_ReturnType.java
@FunctionalInterface
interface Executavel { // interface funcional
String execute(); // método funcional com retorno
}
10. Interfaces funcionais são feitas com o intuito de serem utilizadas em expressões lambda, mas o
código também irá compilar normalmente caso uma classe a implemente.
src/org/j6toj8/lambda/functionalinterfaces/FunctionalInterfaces_Implement.java
@FunctionalInterface
interface Executavel { // interface funcional
String execute(); // método funcional
}
Esse é apenas um exemplo para você saber que essa implementação não gera erro de
compilação, mas não utilize interfaces funcionais dessa forma. Na seção de Expressões Lambda
você verá como as interfaces funcionais devem ser utilizadas na prática.
91
Interfaces Funcionais do pacote java.util.function
As interfaces descritas aqui estão disponíveis no pacote java.util.function. Nesta seção serão
apresentadas apenas suas definições, pois há posteriormente uma seção específica para tratar dos
exemplos de cada uma.
Existem outras interfaces nesse pacote além das listadas aqui, porém são apenas específicas para
lidar com tipos primitivos, seguindo as mesmas definições.
• Consumer<T>: Representa uma operação que aceita uma única entrada e não possui retorno.
• BiConsumer<T,U>: Representa uma operação que aceita duas entradas e não possui retorno.
Os Consumer são praticamente o inverso dos Supplier, pois eles apenas recebem dados ou
informações e os tratam de alguma forma.
• BiFunction<T,U,R>: Representa uma função que aceita dois argumentos e produz um retorno.
As Function são mais parecidas com as funções que já conhecemos. Elas recebem dados e
produzem um retorno.
Os Predicate são parecidos com as Function, porém sempre retornam um resultado booleano,
por isso são utilizados para "testes" de verdadeiro ou falso.
92
Referências
Boyarsky, Jeanne; Selikoff, Scott. OCP: Oracle Certified Professional Java SE 8 Programmer
II Study Guide (p. 52). Wiley. Edição do Kindle.
Describe a lambda expression; refactor the code that uses an anonymous inner class to
use a lambda expression; describe type inference and target typing.
-
Descrever uma expressão lambda; refatorar código que usa classes anônimas internas
para usar expressões lambda; descrever a inferência de tipos e tipos esperados.
Expressões Lambda são parecidas com classes anônimas, porém só possuem a implementação dos
métodos. Por isso, são como "métodos anônimos", que podem ser passados via variáveis.
É essencial o entendimento de funções lambda, e das variações em sua sintaxe, para compreender
as próximas seções de Interfaces Funcionais Pré-Construídas e de Referências a métodos.
• i → System.out.println(i)
• (Integer i) → System.out.println(i)
(Integer i) → { System.out.println(i); }
93
• (Integer i) → { return i + 1; }
• (i, j, k) → { return i + j + k; }
• (i, j, k) → System.out.println(i + j + k)
• () → System.out.println("nada")
1. Expressões lambda podem ser entendidas como uma forma diferente de declarar classes
anônimas.
src/org/j6toj8/lambda/lambdaexpression/LambdaExpression_AnonymousClass.java
Veja que no exemplo acima é instanciada uma Thread com uma instância anônima de Runnable.
Na segunda parte, é feito a mesma coisa de forma muito mais simples utilizando uma expressão
lambda.
2. Expressões lambda são sempre utilizadas para criar instâncias de interfaces funcionais, ou seja,
interfaces que possuem apenas um único método abstrato.
94
src/org/j6toj8/lambda/lambdaexpression/LambdaExpression_FunctionalInterface.java
@FunctionalInterface
interface Executavel { // interface funcional
String execute(); // método funcional
}
Saída no console
Veja que no exemplo acima o mesmo método executeEApresenteMensagem é invocado duas vezes.
Na primeira vez é passada uma nova classe anônima. Na segunda vez é passado uma expressão
lambda.
Veja também que seria impossível criar uma expressão lambda caso a interface não fosse
funcional, ou seja, tivesse mais de um método abstrato. O compilador não saberia identificar
que o método execute, da interface Executavel, está sendo implementado dentro da expressão
lambda.
src/org/j6toj8/lambda/lambdaexpression/LambdaExpression_ForEach.java
95
Saída no console
1
2
3
4
5
Veja que o novo método forEach executa a expressão lambda passada como parâmetro para
cada item da lista, imprimindo todos no console. A expressão lambda recebe como parâmetro
um número, que é o número da lista.
Neste caso, a interface funcional que está sendo implementada pela expressão lambda é
chamada Consumer. Ela será explicada em detalhes em uma seção posterior, juntamente com
outras interfaces funcionais padrões do Java 8. Nesta seção é importante apenas entender o que
são as expressões lambda e como é sua sintaxe.
src/org/j6toj8/lambda/lambdaexpression/LambdaExpression_SimpleComplete.java
As duas funções lambda acima são idênticas, porém uma é mais explícita do que a outra.
5. Os parênteses só podem ser omitidos caso não haja a declaração do tipo, e haja apenas um
único argumento.
6. Expressões lambda que NÃO possuem argumentos também precisam dos parênteses.
96
src/org/j6toj8/lambda/lambdaexpression/LambdaExpression_Parenthesis.java
// COMPILA - sem parênteses quando há apenas uma variável e não escrevemos seu tipo
UnaryOperator<Double> elevarAoQuadrado2 = x -> Math.pow(x, 2);
7. É obrigatória a utilização de chaves, ponto e vírgula e return (caso a função retorne algum
valor) em expressões lambda com mais de uma linha.
src/org/j6toj8/lambda/lambdaexpression/LambdaExpression_Block.java
src/org/j6toj8/lambda/lambdaexpression/LambdaExpression_VarType.java
9. Não é permitido declarar variáveis com o mesmo nome dentro da expressão lambda.
97
src/org/j6toj8/lambda/lambdaexpression/LambdaExpression_Shadowing.java
// NÃO COMPILA - a variável com nome 'x' já existe e não pode ser declarada nas
variáveis da expressão lambda
BinaryOperator<Double> elevarAoX = (Double y, Double x) -> Math.pow(y, x);
// NÃO COMPILA - a variável com nome 'x' já existe e não pode ser declarada no
corpo da expressão lambda
UnaryOperator<Double> elevarAoQuadrado = (Double y) -> {
Double x = 2.0;
return Math.pow(y, x);
};
}
10. É permitido acessar variáveis externas dentro da expressão lambda, mas somente variáveis
finais ou variáveis que não são alteradas.
src/org/j6toj8/lambda/lambdaexpression/LambdaExpression_AccessExternalVar.java
// COMPILA - variável externa 'x2' não é final, mas nunca é alterada, então pode
ser utilizada dentro da expressão lambda
UnaryOperator<Double> elevarAoX2 = (Double y) -> Math.pow(y, x2);
// NÃO COMPILA - variável externa 'x3' é alterada dentro desse método, então não
pode ser utilizada dentro da expressão lambda
UnaryOperator<Double> elevarAoX3 = (Double y) -> Math.pow(y, x3);
Perceba que o compilador identifica que a variável x3 é alterada no final do método, e por isso,
não permite que ela seja utilizada na expressão lambda.
98
src/org/j6toj8/lambda/lambdaexpression/LambdaExpression_TypeInference.java
@FunctionalInterface
interface Executavel {
void execute(); // método funcional sem argumentos
}
@FunctionalInterface
interface Application {
String run(); // método funcional também sem argumentos
}
// esse método também pode receber uma expressão lambda sem argumentos
void rode(Application application) {
application.run();
}
}
No exemplo anterior, apenas o método run da interface funcional Application retorna uma
String, enquanto o método execute da interface funcional Executavel não retorna nada (void).
Isso é diferença suficiente para o compilador saber qual método chamar cada vez que rode é
invocado.
Na primeira chamada ao método rode, como a expressão lambda passada retorna uma String, o
compilador entende que essa expressão lambda é uma implementação da interface Application,
pois o método run também retorna uma String.
Na segunda chamada ao método rode, como a expressão lambda não retorna nada (apenas
imprime a String "executando"), o compilador entende que essa expressão lambda é uma
implementação da interface Executavel, pois o método execute também não retorna nada.
12. Se não for possível descobrir o tipo da expressão lambda, ocorre erro de compilação.
99
src/org/j6toj8/lambda/lambdaexpression/LambdaExpression_Ambiguity.java
@FunctionalInterface
interface Corredor {
void corra();
}
@FunctionalInterface
interface Piloto {
void corra();
}
No exemplo anterior, como as duas interfaces funcionais possuem métodos com retorno void, o
compilador não sabe qual das duas está sendo instanciada na expressão lambda, e ocorre erro
de compilação. A expressão lambda, nesse exemplo, poderia ser tanto do tipo Piloto quanto
Corredor, e não há como o compilador descobrir qual o desenvolvedor de fato quis utilizar.
Referências
Boyarsky, Jeanne; Selikoff, Scott. OCP: Oracle Certified Professional Java SE 8 Programmer
II Study Guide (p. 55). Wiley. Edição do Kindle.
Boyarsky, Jeanne; Selikoff, Scott. OCP: Oracle Certified Professional Java SE 8 Programmer
II Study Guide (p. 172). Wiley. Edição do Kindle.
100
Interfaces Funcionais Pré-Construídas (Built-in
Interfaces)
Objetivo
Develop code that uses the built-in interfaces included in the java.util.function
package, such as Function, Consumer, Supplier, UnaryOperator, Predicate, and Optional
APIs, including the primitive and binary variations of the interfaces
-
Desenvolver código que usa as interfaces embutidas no pacote java.util.function, como
Function, Consumer, Supplier, UnaryOperator, Predicate e a API de Optional, incluindo
as variações de tipos primitivos e binários das interfaces
Interfaces Funcionais
O Java 8 possui algumas Interfaces Funcionais já criadas. Elas provavelmente serão suficientes para
a maioria dos cenários onde é usual utilizar expressões lambda, de tal forma que não deve ser
muito comum você precisar criar uma nova.
É fundamental entender esses exemplos para dominar a utilização de expressões lambda, e para
entender a próxima seção sobre referências a métodos.
Supplier
1. Supplier é uma interface funcional que não recebe nenhum parâmetro de entrada, mas retorna
um valor. Sua definição na JDK é a seguinte:
java.util.function.Supplier<T>
@FunctionalInterface
public interface Supplier<T> {
T get();
}
src/org/j6toj8/lambda/builtininterfaces/BuiltInInterfaces_SupplierExample.java
Saída no console
2019-07-08
101
A saída no console irá imprimir a data atual em que este código foi escrito.
Perceba que a expressão lambda está simplificada, sem chaves {} ou return. Caso tenha dúvidas
com relação a isso, consulte novamente a seção sobre expressões lambda.
3. Um Supplier pode ser utilizado para fornecer uma função custosa em termos de processamento,
para que seja chamada apenas se for necessário:
src/org/j6toj8/lambda/builtininterfaces/BuiltInInterfaces_SupplierUseCase.java
Saída no console
Menor de idade!
Maior de idade! Validação realizada às 2019-07-09T00:21:35.488
Perceba que neste caso o supplier só precisou ser chamado na segunda vez, evitando uma
execução desnecessária da expressão lambda.
src/org/j6toj8/lambda/builtininterfaces/BuiltInInterfaces_SupplierPrimitive.java
Consumer e BiConsumer
1. Consumer é uma interface funcional que recebe um parâmetro de entrada, e não retorna
102
nenhum valor. Sua definição na JDK é a seguinte:
java.util.function.Supplier<T>
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
}
2. BiConsumer é uma interface funcional que recebe dois parâmetros de entrada, e não retorna
nenhum valor. Sua definição na JDK é a seguinte:
java.util.function.Consumer<T>
@FunctionalInterface
public interface BiConsumer<T, U> {
void accept(T t, U u);
}
3. Implementações possíveis para Consumer ou BiConsumer são funções que imprimem informações
no console:
src/org/j6toj8/lambda/builtininterfaces/BuiltInInterfaces_ConsumerExample.java
Saída no console
2019-07-08
2019-07-08
22:37:39.229
103
src/org/j6toj8/lambda/builtininterfaces/BuiltInInterfaces_ConsumerPrimitive.java
Predicate e BiPredicate
1. Predicate é uma interface funcional que recebe um parâmetro de entrada e retorna um valor
booleano. Sua definição na JDK é a seguinte:
java.util.function.Predicate<T>
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
2. BiPredicate é uma interface funcional que recebe dois parâmetros de entrada e retorna um
valor booleano. Sua definição na JDK é a seguinte:
java.util.function.BiPredicate<T>
@FunctionalInterface
public interface BiPredicate<T, U> {
boolean test(T t, U u);
}
3. Implementações possíveis para Predicate ou BiPredicate são funções que verificam se o valor
de entrada é igual ao valor sorteado:
104
src/org/j6toj8/lambda/builtininterfaces/BuiltInInterfaces_PredicateExample.java
A saída no console é aleatória, pois depende do valor sorteado. Um valor possível seria false e
true.
src/org/j6toj8/lambda/builtininterfaces/BuiltInInterfaces_PredicatePrimitive.java
Function e BiFunction
1. Function é uma interface funcional que recebe um parâmetro de entrada e retorna um valor.
Sua definição na JDK é a seguinte:
java.util.function.Function<T, R>
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}
2. BiFunction é uma interface funcional que recebe dois parâmetros de entrada e retorna um
valor. Sua definição na JDK é a seguinte:
java.util.function.BiFunction<T>
@FunctionalInterface
public interface BiFunction<T, U, R> {
R apply(T t, U u);
}
105
3. Implementações possíveis para Function ou BiFunction são funções que multiplicam os valores
fornecidos:
src/org/j6toj8/lambda/builtininterfaces/BuiltInInterfaces_FunctionExample.java
Saída no console
7.5
30.0
src/org/j6toj8/lambda/builtininterfaces/BuiltInInterfaces_FunctionPrimitive.java
Saída no console
7.5
30.0
UnaryOperator e BinaryOperator
106
java.util.function.Function<T, R>
@FunctionalInterface
public interface UnaryOperator<T> extends Function<T, T> {
Perceba que não existe método abstrato declarado, pois ela apenas estende a interface Function
já existente.
2. BinaryOperator é uma interface funcional que recebe dois parâmetros de entrada do mesmo
tipo, e retorna um valor do mesmo tipo das entradas. Sua definição na JDK é a seguinte:
java.util.function.BiFunction<T>
@FunctionalInterface
public interface BinaryOperator<T> extends BiFunction<T,T,T> {
Perceba que não existe método abstrato declarado, pois ela apenas estende a interface
BiFunction já existente.
src/org/j6toj8/lambda/builtininterfaces/BuiltInInterfaces_OperatorExample.java
Saída no console
9
6
107
src/org/j6toj8/lambda/builtininterfaces/BuiltInInterfaces_OperatorPrimitive.java
Saída no console
9
6
Optional
O Java 8 possui um tipo específico para representar valores que podem não ter sido informados,
que é a classe Optional. A partir do Java 8, ela geralmente é uma opção melhor do que retornar ou
armazenar null, pois seus métodos auxiliam em várias situações.
1. É possível criar uma instância de Optional com valor através do método of.
2. É possível criar uma instância de Optional sem valor através do método empty.
3. É possível checar se uma instância de Optional possui um valor através do método isPresent.
108
src/org/j6toj8/lambda/builtininterfaces/BuiltInInterfaces_OptionalCreation.java
5. Não é possível chamar o método of passando null como argumento. Para isso existe o método
ofNullable.
109
src/org/j6toj8/lambda/builtininterfaces/BuiltInInterfaces_OptionalNullable.java
Saída no console
java.lang.NullPointerException
at java.util.Objects.requireNonNull(Objects.java:203)
at java.util.Optional.<init>(Optional.java:96)
at java.util.Optional.of(Optional.java:108)
at
org.j6toj8.lambda.builtininterfaces.BuiltInInterfaces_OptionalNullable.main(BuiltIn
Interfaces_OptionalNullable.java:11)
false
6. Com o método ifPresent é possível executar uma expressão lambda apenas se o Optional tiver
valor.
src/org/j6toj8/lambda/builtininterfaces/BuiltInInterfaces_OptionalIfPresent.java
// A linha abaixo não irá imprimir nada, pois o optional está vazio
optionalVazio.ifPresent(valor -> System.out.println("Vazio: " + valor));
// A linha abaixo irá imprimir, pois o optional possui valor
optionalComValor.ifPresent(valor -> System.out.println("Com Valor: " + valor ));
}
Saída no console
7. É possível recuperar um valor padrão caso o Optional esteja vazio. O método orElse retorna um
110
valor diretamente, e o orElseGet retorna através de uma expressão lambda.
src/org/j6toj8/lambda/builtininterfaces/BuiltInInterfaces_OptionalOrElse.java
// as duas variáveis abaixo terão a String "valor padrao", pois o optional está
vazio
String orElse = optionalVazio.orElse("valor padrao"); // obtém a String
diretamente
String orElseGet = optionalVazio.orElseGet(() -> { return "valor padrao"; }); //
obtém a String através da expressão lambda
System.out.println(orElse);
System.out.println(orElseGet);
// as duas variáveis abaixo irão utilizar o valor presente no optional, pois ele
já está preenchido
String orElse2 = optionalComValor.orElse("valor padrao");
String orElseGet2 = optionalComValor.orElseGet(() -> { return "valor padrao"; });
System.out.println(orElse2);
System.out.println(orElseGet2);
}
Saída no console
valor padrao
valor padrao
valor
valor
8. Também é possível lançar uma exceção caso um valor não esteja presente no Optional
utilizando o método orElseThrow.
111
src/org/j6toj8/lambda/builtininterfaces/BuiltInInterfaces_OptionalOrElseThrow.java
// Nesse caso será impresso o valor presente em Optional, pois ele está
preenchido
String orElseThrow1 = optionalComValor.orElseThrow(() -> new RuntimeException());
System.out.println(orElseThrow1);
// Nesse caso será lançada exceção, pois o Optional não está preenchido
String orElseThrow2 = optionalVazio.orElseThrow(() -> new RuntimeException());
}
Saída no console
valor
Exception in thread "main" java.lang.RuntimeException
at
org.j6toj8.lambda.builtininterfaces.BuiltInInterfaces_OptionalOrElseThrow.lambda$1(
BuiltInInterfaces_OptionalOrElseThrow.java:17)
at java.util.Optional.orElseThrow(Optional.java:290)
at
org.j6toj8.lambda.builtininterfaces.BuiltInInterfaces_OptionalOrElseThrow.main(Buil
tInInterfaces_OptionalOrElseThrow.java:17)
src/org/j6toj8/lambda/builtininterfaces/BuiltInInterfaces_OptionalGetEmpty.java
Saída no console
valor
Exception in thread "main" java.util.NoSuchElementException: No value present
at java.util.Optional.get(Optional.java:135)
at
org.j6toj8.lambda.builtininterfaces.BuiltInInterfaces_OptionalGetEmpty.main(BuiltIn
Interfaces_OptionalGetEmpty.java:13)
10. Existem algumas classes para lidar com valor opcionais de variáveis primitivas, já que elas não
podem ser utilizada com generics: OptionalInt, OptionalDouble, OptionalLong.
112
src/org/j6toj8/lambda/builtininterfaces/BuiltInInterfaces_OptionalPrimitive.java
if (optionalComValor.isPresent()) {
System.out.println(optionalComValor.getAsInt());
}
if (optionalVazio.isPresent()) {
System.out.println(optionalVazio.getAsInt());
}
Saída no console
Referências
Boyarsky, Jeanne; Selikoff, Scott. OCP: Oracle Certified Professional Java SE 8 Programmer
II Study Guide (p. 173). Wiley. Edição do Kindle.
Develop code that uses a method reference, including refactoring a lambda expression
to a method reference.
-
Desenvolver código que utiliza uma referência a método, incluindo a refatoração de uma
expressão lambda para uma referência a método.
A sintaxe de referência a um método é uma novidade do Java 8. Com ela é possível fazer referência
a métodos específicos, em quatro ocasiões diferentes:
113
• Referências a métodos de um tipo de objeto (de uma classe, interface, etc) → String::isEmpty
É essencial lembrar das Interfaces Funcionais, das variações de sintaxe de Expressões Lambda e
das definições de Interfaces Funcionais Pré-Construídas. Caso julgue necessário, reveja as seções
deste capítulo.
É possível pensar nas referências a métodos como uma outra forma de escrever uma expressão
lambda, caso a única coisa que sua expressão lambda faça seja chamar um outro método.
1. Chamadas a métodos estáticos em expressões lambda podem virar uma referência ao método.
src/org/j6toj8/lambda/methodreference/MethodReference_Static.java
System.out.println(converteIntEmStr1.apply(5));
System.out.println(converteIntEmStr2.apply(5));
Saída no console
5
5
Nesse caso a única coisa que a expressão lambda faz é receber um argumento x e repassar para
o método valueOf de String. Para simplificar isso, o Java 8 permite que você escreva essa mesma
função lambda como foi apresentado na linha seguinte: String::valueOf.
2. Chamadas a métodos de uma instância específica também podem ser representados como uma
referência a um método.
114
src/org/j6toj8/lambda/methodreference/MethodReference_Instance.java
Saída no console
5 - 8
5 - 8
3. Chamadas a métodos de uma classe, sem especificar a instância específica, também podem ser
representados como uma referência a um método.
115
src/org/j6toj8/lambda/methodreference/MethodReference_Type.java
Saída no console
8.0
8.0
Nesse exemplo, a referência está sendo feita ao método doubleValue do tipo Integer. Só é
possível representar a primeira expressão lambda na forma de um method reference porque:
src/org/j6toj8/lambda/methodreference/MethodReference_TypeWithParam.java
Saída no console
-1
-1
Nesse exemplo o compilador "descobre" ainda mais coisas que nos exemplos anteriores. Ao
escrever apenas a referência ao método, o compilador entende que a variável x, que vem
primeiro, será a instância de Integer onde o método compareTo será chamado. E que y é a
116
instância de Integer que será passada como argumento para o método compareTo.
src/org/j6toj8/lambda/methodreference/MethodReference_Constructor.java
Saída no console
1
1
Esse exemplo é muito parecido com o anterior, com a única diferença sendo que o método
referenciado é um construtor. Perceba que o construtor também recebe um parâmetro e, assim
como no exemplo anterior, o compilador entende que o argumento da função lambda deve ser
passado para o construtor que foi chamado.
6. Expressões lambda complexas não podem ser convertidas em referência a método, como a
expressão abaixo:
src/org/j6toj8/lambda/methodreference/MethodReference_Complex.java
Como nesse caso temos uma outra String + "2" sendo acrescentada no construtor, não há como
representar isso com uma simples referência ao construtor.
7. É possível utilizar method reference também com suas própria classes. Veja no exemplo a seguir
os tipos criados pelo nosso código e as expressões lambda equivalentes com e sem referência a
métodos.
117
src/org/j6toj8/lambda/methodreference/MethodReference_CustomType.java
118
retornam um valor.
◦ A última implementa a interface functional Supplier, pois não recebe argumento, mas
retorna um valor.
Em caso de dúvidas, consulte novamente os tipos de interfaces funcionais nas outras seções
deste capítulo.
8. A variedade de formas para representar uma mesma expressão lambda pode ser grande, então
cuidado para não se confundir.
src/org/j6toj8/lambda/methodreference/MethodReference_Variaty.java
Você já viu todas as formas de criar uma expressão lambda, desde a mais completa até a mais
simples. Tenha certeza que conhece todas essas variações para o exame de certificação.
Referências
Boyarsky, Jeanne; Selikoff, Scott. OCP: Oracle Certified Professional Java SE 8 Programmer
II Study Guide (p. 152). Wiley. Edição do Kindle.
119
Java Streams
Utilizando Streams
Objetivo
Describe the Stream interface and pipelines; create a stream by using the
Arrays.stream() and IntStream.range() methods; identify the lambda operations that are
lazy.
-
Descrever a interface e pipelines de Stream; criar um stream utilizando os métodos
Arrays.stream() e IntStream.range(); identificar quais operações lambda executam sob
demanda (_lazy_).
Uma das maiores novidades do Java 8 são os Streams. Um Stream é basicamente um fluxo de dados.
Os dados podem ser Strings, números, ou qualquer outro objeto. Esses dados passam por uma série
de operações, e o conjunto dessas operações é chamado de pipeline. Essas operações são quase
sempre representadas por expressões lambda, então é muito importante ter dominado todo o
capítulo sobre lambda, pois todos aqueles conceitos serão utilizados agora para formar um Stream.
Criando um Stream
Geralmente, um Stream é criado a partir de um conjunto de dados, como uma lista ou outro tipo de
coleção. O objetivo da certificação deixa explícito que é necessário conhecer os métodos
Arrays.stream() e IntStream.range(). Mas, além dessas, serão apresentadas também algumas outras
formas comuns de criar um Stream.
src/org/j6toj8/streams/usingstreams/Stream_ArraysStream.java
Saída no console
A
B
C
120
src/org/j6toj8/streams/usingstreams/Stream_IntRangeStream.java
IntStream.range(0, 4).forEach(System.out::println);
Saída no console
0
1
2
3
src/org/j6toj8/streams/usingstreams/Streams_ListStream.java
Saída no console
A
B
C
src/org/j6toj8/streams/usingstreams/Streams_Of.java
Saída no console
A
B
1
2
3.0
4.0
Nesse caso foi criado um Stream que contém: String, Character, Integer, Long, Float e Double.
Operações em Streams
As operações feitas em um Stream irão formar seu pipeline. As operações que podem ser realizadas
121
em um Stream são divididas em operações intermediárias e operações finais. O Stream pode
conter inúmeras operações intermediárias, porém somente uma final. Nos exemplos anteriores a
única operação utilizada foi o forEach, que é uma operação final. A seguir serão apresentadas
outras operações.
Operações intermediárias
src/org/j6toj8/streams/usingstreams/Stream_Skip.java
IntStream.range(0, 4) // stream de 0 a 3
.skip(2) // ignora 2 elementos
.forEach(System.out::println); // imprime os elementos
Saída no console
2
3
Perceba que nesse caso os elementos 0 e 1 foram ignorados, pois são os dois primeiros
elementos do Stream. Isso ocorreu pela existência da operação skip.
src/org/j6toj8/streams/usingstreams/Streams_Limit.java
IntStream.range(0, 4) // stream de 0 a 3
.limit(2) // limita a 2 elementos
.forEach(System.out::println); // imprime os elementos
Saída no console
0
1
Nesse caso apenas os 2 primeiros elementos foram impressos no console, pois a operação limit
limitou a quantidade de elementos a serem processados.
src/org/j6toj8/streams/usingstreams/Streams_Filter.java
IntStream.range(0, 4) // stream de 0 a 3
.filter(e -> e % 2 == 0) // limita a números pares (resto da divisão por 2 é 0)
.forEach(System.out::println); // imprime os elementos
122
Saída no console
0
2
Nesse caso apenas os elementos pares foram impressos, pois a operação filter limitou àqueles
que possuem resto da divisão por 2 igual a 0.
src/org/j6toj8/streams/usingstreams/Streams_Distinct.java
Arrays.stream(array)
.distinct() // ignora elementos repetidos
.forEach(System.out::println);
Saída no console
A
B
C
F
Perceba que nesse caso os elementos repetidos do stream ("A" e "B") foram ignorados, sendo
apresentados apenas uma vez.
A operação distinct utiliza os método equals e hashCode, então tenha certeza de que eles estão
implementados corretamente caso esteja utilizando um tipo de objeto criado por você. No
exemplo foram utilizados objetos do tipo String, que já possuem essa implementação por
padrão.
5. É possível aplicar uma transformação nos elementos do Stream utilizando a operação map.
src/org/j6toj8/streams/usingstreams/Streams_Map.java
IntStream.range(0, 4) // stream de 0 a 3
.map(e -> e * 2) // multiplica os elementos por 2
.forEach(System.out::println); // imprime os elementos
Saída no console
0
2
4
6
123
Perceba que nesse caso os elementos sofreram uma transformação, que foi a multiplicação por
2, antes de serem impressos no console.
src/org/j6toj8/streams/usingstreams/Streams_Sorted.java
Arrays.stream(array)
.sorted() // ordena utilizando a ordem natural
.forEach(System.out::println);
Saída no console
A
A
B
B
C
F
G
T
Y
Nesse caso todos os elementos são ordenados utilizando a ordem natural dos objetos String,
pois eles já implementam a interface Comparable, sendo apresentados em ordem alfabética.
Também existe uma versão do método sort que recebe como argumento uma implementação
de Comparator, caso deseje ordenar de outra forma.
7. É possível observar os elementos que passam por um Stream utilizando a operação peek.
src/org/j6toj8/streams/usingstreams/Streams_Peek.java
Arrays.stream(array)
.peek(e -> System.out.println("Peek: " + e)) // observa o que passou pelo
Stream
.forEach(e -> System.out.println("ForEach: " + e));
124
Saída no console
Peek: G
ForEach: G
Peek: T
ForEach: T
Peek: Y
ForEach: Y
Peek: A
ForEach: A
A operação peek funciona apenas para observar o que está passando pelo Stream. Pode ser
muito útil para realizar debug ou log. Nesse caso os elementos estão sendo impressos duas vezes
no console, pois o método peek e o forEach estão ambos realizando essa mesma ação. Porém, em
aplicações reais, geralmente a operação final não será um forEach, de tal forma que fará sentido
utilizar o peek.
src/org/j6toj8/streams/usingstreams/Streams_FlatMap.java
Saída no console
A
B
C
D
E
F
G
H
I
Perceba que nesse caso existem 3 Arrays distintos. Então cria-se um Stream contendo 3 Arrays. O
cenário comum seria um que cada elemento do Stream fosse um objeto do tipo Array. Porém, ao
utilizar a operação flatMap, é criado um Stream para cada um desses Arrays, que são unidos e
formam um único Stream contínuo.
125
Operações finais
1. É possível executar uma ação final para cada elemento do Stream utilizando a operação forEach,
conforme demonstrado nos exemplos anteriores.
2. É possível recuperar o maior e o menor valor de um Stream utilizando as operações finais max e
min. E também é possível recuperar a quantidade de elementos de um Stream utilizando a
operação final count.
src/org/j6toj8/streams/usingstreams/Streams_MaxMinCount.java
Saída no console
Max: 9
Min: 1
Count: 8
No caso das operações max e min, é necessário passar como argumento qual comparador será
utilizado. Como os números possuem uma ordem natural, isto é, implementam a interface
Comparable, é possível utilizar um comparador que usa essa ordem natural, que é o
Comparator.naturalOrder(). Caso seja um tipo de objeto que não possui uma ordem natural, é
necessário passar como argumento uma outra implementação de Comparator.
As operações max e min retornam Optional pois, caso o Stream esteja vazio, será um Optional
vazio. Desde o Java 8, com a adição da classe Optional, isso tem sido preferido ao invés de
retornar null, pois facilita a programação funcional. A operação count não precisa de um
Optional, pois mesmo com um Stream vazio irá retornar 0.
126
src/org/j6toj8/streams/usingstreams/Streams_FindFirstAny.java
Saída no console
First: 7
Any: 7
Nesse caso, como o Stream é sequencial e não paralelo, os dois resultados são iguais. Em
Streams paralelos, que serão apresentados em uma próxima seção, a operação findAny pode
trazer resultados diferentes.
Assim como as operações max e min apresentadas anteriormente, findAny e findFirst retornam
um Optional vazio caso o Stream esteja vazio.
src/org/j6toj8/streams/usingstreams/Streams_Match.java
Saída no console
anyMatch: true
allMatch: false
noneMatch: false
127
segunda, se todos os elementos são maiores do que 5. E na terceira, se nenhum elemento é
maior do que 5.
src/org/j6toj8/streams/usingstreams/Streams_ReuseStream.java
Saída no console
7
2
1
Exception in thread "main" java.lang.IllegalStateException: stream has already been
operated upon or closed
at
java.util.stream.AbstractPipeline.sourceStageSpliterator(AbstractPipeline.java:279)
at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580)
at
org.j6toj8.streams.usingstreams.Streams_ReuseStream.main(Streams_ReuseStream.java:1
1)
Pipeline
src/org/j6toj8/streams/usingstreams/Streams_Pipeline.java
Saída no console
8
12
Para entender todas as operações realizadas nesse pipeline, é necessário entender passo a
passo:
128
c. Foram ignorados os dois primeiros números, mantendo apenas: 4, 6 e 8
2. O Stream só será criado de fato depois que alguma operação for executada nele.
src/org/j6toj8/streams/usingstreams/Streams_ChangeBackingList.java
list.add(4);
stream.forEach(System.out::println);
Saída no console
1
2
3
4
Perceba que, mesmo que o Stream aparentemente tenha sido criado antes de adicionar o
número 4 na lista, ele imprime esse número no console. Isso acontece porque o Stream só foi
criado de fato quando alguma operação foi feita nele, ou seja, quando o forEach foi invocado.
src/org/j6toj8/streams/usingstreams/Streams_Optional.java
max.ifPresent(System.out::println);
Perceba que o método ifPresent é da classe Optional, mesmo que no segundo exemplo possa
129
parecer que ele faz parte do Stream. Em outras palavras, a operação final é max, e ifPresent é
uma chamada em Optional e não mais no Stream.
src/org/j6toj8/streams/usingstreams/Streams_LazyNoFinal.java
IntStream.range(0, 4)
.filter(e -> e % 2 == 0)
.limit(3)
.map(e -> e * 2)
.peek(System.out::println);
Nesse caso nada é impresso no console, pois nenhuma operação final foi aplicada no Stream.
Ou seja, se não há nada consumindo o resultado desse Stream, o Java nem precisa executar o
pipeline criado.
2. Outras operações intermediárias também não costumam ser executadas se não for necessário.
src/org/j6toj8/streams/usingstreams/Streams_LazyMap.java
IntStream.range(0, 10)
.peek(e -> System.out.println("Peek: " + e))
.limit(3)
.forEach(e -> System.out.println("ForEach: " + e));
Saída no console
Peek: 0
ForEach: 0
Peek: 1
ForEach: 1
Peek: 2
ForEach: 2
Perceba que, mesmo que a operação peek esteja antes da operação limit, ela não é executada
para todos os elementos do Stream, apenas para aqueles que serão realmente utilizados.
Streams primitivos
Existem Streams específicos para alguns tipos primitivos como double, int e long. Eles possuem a
vantagem de evitar o Boxing e Unboxing, fornecendo alguns métodos mais especializados como
demonstrado a seguir.
130
1. É possível criar Streams de tipos primitivos com as classes: DoubleStream, IntStream e LongStream.
src/org/j6toj8/streams/usingstreams/primitives/Streams_Primitives.java
System.out.println(" DoubleStream");
DoubleStream.of(1.1, 2.2, 3.3).forEach(System.out::print);
System.out.println("\n IntStream");
IntStream.of(1, 2, 3).forEach(System.out::print);
System.out.println();
IntStream.range(1, 4).forEach(System.out::print);
System.out.println("\n LongStream");
LongStream.of(1, 2, 3).forEach(System.out::print);
System.out.println();
LongStream.range(1, 4).forEach(System.out::print);
Saída no console
DoubleStream
1.12.23.3
IntStream
123
123
LongStream
123
123
src/org/j6toj8/streams/usingstreams/primitives/Streams_MapTo.java
131
Saída no console
src/org/j6toj8/streams/usingstreams/primitives/Streams_Generate.java
Saída no console
Nesse caso os Streams são realmente infinitos. Só foram apresentados 3 números de cada pois
existe a operação limit, caso contrário a execução do programa também seria sem fim.
4. É possível utilizar a operação rangeClosed ao invés de range, deixando o código mais legível.
src/org/j6toj8/streams/usingstreams/primitives/Streams_RangeClosed.java
IntStream.range(1, 4).forEach(System.out::print);
System.out.println();
IntStream.rangeClosed(1, 4).forEach(System.out::print);
132
Saída no console
123
1234
Perceba que na chamada utilizando range, o último número é exclusivo (não faz parte do
Stream). No rangeClosed, tanto o primeiro quanto o último número são inclusivos (fazem parte
do Stream).
src/org/j6toj8/streams/usingstreams/primitives/Streams_Statistics.java
Saída no console
Quantidade: 10
Maior: 9
Menor: 0
Soma: 45
Média: 4.5
Reduce e Collectors
Reduce
Reduce é uma das principais operações final que podem ser feitas em um Stream. Reduce é uma
operação que transforma os vários valores do Stream em um único valor. Várias operações
apresentadas anteriormente são um tipo de Reduce, como: max, min e summaryStatistics. Porém, nem
sempre essas operações são suficientes, e por isso existem os métodos reduce. Eles permitem a
implementação de operações personalizadas de Reduce.
1. É possível criar uma operação de Reduce personalizada com o método reduce() que recebe 1
argumento.
src/org/j6toj8/streams/usingstreams/primitives/Streams_Reduce.java
System.out.println(reduce.get());
133
Saída no console
336
Nesse caso está sendo feito um Reduce onde o resultado da operação anterior é passado para a
próxima execução. Ou seja, primeiro é feita a multiplicação de 7 * 2, que é 14. Então a função é
chamada novamente passando como argumento o resultado anterior (14) e o próximo número
do Stream (3). O resultado é 42. Então a função é chamada uma última vez passando o resultado
anterior (42) e o próximo número do Stream (8), o que dá o resultado de 336.
src/org/j6toj8/streams/usingstreams/primitives/Streams_ReduceIdentity.java
System.out.println(reduce);
Saída no console
336
Nesse caso é possível informar o valor de identidade da função. O conceito de valor ou função
de identidade são um pouco mais complexos, mas para a certificação apenas compreenda que
ele representa um valor neutro. Ou seja, para a operação de multiplicação, o valor de identidade
é 1, pois qualquer valor multiplicado por 1 resulta nele mesmo. Caso fosse uma operação de
soma, o valor de identidade seria 0, pois qualquer valor somado a 0 resulta nele mesmo.
Além disso, se o Stream estiver vazio, o valor de identidade será retornado. Por isso, diferente
do exemplo anterior, não é necessário retornar um Optional.
3. É possível criar uma operação de Reduce que pode ser executada em várias Threads e depois
combinada em um único valor.
src/org/j6toj8/streams/usingstreams/primitives/Streams_ReduceCombiner.java
System.out.println(reduce);
Saída no console
336
Nesse caso é passado um argumento adicional. Ele é a função de combinação. Essa função é
utilizada quando o Stream é paralelo, ou seja, utiliza mais de uma thread. Ela pega o valor
134
retornado por 2 ou mais threads e combina-os em um único valor. Em uma operação de
multiplicação, a combinação também é uma multiplicação. Ou seja, caso a primeira thread
multiplique 7 e 2, resultando em 14, e a segunda multiplique 3 e 8, resultando em 24, a função
de combinação só precisa multiplicar 14 por 24 para chegar ao valor de 336. Sendo assim, a
função de combinação só faz sentido em um Stream paralelo, que será apresentado no próximo
capítulo.
Collect
A operação final collect também é um tipo de Reduce, porém é utilizada para objetos mutáveis. Ou
seja, ao invés de utilizar a operação reduce com String, provavelmente seria mais eficiente utilizar a
operação collect com a classe StringBuilder, para evitar a criação de vários objetos do tipo String.
Como Java utiliza muitos objetos mutáveis, incluindo listas e mapas, geralmente a operação collect
será mais eficiente do que a reduce.
Por serem muito comuns, existem vários Collectors já implementados no Java, disponíveis na classe
Collectors.
src/org/j6toj8/streams/usingstreams/primitives/Streams_CollectorJoining.java
System.out.println(collect);
Saída no console
ABC
2. É possível utilizar um Collector para representar cada elemento como um número e calcular a
média entre eles.
src/org/j6toj8/streams/usingstreams/primitives/Streams_CollectorAveragingInt.java
System.out.println(collect);
Saída no console
6.2
135
src/org/j6toj8/streams/usingstreams/primitives/Streams_CollectorToCollect.java
Saída no console
ArrayList: [1, 2, 3, 4]
HashSet: [1, 2, 3, 4]
LinkedList: [1, 2, 3, 4]
TreeSet: [1, 2, 3, 4]
src/org/j6toj8/streams/usingstreams/primitives/Streams_CollectorToMap.java
System.out.println(collect);
Saída no console
5. Também é possível armazenar em um mapa para casos em que a chave for se repetir. O terceiro
argumento do método toMap define a regra de mesclagem dos valores para chaves iguais.
136
src/org/j6toj8/streams/usingstreams/primitives/Streams_CollectorToMapDuplicateKey.java
System.out.println(collect);
Saída no console
6. É possível utilizar um Collector que cria um mapa agrupando valores que tem a mesma chave
em uma lista.
src/org/j6toj8/streams/usingstreams/primitives/Streams_CollectorGroupingBy.java
System.out.println(collect);
Saída no console
7. Também é possível personalizar a maneira que o valores com chaves iguais serão combinados.
src/org/j6toj8/streams/usingstreams/primitives/Streams_CollectorGroupingByDownstream.java
System.out.println(collect);
137
Saída no console
Perceba que nesse caso os valores foram combinados utilizando outro Collector, que agrupou os
nomes separando com vírgula.
8. Também é possível definir qual tipo de mapa será utilizado para agrupar.
src/org/j6toj8/streams/usingstreams/primitives/Streams_CollectorGroupingByMapFactory.java
System.out.println(collect);
Saída no console
Perceba que o resultado desse exemplo é idêntico ao anterior, porém foi passado um argumento
a mais, que é o construtor do mapa que deveria ser utilizado.
9. É possível utilizar um Collector que particiona valores em True ou False a partir de um função
do tipo Predicate.
src/org/j6toj8/streams/usingstreams/primitives/Streams_CollectorPartitioningBy.java
System.out.println(collect);
Saída no console
Perceba que nesse caso a regra de particionamento são os nomes que iniciam-se por R.
138
10. Também é possível personalizar como a combinação dos valores particionados será feita.
src/org/j6toj8/streams/usingstreams/primitives/Streams_CollectorPartitioningByDownstream.java
System.out.println(collect);
Saída no console
{false=Luiz,Amélia, true=Rinaldo,Rodrigo,Roseany}
Perceba que nesse caso os valores foram combinados utilizando um outro Collector, que juntou
os valores daquela mesma chave em uma única String separados por vírgula.
11. É possível adicionar uma camada a mais de transformação ao utilizar um Collector, utilizando
o método mapping.
src/org/j6toj8/streams/usingstreams/primitives/Streams_CollectorMapping.java
System.out.println(collect);
Saída no console
139
Referências
• Using Streams
Boyarsky, Jeanne; Selikoff, Scott. OCP: Oracle Certified Professional Java SE 8 Programmer
II Study Guide (p. 185). Wiley. Edição do Kindle.
Streams Paralelos
Objetivo
Develop code that uses parallel streams, including decomposition operation and
reduction operation in streams
-
Desenvolver código que usa Streams Paralelos, incluindo operação de decomposição e
operação de redução em Streams
Streams podem ser sequenciais ou paralelos. Os sequencias foram vistos na seção anterior,
enquanto os paralelos serão apresentados nesta seção. Streams paralelos são executados por mais
de uma Thread, geralmente uma quantidade igual à quantidade de núcleos do processador onde a
aplicação está sendo executada. Apesar disso, nem sempre é útil utilizá-los. Seu ganho real é em
Streams com grande volumes de dados. Em um Stream pequeno, transformá-lo em paralelo pode
até causar uma perda de performance.
Ao utilizar qualquer tipo de Stream, é recomendável não executar funções lambdas que causem
efeitos colaterais, como mudanças no estado de objetos. Em Streams paralelos essa recomendação é
ainda mais importante.
src/org/j6toj8/streams/parallelstreams/Streams_Parallel.java
140
src/org/j6toj8/streams/parallelstreams/Streams_ParallelStream.java
src/org/j6toj8/streams/parallelstreams/Streams_ParallelForEach.java
System.out.println("Sequencial: ");
list.stream() // cria um Stream sequencial
.forEach(System.out::println);
System.out.println("Paralelo: ");
list.parallelStream() // cria um Stream paralelo
.forEach(System.out::println);
Saída no console
Sequencial:
A
B
C
Paralelo:
B
C
A
O Stream paralelo poderia ter impresso em qualquer ordem, pois não há nenhuma garantia na
ordem em que os elementos serão tratados.
4. A operação forEachOrdered garante que a ordem será mantida mesmo em Streams paralelos.
src/org/j6toj8/streams/parallelstreams/Streams_ParallelForEachOrdered.java
System.out.println("Sequencial: ");
list.stream() // cria um Stream sequencial
.forEachOrdered(System.out::println);
System.out.println("Paralelo: ");
list.parallelStream() // cria um Stream paralelo
.forEachOrdered(System.out::println);
141
Saída no console
Sequencial:
A
B
C
Paralelo:
A
B
C
src/org/j6toj8/streams/parallelstreams/Streams_ParallelPerformance.java
Saída no console
Perceba que na máquina onde o código foi executado, a execução em paralelo demorou apenas
15% do tempo da execução sequencial. Esse não é um teste minucioso, mas mostra o potencial
de Streams paralelos.
6. Operações intermediárias que alteram o estado de objetos podem gerar resultados inesperados
ao serem executadas em paralelo.
142
src/org/j6toj8/streams/parallelstreams/Streams_ParallelStatefulOperation.java
Saída no console
Ordem no forEachOrdered:
A
B
C
Ordem na synchronizedList:
A
C
B
Perceba que a ordem foi respeitada na última operação do Stream, o forEachOrdered, mas não foi
respeitada na execução da operação intermediária map. Isso ocorre porque essa operação
intermediária não precisa seguir a ordem dos itens do stream.
src/org/j6toj8/streams/parallelstreams/Streams_ParallelFindAny.java
143
Saída no console
findAny Sequencial: 7
findAny Paralelo: 9
8. Ao realizar uma operação de reduce não há problema caso o acumulador seja associativo.
src/org/j6toj8/streams/parallelstreams/Streams_ParallelReduceAssociative.java
Stream.of(7, 2, 3, 8, 2, 1, 4, 5)
.reduce((e1, e2) -> e1 * e2)
.ifPresent(System.out::println);
Stream.of(7, 2, 3, 8, 2, 1, 4, 5)
.parallel()
.reduce((e1, e2) -> e1 * e2)
.ifPresent(System.out::println);
Saída no console
13440
13440
Perceba que o resultado com o Stream sequencial é idêntico ao paralelo. Isso ocorre porque a
operação de multiplicação é associativa, ou seja, fazer (2 x 2) x (3 x 3) é o mesmo que fazer (2
x 2 x 3) x 3, ou até mesmo 2 x (2 x 3) x 3.
src/org/j6toj8/streams/parallelstreams/Streams_ParallelReduceNonAssociative.java
Stream.of(7, 2, 3, 8, 2, 1, 4, 5)
.reduce((e1, e2) -> e1 - e2)
.ifPresent(System.out::println);
Stream.of(7, 2, 3, 8, 2, 1, 4, 5)
.parallel()
.reduce((e1, e2) -> e1 - e2)
.ifPresent(System.out::println);
Saída no console
-18
8
Isso ocorre pois a operação de subtração não é associativa, então o resultado pode variar
conforme o Stream for "fatiado" para ser executado em paralelo. Ou seja, fazer 1 - 2 - 3 - 4
144
não é o mesmo que fazer (1 - 2) - (3 - 4).
src/org/j6toj8/streams/parallelstreams/Streams_ParallelToConcurrentMap.java
Saída no console
Perceba que o resultados das operações pode ser diferente. Ao utilizar o Collector
toConcurrentMap em um Stream paralelo, as operações podem ser executadas em qualquer
ordem e não há necessidade de criar múltiplos Map’s para serem combinados posteriormente.
Em grandes Streams, isso pode ocasionar em um ganho de performance.
src/org/j6toj8/streams/parallelstreams/Streams_ParallelGroupingByConcurrent.java
145
Saída no console
Referências
Boyarsky, Jeanne; Selikoff, Scott. OCP: Oracle Certified Professional Java SE 8 Programmer
II Study Guide (p. 366). Wiley. Edição do Kindle.
146
Concurrency
Pacote Concurrent
Objetivo
O pacote java.util.concurrent inclui inúmeras classes para serem utilizadas em aplicações multi-
thread. Nesta seção serão apresentadas algumas dessas classes.
Muitas das classes do pacote concurrent são apenas versões das coleções comuns, porém com
blocos syncronized, garantindo que múltiplas threads poderão acessá-las ao mesmo tempo
mantendo sua integridade. As classes ConcurrentHashMap, ConcurrentLinkedQueue e
ConcurrentLinkedDeque são exemplos disso. Por isso é importante conhecer e lembrar das coleções
comuns do Java 6.
Todas as seções deste capítulo podem conter exemplos maiores do que os que foram apresentados
até agora, principalmente quando for necessário a criação de múltiplas Threads. É importante
dedicar um tempo maior para entender cada um desses exemplos.
1. É possível criar uma Fila que lança uma exceção após um tempo predefinido utilizando a classe
LinkedBlockingQueue.
src/org/j6toj8/concurrency/concurrentpackage/Concurrency_LinkedBlockingQueue.java
try {
queue.offer("ABC", 1, TimeUnit.SECONDS);
} catch (InterruptedException e) {
System.out.println("Não conseguiu inserir em menos de 1 segundo.");
}
try {
queue.poll(1, TimeUnit.SECONDS);
} catch (InterruptedException e) {
System.out.println("Não conseguiu remover em menos de 1 segundo.");
}
2. É possível criar uma Fila Duplamente Ligada (Deque) que lança uma exceção após um tempo
predefinido utilizando a classe LinkedBlockingDeque.
147
src/org/j6toj8/concurrency/concurrentpackage/Concurrency_LinkedBlockingDeque.java
try {
queue.offerFirst("ABC", 1, TimeUnit.SECONDS);
queue.offerLast("DEF", 1, TimeUnit.SECONDS);
} catch (InterruptedException e) {
System.out.println("Não conseguiu inserir em menos de 1 segundo.");
}
try {
queue.pollFirst(1, TimeUnit.SECONDS);
queue.pollLast(1, TimeUnit.SECONDS);
} catch (InterruptedException e) {
System.out.println("Não conseguiu remover em menos de 1 segundo.");
}
3. É possível criar uma lista que aloca todo o seu conteúdo em um novo array sempre que é
modificada utilizando a classe CopyOnWriteArrayList.
src/org/j6toj8/concurrency/concurrentpackage/Concurrency_CopyOnWriteArrayList.java
Saída no console
A
B
C
Lista final: [A, B, C, D]
Perceba que foi possível acrescentar um valor na lista durante a execução do forEach. Em uma
lista tradicional haveria ocorrido uma ConcurrentModificationException.
Perceba também que, mesmo alterando a lista dentro do forEach, a letra "D" não aparece no
console. Isso ocorre pois, quando a letra "D" foi inserida na lista, um novo array foi alocado
148
internamente contendo todos os novos elementos, e a iteração continuou ocorrendo no array
antigo.
src/org/j6toj8/concurrency/concurrentpackage/Concurrency_CollectionsSyncronized.java
5. Não é possível adicionar ou remover elementos durante o forEach de uma coleção sincronizada
que foi criada utilizando o método da classe Collections.
src/org/j6toj8/concurrency/concurrentpackage/Concurrency_CollectionsSyncronizedForEach.java
Saída no console
Perceba que, apesar do Map ter sido transformado em syncronized, não é possível alterá-lo
durante uma iteração com forEach. Isso é possível apenas nas versões Concurrent das coleções.
149
src/org/j6toj8/concurrency/concurrentpackage/Concurrency_CyclicBarrier.java
CyclicBarrier cyclicBarrier;
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + ": Primeira Parte");
try {
cyclicBarrier.await(); // sincronização das threads
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
try {
cyclicBarrier.await(); // sincronização das threads
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
150
Saída no console
Neste exemplo estão sendo criadas 3 threads. Todas executam instâncias da classe Acao que
recebem a mesma instância da classe CyclicBarrier. Toda vez que uma thread faz uma chamada
ao método await da instância de cyclicBarrier, ela fica suspensa até que todas as outras threads
cheguem até aquele mesmo ponto. Por isso todas as threads imprimem no console de forma
sincronizada. Se não houvesse sincronização, a saída no console seria completamente
imprevisível. Abaixo é o exemplo de uma execução sem a chamada ao método await:
Referências
Boyarsky, Jeanne; Selikoff, Scott. OCP: Oracle Certified Professional Java SE 8 Programmer
II Study Guide (p. 358). Wiley. Edição do Kindle.
• CyclicBarrier in Java.
151
Locks
Objetivo
As classes e interfaces Lock, ReadWriteLock e ReentrantLock permitem obter diferentes tipos de Locks
(em tradução livre: bloqueios ou travas). Esses Locks são utilizados para que um número limitado
de threads tenham acesso a mesma variável em um determinado momento, ou para que apenas
uma delas possa alterar seu valor.
Nesta seção serão apresentados exemplos utilizando essas classes e interfaces. Assim como em
outras seções deste capítulo, os exemplos podem ser grandes quando for necessário a criação de
threads. Dedique um tempo maior para entendê-los completamente.
Reentrant Lock
src/org/j6toj8/concurrency/locks/Locks_ReentrantLock.java
Saída no console
ABC
Perceba que o lock é removido dentro de um bloco finally. Isso garante que uma thread não irá
ficar com um lock indeterminadamente.
2. Chamar o método unlock sem ter obtido um lock anteriormente irá lançar uma exceção.
152
src/org/j6toj8/concurrency/locks/Locks_UnlockWithoutLock.java
Saída no console
ABC
Exception in thread "main" java.lang.IllegalMonitorStateException
at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:151)
at
java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchro
nizer.java:1261)
at java.util.concurrent.locks.ReentrantLock.unlock(ReentrantLock.java:457)
at
org.j6toj8.concurrency.locks.Locks_UnlockWithoutLock.main(Locks_UnlockWithoutLock.j
ava:14)
src/org/j6toj8/concurrency/locks/Locks_TryLock.java
if (temLock) {
try {
System.out.println("ABC");
} finally {
lock.unlock(); // desfaz o lock
}
} else {
System.out.println("DEF");
}
Saída no console
ABC
153
src/org/j6toj8/concurrency/locks/Locks_TryLockTimeout.java
if (temLock) {
try {
System.out.println("ABC");
} finally {
lock.unlock(); // desfaz o lock
}
} else {
System.out.println("DEF");
}
Saída no console
ABC
5. Em um cenário com várias threads, é possível que apenas uma delas consiga obter um lock.
154
src/org/j6toj8/concurrency/locks/Locks_TryLockMultithread.java
@Override
public void run() {
if (lock.tryLock()) {
try {
System.out.println(Thread.currentThread().getName() + ": Conseguiu o
Lock");
} finally {
lock.unlock();
}
}
}
}
Saída no console
Nesta execução com 3 threads, apenas duas conseguiram obter o lock imediatamente e imprimir
no console. Porém o resultado é imprevisível. Podem existir execuções onde todas obterão o
lock, e outras em que apenas uma thread conseguirá.
6. Uma thread pode obter mais de um lock no mesmo objeto Lock, mas deve desfazer o lock
múltiplas vezes também.
155
src/org/j6toj8/concurrency/locks/Locks_LockTwice.java
Saída no console
ABC
Como a thread chamou lock duas vezes, caso ela não houvesse chamado unlock duas vezes,
outra thread não seria capaz de obter o lock.
7. É possível garantir uma distribuição mais "justa" de locks passando true como argumento para
o ReentrantLock.
src/org/j6toj8/concurrency/locks/Locks_Fair.java
Ao passar o argumento true, quando várias threads estiverem esperando pelo mesmo lock, ele
será dado àquela thread que está aguardando a mais tempo.
ReentrantReadWriteLock
1. É possível separar locks de leitura e escrita utilizando a classe ReadWriteLock. Locks de leitura
podem ser obtidos por múltiplas threads, porém locks de escrita não.
156
src/org/j6toj8/concurrency/locks/Locks_ReadWriteLock.java
@Override
public void run() {
Lock readLock = lock.readLock();
if (readLock.tryLock()) {
try {
System.out.println(Thread.currentThread().getName() + ": Conseguiu o Lock
de leitura");
} finally {
readLock.unlock();
}
}
157
Saída no console
Perceba que todas as threads conseguiram obter o lock de leitura, porém apenas uma conseguiu
obter o lock de escrita.
2. Se uma thread já possuir o lock de escrita, outras não conseguirão obter nem mesmo o lock de
leitura.
158
src/org/j6toj8/concurrency/locks/Locks_ReadWriteLockInverted.java
@Override
public void run() {
Lock writeLock = lock.writeLock();
if (writeLock.tryLock()) {
try {
System.out.println(Thread.currentThread().getName() + ": Conseguiu o Lock
de escrita");
} finally {
writeLock.unlock();
}
}
159
Saída no console
Perceba que neste exemplo o lock de escrita está sendo obtido antes do de leitura, de tal forma
que apenas a primeira thread que foi executada conseguiu obter os dois locks.
Referências
• Applying Locks
Boyarsky, Jeanne; Selikoff, Scott. OCP: Oracle Certified Professional Java SE 8 Programmer
II Study Guide (p. 607). Wiley. Edição do Kindle.
• Guide to java.util.concurrent.Locks.
Use Executor, ExecutorService, Executors, Callable, and Future to execute tasks using
thread pools
-
Usar Executor, ExecutorService, Executors, Callable, e Future para executar tarefas
usando um conjunto de threads
160
src/org/j6toj8/concurrency/executetasks/Tasks_SingleThread.java
Saída no console
O método execute recebe uma instância de Runnable, que nestes exemplos serão criadas na
forma de uma expressão lambda.
Perceba também que é boa prática chamar o método shutdown dentro de um bloco finally,
buscando não deixar threads pendentes. Fique atento, pois chamar o método shutdown não
garante que a threads foram finalizadas.
Em um programa real, talvez você não queira que a tarefa seja finalizada logo após a sua
criação, como nos exemplos aqui apresentados. Logo, pode ser necessário invocar o shutdown em
algum ponto do sistema, ou quando a aplicação for encerrada. Apenas lembre-se de invocar o
método shutdown para que sua aplicação possa ser encerrada corretamente, caso contrário o
processo poderá ficar travado impedindo que o programe encerre corretamente..
2. É possível executar várias tarefas em uma mesma thread separada utilizando o mesmo método
newSingleThreadExecutor.
161
src/org/j6toj8/concurrency/executetasks/Tasks_SingleThreadManyTasks.java
Saída no console
Perceba que todas as tarefas foram executadas pela mesma thread, que é diferente e paralela à
thread principal.
src/org/j6toj8/concurrency/executetasks/Tasks_ShutdownNow.java
Saída no console
162
4. É possível utilizar o método submit para ter informações sobre a tarefa que está sendo
executada.
src/org/j6toj8/concurrency/executetasks/Tasks_SingleThreadSubmit.java
Saída no console
A instância de Future representa um valor que será retornado no futuro. Nesse caso é um valor
que representa a própria tarefa submetida.
Veja que a tarefa inicialmente ainda não havia sido finalizada, mas nas duas últimas impressões
no console já havia sido.
O resultado desse exemplo irá ser diferente para cada execução, tendo em vista a utilização de
mais de uma thread: a principal e uma paralela.
5. Também é possível ver outras informações sobre a tarefa ou tentar cancelar sua execução.
163
src/org/j6toj8/concurrency/executetasks/Tasks_SingleThreadFuture.java
Saída no console
Perceba como foi possível cancelar a tarefa mesmo após ela já ter impresso no console.
6. É possível retornar um valor da tarefa utilizando o método submit que recebe uma instância de
Callable.
164
src/org/j6toj8/concurrency/executetasks/Tasks_SingleThreadCallable.java
// O .get() abaixo irá esperar a tarefa finalizar para pegar seu retorno
System.out.println("Retorno da tarefa: " + retornoDaTarefa.get());
} catch (InterruptedException | ExecutionException e) {
System.out.println("Execução interrompida.");
} finally {
if (executor != null) {
executor.shutdown();
}
}
Saída no console
Nesse caso a instância de Future representa o valor retornado pela tarefa, uma String.
É necessário o bloco catch para capturar as exceções que podem ser lançadas pelo método get
da classe Future.
7. É possível passar uma lista de tarefas para serem executadas utilizando o método invokeAll.
165
src/org/j6toj8/concurrency/executetasks/Tasks_SingleThreadInvokeAll.java
Saída no console
8. É possível passar uma lista de tarefas onde apenas uma será concluída utilizando o método
invokeAny.
166
src/org/j6toj8/concurrency/executetasks/Tasks_SingleThreadInvokeAny.java
// invokeAny devolve apenas uma das tarefas que finalizou e interrompe as outras
String retorno = executor.invokeAny(tarefas);
System.out.println("Retorno da tarefa: " + retorno);
Saída no console
As outras tarefas são interrompidas ou, como neste caso só temos uma thread, nem são
executadas.
Em um exemplo com inúmeras threads, é possível que qualquer uma das 3 tarefas finalize
primeiro e interrompa as outras duas.
9. Runnable não possui retorno nem pode lançar exceção checada, enquanto Callable possui
retorno e pode lançar exceção checada. É importante saber diferenciar qual versão do método
submit está sendo chamado.
167
src/org/j6toj8/concurrency/executetasks/Tasks_RunnableCallable.java
// tarefa que lança uma Exception deve ser Callable, logo deve ter retorno
executor.submit(() -> {Thread.sleep(1); return "Callable";});
} finally {
if (executor != null) {
executor.shutdown();
}
}
10. É possível esperar as tarefas finalizarem sua execução por um tempo específico com o método
awaitTermination.
168
src/org/j6toj8/concurrency/executetasks/Tasks_SingleThreadAwaitTermination.java
if (executor != null) {
try {
System.out.println("Tarefas finalizadas? " + executor.isTerminated());
executor.awaitTermination(1, TimeUnit.SECONDS);
System.out.println("Tarefas finalizadas? " + executor.isTerminated());
} catch (InterruptedException e) {
System.out.println("Erro de interrupção.");
}
}
Saída no console
Caso as tarefas não tenham terminado depois de 1 segundo, a execução teria continuado
normalmente. Não há exceção neste caso. A única diferença seria a saída no console, pois nas
duas vezes seria impresso false.
Tarefas agendadas
169
src/org/j6toj8/concurrency/executetasks/Schedule_SingleThread.java
Saída no console
Agora: 16:05:25.978
Execução: 16:05:28.984
O método schedule utilizado neste exemplo recebe um Runnable, por isso não há retorno na
expressão lambda.
2. É possível agendar uma tarefa com retorno passando uma instância de Callable.
170
src/org/j6toj8/concurrency/executetasks/Schedule_SingleThreadCallable.java
Saída no console
3. É possível agendar uma tarefa para ser executada a cada X tempo usando o método
scheduleAtFixedRate.
171
src/org/j6toj8/concurrency/executetasks/Schedule_SingleThreadFixedRate.java
Saída no console
Neste exemplos a execução demora 3 segundos para começar, e é repetida a cada 1 segundo, até
o shutdown ser chamado. Por isso existe um sleep no final do bloco try, para garantir que
enxergaríamos a execução da tarefa por algumas vezes antes de invocar o shutdown.
172
4. É possível agendar uma tarefa para ser executada X tempo após o término da tarefa anterior
usando o método scheduleWithFixedDelay.
src/org/j6toj8/concurrency/executetasks/Schedule_SingleThreadFixedDelay.java
Saída no console
Este exemplo é muito parecido com o anterior, porém há uma diferença importante: a nova
173
tarefa só começa a executar 1 segundo depois do término da anterior. No exemplo acima a
tarefa era executada a cada 1 segundo, independente do término da anterior. Perceba as
diferenças nos milissegundos.
Múltiplas Threads
Todos os exemplos até agora utilizaram apenas uma thread adicional. Em todos eles, seria possível
utilizar construtores de Executors que fornecem mais de uma thread.
1. É possível criar um Executor que cria threads conforme o necessário, e as reutiliza quando
possível, utilizando o método newCachedThreadPool.
src/org/j6toj8/concurrency/executetasks/TasksMulti_CachedThreadPool.java
Saída no console
174
src/org/j6toj8/concurrency/executetasks/TasksMulti_FixedThreadPool.java
Saída no console
175
src/org/j6toj8/concurrency/executetasks/TasksMulti_ScheduledThreadPool.java
Saída no console
Agora: 16:33:36.825
Execução 1: pool-1-thread-1 - 16:33:39.834
Execução 2: pool-1-thread-2 - 16:33:39.836
Execução 3: pool-1-thread-1 - 16:33:39.837
Execução 4: pool-1-thread-1 - 16:33:39.838
Execução 5: pool-1-thread-2 - 16:33:39.838
Referências
Boyarsky, Jeanne; Selikoff, Scott. OCP: Oracle Certified Professional Java SE 8 Programmer
II Study Guide (p. 335). Wiley. Edição do Kindle.
• hhttps://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ExecutorService.html[Inter
face ExecutorService.] Java Plataform SE 7.
176
Framework Fork/Join
Objetivo
Com o framework de Fork/Join é possível dividir uma tarefa grande em pedaços menores e executá-
los em paralelo. A utilização do framework é simples. A criação de tarefas quebradas em várias
partes pode ser um pouco mais complexa.
• Recebe um valor;
◦ Caso negativo, quebra em uma ou mais partes e cria novas tarefas menores;
Serão apresentados exemplos utilizando a classe RecursiveAction, que não retorna valores, e por
isso sua implementação é mais simples. E utilizando a classe RecursiveTask, que retorna valores e
por isso sua implementação é um pouco mais complexa.
1. É possível implementar uma RecursiveAction que divide uma tarefa grande em partes menores.
src/org/j6toj8/concurrency/forkjoin/ForkJoin_RecursiveAction.java
@Override
protected void compute() {
if (stringParaImprimir.length() < 10) {
// se a String tiver menos de 10 caracteres, será impressa
System.out.println(Thread.currentThread().getName() + " - " +
stringParaImprimir);
} else {
177
// caso contrário, são criadas duas novas tarefas, uma com a primeira metade
da String
// e outra com a segunda metade da String
List<ImpressaoDeStrings> novasTarefas = divideEmDuasTarefas();
Saída no console
ForkJoinPool-1-worker-1 - ABCDEF
ForkJoinPool-1-worker-4 - TUVWXYZ
ForkJoinPool-1-worker-2 - NOPQRS
ForkJoinPool-1-worker-3 - GHIJKLM
178
c. No método compute, a tarefa decide se irá imprimir diretamente no console, ou se irá dividir
esse trabalho em duas partes: caso a String tenha menos que 10 caracteres, ela imprime
diretamente no Console; caso contrário, divide o trabalho em duas novas tarefas.
Perceba que cada impressão no console é realizada em uma thread diferente, ficando claro
que o trabalho está sendo compartilhado por múltiplas threads.
Perceba também que o processamento da String não retorna nenhum valor, e por isso foi
utilizada a classe RecursiveAction.
2. Caso seja necessário retornar um valor, é possível implementar uma RecursiveTask que divide
uma tarefa grande em partes menores.
src/org/j6toj8/concurrency/forkjoin/ForkJoin_RecursiveTask.java
@Override
protected Integer compute() {
if (stringParaImprimir.length() < 10) {
// se a String tiver menos de 10 caracteres, será impressa
System.out.println(Thread.currentThread().getName() + " - " +
stringParaImprimir);
// 'fork' irá enviar a tarefa1 para ser executada em uma nova thread
ForkJoinTask<Integer> primeiraTarefa = tarefa1.fork();
179
// retornamos a soma dos resultados, pois é a quantidade de carateres
impressos
return resultadoDaTarefa2 + resultadoDaTarefa1;
}
}
Saída no console
ForkJoinPool-1-worker-3 - NOPQRS
ForkJoinPool-1-worker-2 - GHIJKLM
ForkJoinPool-1-worker-4 - ABCDEF
ForkJoinPool-1-worker-1 - TUVWXYZ
Resultado da execução: 26
c. No método compute, a tarefa decide se irá imprimir diretamente no console, ou se irá dividir
esse trabalho em duas partes: caso a String tenha menos que 10 caracteres, ela imprime
180
diretamente no Console; caso contrário, divide o trabalho em duas novas tarefas.
Referências
Boyarsky, Jeanne; Selikoff, Scott. OCP: Oracle Certified Professional Java SE 8 Programmer
II Study Guide (p. 377). Wiley. Edição do Kindle.
181
Java File I/O (NIO.2)
Paths
Objetivo
A classe Path representa um arquivo ou um diretório no sistema de arquivos, e a maioria das suas
operações não altera diretamente arquivos ou diretórios.
Para que os exemplos executem independente do sistema, será utilizado o diretório do usuário, que
no Java está disponível em uma propriedade da JVM chamada user.home.
182
src/org/j6toj8/fileio/paths/Paths_Creation.java
// path absoluto
Path path1 = Paths.get("/home/rinaldo");
System.out.println("Path 1: " + path1);
// path relativo
Path path8 = Paths.get("rinaldo");
System.out.println("Path 8: " + path8);
183
Saída no console
2. É possível criar uma instância de Path apontando para um diretório ou arquivo que não existe.
src/org/j6toj8/fileio/paths/Paths_CreationDoesntExists.java
Saída no console
src/org/j6toj8/fileio/paths/Paths_ToFile.java
Saída no console
184
4. Existem inúmeros método no Path para recuperar informações a seu respeito.
src/org/j6toj8/fileio/paths/Paths_Information.java
System.out.println();
Saída no console
185
src/org/j6toj8/fileio/paths/Paths_Names.java
Saída no console
home
rinaldo
arquivos
arquivo.txt
src/org/j6toj8/fileio/paths/Paths_ToAbsolute.java
System.out.println();
Saída no console
arquivos
É absoluto? false
/home/rinaldo/Desenvolvimento/git/java6-to-java8/arquivos
É absoluto? true
Neste caso a saída do console vai depende do diretório onde a aplicação está sendo executada.
186
src/org/j6toj8/fileio/paths/Paths_SubPath.java
Saída no console
187
src/org/j6toj8/fileio/paths/Paths_Normalize.java
System.out.println();
System.out.println();
Saída no console
Path: /home/rinaldo/arquivos/./arquivo1.txt
Path normalize: /home/rinaldo/arquivos/arquivo1.txt
Path: /home/rinaldo/arquivos/../arquivo1.txt
Path normalize: /home/rinaldo/arquivo1.txt
src/org/j6toj8/fileio/paths/Paths_Resolve.java
System.out.println();
188
Saída no console
Perceba que sempre que o argumento é um Path absoluto, o resultado final é ele mesmo.
Quando o argumento é um Path relativo, ele é acrescentado ao original, seja este absoluto ou
relativo.
189
src/org/j6toj8/fileio/paths/Paths_Relativize.java
System.out.println();
try {
// Exceção será lançada, pois não é possível chamar relativize em tipos diferente
de Path
System.out.println("Absoluto + Relativo: " + pathAbsoluto1.
relativize(pathRelativo1));
} catch (Exception e) {
e.printStackTrace();
}
try {
// Exceção será lançada, pois não é possível chamar relativize em tipos diferente
de Path
System.out.println("Relativo + Absoluto: " + pathRelativo1.
relativize(pathAbsoluto1));
} catch (Exception e) {
e.printStackTrace();
}
190
Saída no console
Absoluto 1: /home/rinaldo/arquivos
Absoluto 2: /home/rinaldo/arquivos/arquivo1.txt
Relativo 1: arquivo1.txt
Relativo 2: arquivos/arquivo1.txt
Absoluto 1 + Absoluto 2: arquivo1.txt
Absoluto 2 + Absoluto 1: ..
Relativo 1 + Relativo 2: ../arquivos/arquivo1.txt
Relativo 2 + Relativo 1: ../../arquivo1.txt
java.lang.IllegalArgumentException: 'other' is different type of Path
at sun.nio.fs.UnixPath.relativize(UnixPath.java:416)
at sun.nio.fs.UnixPath.relativize(UnixPath.java:43)
at org.j6toj8.fileio.paths.Paths_Relativize.main(Paths_Relativize.java:33)
java.lang.IllegalArgumentException: 'other' is different type of Path
at sun.nio.fs.UnixPath.relativize(UnixPath.java:416)
at sun.nio.fs.UnixPath.relativize(UnixPath.java:43)
at org.j6toj8.fileio.paths.Paths_Relativize.main(Paths_Relativize.java:40)
Todas essas combinações podem aparecer no exame, então entenda bem como cada uma delas
se comporta. Lembre-se principalmente de que não é possível derivar um Path absoluto de um
relativo, e vice-versa.
11. É possível converter um Path sintético, que não aponta de fato para um arquivo no sistema de
arquivos, em um Path real, que aponta para um arquivo ou diretório que existe no sistema de
arquivos.
src/org/j6toj8/fileio/paths/Paths_ToRealPath.java
try {
Path realPath = pathQueExiste.toRealPath();
System.out.println("realPath: " + realPath);
} catch (IOException e) {
e.printStackTrace();
}
try {
pathQueNaoExiste.toRealPath(); // LANÇA EXCEÇÃO
} catch (IOException e) {
e.printStackTrace();
}
191
Saída no console
Perceba que é lançada exceção caso o arquivo realmente não exista no sistema de arquivos.
Referências
• Introducing NIO.2
Boyarsky, Jeanne; Selikoff, Scott. OCP: Oracle Certified Professional Java SE 8 Programmer
II Study Guide (p. 454). Wiley. Edição do Kindle.
Files
Objetivo
Check, delete, copy, or move a file or directory by using the Files class
-
Checar, deletar, copiar ou mover um arquivo ou diretório utilizando a classe Files
A classe Files contém inúmeros métodos estáticos para lidar com instâncias de Path. A maioria dos
métodos recebem instâncias de Path para realizar alguma operação no arquivo ou diretório
representado pelo Path. Muitos irão lançar uma exceção caso o arquivo ou diretório não exista no
sistema de arquivos.
192
src/org/j6toj8/fileio/files/Files_Checks.java
try {
System.out.println("Qual o tamanho de Path 1? " + Files.size(path1));
} catch (IOException e1) {
// Lança exceção se o arquivo não existir
e1.printStackTrace();
}
try {
System.out.println("Path 1 é oculto? " + Files.isHidden(path1));
} catch (IOException e) {
// Lança exceção se o arquivo não existir
e.printStackTrace();
}
try {
System.out.println("Path 1 e Path 1 são iguais? " + Files.isSameFile(path1,
path1));
} catch (IOException e) {
// Lança exceção se o arquivo não existir
e.printStackTrace();
}
193
Saída no console
Perceba que algumas chamadas lançam IOException. Isso ocorre pois elas irão lançar essa
exceção caso o Path não exista no sistema de arquivos.
2. É possível verificar se dois Path's são iguais, mesmo que estejam representados de forma
diferente.
src/org/j6toj8/fileio/files/Files_SameFile.java
try {
// todos os Path são iguais
System.out.println("Path 1 e Path 2 são iguais? " + Files.isSameFile(path1,
path2));
System.out.println("Path 2 e Path 3 são iguais? " + Files.isSameFile(path2,
path3));
System.out.println("Path 3 e Path 4 são iguais? " + Files.isSameFile(path3,
path4));
System.out.println("Path 1 e Path 4 são iguais? " + Files.isSameFile(path1,
path4));
} catch (IOException e) {
e.printStackTrace();
}
194
Saída no console
Perceba que nesse exemplo todos os Path são iguais, pois apontam para o mesmo arquivo. Ou
seja, o método realmente verifica se o arquivo no sistema de arquivos é o mesmo, independente
da forma como o diretório está sendo representado no Path. Isso funcionará inclusive para links
simbólicos que apontam para o mesmo arquivo.
src/org/j6toj8/fileio/files/Files_CreateFile.java
try {
System.out.println("Path existe? " + Files.exists(path));
Files.createFile(path);
System.out.println("Path existe? " + Files.exists(path));
} catch (IOException e) {
e.printStackTrace();
}
Saída no console
195
src/org/j6toj8/fileio/files/Files_CreateDirectory.java
try {
System.out.println("Path existe? " + Files.exists(path));
Files.createDirectory(path);
System.out.println("Path existe? " + Files.exists(path));
} catch (IOException e) {
e.printStackTrace();
}
Saída no console
5. Caso o Path possua vários elementos a serem criados, é necessário utilizar o método
createDirectories, no plural, caso contrário será lançada uma exceção.
196
src/org/j6toj8/fileio/files/Files_CreateDirectories.java
try {
Files.createDirectory(path); // MÉTODO ERRADO, LANÇA EXCEÇÃO
} catch (IOException e) {
e.printStackTrace();
}
try {
System.out.println("Path existe? " + Files.exists(path));
Files.createDirectories(path);
System.out.println("Path existe? " + Files.exists(path));
} catch (IOException e) {
e.printStackTrace();
}
Saída no console
197
src/org/j6toj8/fileio/files/Files_CopyPath.java
try {
System.out.println("Path 1 existe? " + Files.exists(path1));
Files.createFile(path1);
System.out.println("Path 1 existe? " + Files.exists(path1));
System.out.println("Path 2 existe? " + Files.exists(path2));
Files.copy(path1, path2);
System.out.println("Path 2 existe? " + Files.exists(path2));
} catch (IOException e) {
e.printStackTrace();
}
Saída no console
Ao copiar um arquivo, é necessário que os diretórios já existam, caso contrário será lançada
uma exceção.
198
src/org/j6toj8/fileio/files/Files_CopyToPath.java
Saída no console
src/org/j6toj8/fileio/files/Files_CopyFromPath.java
199
Saída no console
src/org/j6toj8/fileio/files/Files_MoveFile.java
try {
System.out.println("Arquivo origem existe? " + Files.exists(arquivoOrigem));
Files.createFile(arquivoOrigem);
System.out.println("Arquivo origem existe? " + Files.exists(arquivoOrigem));
Saída no console
Ao mover um Path para um diretório que não exista, será lançado exceção.
200
src/org/j6toj8/fileio/files/Files_DeletePath.java
try {
System.out.println("Path existe? " + Files.exists(path));
Files.createFile(path);
System.out.println("Path existe? " + Files.exists(path));
Saída no console
Perceba que existem dois métodos diferentes para apagar. O primeira irá lançar exceção se o
arquivo não existir, o segundo não irá lançar.
201
src/org/j6toj8/fileio/files/Files_WriteFile.java
Saída no console
Perceba que primeiro foi criado o arquivo. Depois ele foi escrito com um BufferedWriter. E
depois foi lido com um BufferedReader.
12. É possível ler todas as linhas de um arquivo com uma única chamada.
202
src/org/j6toj8/fileio/files/Files_ReadAllLines.java
arquivo.txt
1
2
3
4
5
Saída no console
203
src/org/j6toj8/fileio/files/Files_LastModified.java
try {
Files.createFile(path);
System.out.println("Data de Modificação: " + Files.getLastModifiedTime(path));
FileTime fileTime = FileTime.from(Instant.now().plusMillis(10000));
Files.setLastModifiedTime(path, fileTime);
System.out.println("Data de Modificação: " + Files.getLastModifiedTime(path));
} catch (IOException e) {
e.printStackTrace();
}
Saída no console
204
src/org/j6toj8/fileio/files/Files_Owner.java
try {
Files.createFile(path);
System.out.println(Files.getOwner(path)); // imprime o owner atual
Saída no console
15. É possível recuperar todos os atributos de um arquivo com uma única chamada, utilizando a
classe BasicFileAttributes.
205
src/org/j6toj8/fileio/files/Files_BasicFileAttributes.java
try {
BasicFileAttributes attributes = Files.readAttributes(path,
BasicFileAttributes.class);
System.out.println("Tamanho: " + attributes.size());
Saída no console
16. É possível modificar as datas de criação, modificação e acesso do arquivo com a classe
BasicFileAttributeView.
206
src/org/j6toj8/fileio/files/Files_BasicFileAttributeView.java
try {
BasicFileAttributeView attributesView = Files.getFileAttributeView(path,
BasicFileAttributeView.class);
BasicFileAttributes attributesAntigos = attributesView.readAttributes();
Saída no console
207
Referências
• Introducing NIO.2
Boyarsky, Jeanne; Selikoff, Scott. OCP: Oracle Certified Professional Java SE 8 Programmer
II Study Guide (p. 454). Wiley. Edição do Kindle.
DirectoryStream e FileVisitor
Objetivo
Nestão seção serão apresentadas duas classes para percorrer diretórios: DirectoryStream e
FileVisitor.
src/org/j6toj8/fileio/recursiveaccess/Recursive_DirectoryStream.java
208
Saída no console
Path: /home/rinaldo/arquivos
/home/rinaldo/arquivos/arquivo1.txt
/home/rinaldo/arquivos/arquivo3.txt
/home/rinaldo/arquivos/subpasta1
/home/rinaldo/arquivos/arquivo2.txt
/home/rinaldo/arquivos/arquivo1.txt
/home/rinaldo/arquivos/arquivo3.txt
/home/rinaldo/arquivos/subpasta1
/home/rinaldo/arquivos/arquivo2.txt
◦ Pode ser iterada com o método forEach que recebe uma expressão lambda.
◦ Não lista os subdiretórios de forma recursiva, mas sim apenas o primeiro nível.
209
src/org/j6toj8/fileio/recursiveaccess/Recursive_SimpleFileVisitor.java
Saída no console
Path: /home/rinaldo/arquivos
Arquivo visitado: /home/rinaldo/arquivos/arquivo1.txt. Tamanho: 2
Arquivo visitado: /home/rinaldo/arquivos/arquivo3.txt. Tamanho: 10
Arquivo visitado: /home/rinaldo/arquivos/subpasta1/subpasta12/arquivo122.txt.
Tamanho: 10
Arquivo visitado: /home/rinaldo/arquivos/subpasta1/subpasta12/arquivo121.txt.
Tamanho: 10
Arquivo visitado: /home/rinaldo/arquivos/subpasta1/arquivo11.txt. Tamanho: 10
Arquivo visitado: /home/rinaldo/arquivos/subpasta1/arquivo12.txt. Tamanho: 2
Arquivo visitado: /home/rinaldo/arquivos/subpasta1/arquivo13.txt. Tamanho: 2
Arquivo visitado: /home/rinaldo/arquivos/arquivo2.txt. Tamanho: 2
Veja que foi retornado FileVisitResult.CONTINUE do método visitFile. Isso instrui o FileVisitor a
continuar visitando a árvore de arquivos. Também é possível retornar TERMINATE, SKIP_SUBTREE e
SKIP_SIBLINGS, que serão apresentados a seguir.
210
src/org/j6toj8/fileio/recursiveaccess/Recursive_VisitorTerminate.java
Saída no console
Path: /home/rinaldo/arquivos
Arquivo visitado: /home/rinaldo/arquivos/arquivo1.txt. Tamanho: 2
Arquivo visitado: /home/rinaldo/arquivos/arquivo3.txt. Tamanho: 10
Arquivo visitado: /home/rinaldo/arquivos/subpasta1/subpasta12/arquivo122.txt.
Tamanho: 10
Arquivo encontrado. Finalizando.
211
src/org/j6toj8/fileio/recursiveaccess/Recursive_VisitorDirectory.java
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
throws IOException {
// Ação que será executada antes de visitar um diretório
if (dir.getFileName().toString().equals("subpasta12")) {
return FileVisitResult.SKIP_SUBTREE; // ignora o diretório subpasta12
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws
IOException {
// Ação que será executada depois de visitar um diretório
return FileVisitResult.CONTINUE;
}
Saída no console
Path: /home/rinaldo/arquivos
Arquivo visitado: /home/rinaldo/arquivos/arquivo1.txt. Tamanho: 2
Arquivo visitado: /home/rinaldo/arquivos/arquivo3.txt. Tamanho: 10
Arquivo visitado: /home/rinaldo/arquivos/subpasta1/arquivo11.txt. Tamanho: 10
Arquivo visitado: /home/rinaldo/arquivos/subpasta1/arquivo12.txt. Tamanho: 2
Arquivo visitado: /home/rinaldo/arquivos/subpasta1/arquivo13.txt. Tamanho: 2
Arquivo visitado: /home/rinaldo/arquivos/arquivo2.txt. Tamanho: 2
212
5. Também é possível ignorar todos os elementos que estão no mesmo nível de um Path.
src/org/j6toj8/fileio/recursiveaccess/Recursive_VisitorIgnoreSiblings.java
Saída no console
Path: /home/rinaldo/arquivos
Arquivo visitado: /home/rinaldo/arquivos/arquivo1.txt. Tamanho: 2
Arquivo visitado: /home/rinaldo/arquivos/arquivo3.txt. Tamanho: 10
Arquivo visitado: /home/rinaldo/arquivos/subpasta1/subpasta12/arquivo122.txt.
Tamanho: 10
Arquivo visitado: /home/rinaldo/arquivos/subpasta1/subpasta12/arquivo121.txt.
Tamanho: 10
Arquivo visitado: /home/rinaldo/arquivos/subpasta1/arquivo11.txt. Tamanho: 10
Arquivo visitado: /home/rinaldo/arquivos/arquivo2.txt. Tamanho: 2
213
src/org/j6toj8/fileio/recursiveaccess/Recursive_FileVisitor.java
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
throws IOException {
System.out.println("Antes de visitar o diretório: " + dir);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws
IOException {
System.out.println("Após de visitar o diretório: " + dir);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) throws
IOException {
System.out.println("Falhou ao visitar o arquivo: " + file);
return FileVisitResult.CONTINUE;
}
}
214
Saída no console
Path: /home/rinaldo/arquivos
Antes de visitar o diretório: /home/rinaldo/arquivos
Arquivo visitado: /home/rinaldo/arquivos/arquivo1.txt
Arquivo visitado: /home/rinaldo/arquivos/arquivo3.txt
Antes de visitar o diretório: /home/rinaldo/arquivos/subpasta1
Antes de visitar o diretório: /home/rinaldo/arquivos/subpasta1/subpasta12
Arquivo visitado: /home/rinaldo/arquivos/subpasta1/subpasta12/arquivo122.txt
Arquivo visitado: /home/rinaldo/arquivos/subpasta1/subpasta12/arquivo121.txt
Após de visitar o diretório: /home/rinaldo/arquivos/subpasta1/subpasta12
Arquivo visitado: /home/rinaldo/arquivos/subpasta1/arquivo11.txt
Antes de visitar o diretório: /home/rinaldo/arquivos/subpasta1/subpasta11
Após de visitar o diretório: /home/rinaldo/arquivos/subpasta1/subpasta11
Arquivo visitado: /home/rinaldo/arquivos/subpasta1/arquivo12.txt
Arquivo visitado: /home/rinaldo/arquivos/subpasta1/arquivo13.txt
Após de visitar o diretório: /home/rinaldo/arquivos/subpasta1
Arquivo visitado: /home/rinaldo/arquivos/arquivo2.txt
Após de visitar o diretório: /home/rinaldo/arquivos
7. É possível definir opções adicionais e limitar a profundidade utilizando outra versão do método
walkFileTree.
src/org/j6toj8/fileio/recursiveaccess/Recursive_VisitorOptionsAndDepth.java
215
Saída no console
Path: /home/rinaldo/arquivos
Arquivo visitado: /home/rinaldo/arquivos/arquivo1.txt
Arquivo visitado: /home/rinaldo/arquivos/arquivo3.txt
Arquivo visitado: /home/rinaldo/arquivos/subpasta1/subpasta12
Arquivo visitado: /home/rinaldo/arquivos/subpasta1/arquivo11.txt
Arquivo visitado: /home/rinaldo/arquivos/subpasta1/subpasta11
Arquivo visitado: /home/rinaldo/arquivos/subpasta1/arquivo12.txt
Arquivo visitado: /home/rinaldo/arquivos/subpasta1/arquivo13.txt
Arquivo visitado: /home/rinaldo/arquivos/arquivo2.txt
Referências
Boyarsky, Jeanne; Selikoff, Scott. OCP: Oracle Certified Professional Java SE 8 Programmer
II Study Guide (p. 617). Wiley. Edição do Kindle.
Find a file by using the PathMatcher interface, and use Java SE 8 I/O improvements,
including Files.find(), Files.walk(), and lines() methods
-
Encontrar um arquivo usando a interface PathMatcher, e usar as melhorias de I/O do
Java SE 8, incluindo os métodos Files.find(), Files.walk(), and lines()
Nestão seção serão apresentadas melhorias do Java 8 para encontrar e ler arquivos. São operações
que já poderiam ser realizadas com outros métodos antes do Java 8. Porém, com essas melhorias, é
possível realizar essas operações utilizando Streams.
216
src/org/j6toj8/fileio/fileimprovements/Improvements_Walk.java
try {
System.out.println("\nTodos os arquivos e diretórios: ");
Files.walk(path) // cria o stream
.forEach(System.out::println); // imprime no console
} catch (IOException e) {
e.printStackTrace();
}
try {
System.out.println("\nOs primeiro 5 arquivos e diretórios: ");
Files.walk(path)
.limit(5)
.forEach(System.out::println);
} catch (IOException e) {
e.printStackTrace();
}
Saída no console
Path: /home/rinaldo/arquivos
Perceba que a instância criada é realmente um Stream<Path>, de tal forma que é possível
realizar as operações disponíveis em qualquer Stream, como o método filter.
217
2. Existe uma versão do método walk para definir opções adicionais e limitar a profundidade do
acesso aos subdiretórios.
src/org/j6toj8/fileio/fileimprovements/Improvements_WalkDepth.java
try {
System.out.println("\nArquivos e Links simbólicos até o segundo nível: ");
Files.walk(path, 2, FileVisitOption.FOLLOW_LINKS)
.forEach(System.out::println);
} catch (IOException e) {
e.printStackTrace();
}
Saída no console
Path: /home/rinaldo/arquivos
3. É possível pesquisar por um arquivo utilizando o método find e filtrar por atributos.
218
src/org/j6toj8/fileio/fileimprovements/Improvements_Find.java
try {
System.out.println("\nTodos os arquivos, ignorando diretórios, até o segundo
nível: ");
// ao chamar o find:
// primeiro argumento: o path inicial
// segundo argumento: o limite de profundidade
// terceiro argumento: expressão lambda para filtrar
Files.find(path, 2, (p, a) -> a.isRegularFile())
.forEach(System.out::println);
} catch (IOException e) {
e.printStackTrace();
}
Saída no console
Path: /home/rinaldo/arquivos
Perceba que ao utilizar o find a expressão lambda tem acesso ao Path e seus atributos, que é
uma instância de BasicFileAttributes, permitindo uma maior flexibilidade na busca.
219
src/org/j6toj8/fileio/fileimprovements/Improvements_List.java
try {
System.out.println("\nListagem do diretório: ");
Files.list(path)
.forEach(System.out::println);
} catch (IOException e) {
e.printStackTrace();
}
try {
System.out.println("\nListagem do diretório, apenas arquivos: ");
Files.list(path)
.filter(p -> Files.isRegularFile(p))
.forEach(System.out::println);
} catch (IOException e) {
e.printStackTrace();
}
Saída no console
Path: /home/rinaldo/arquivos
Listagem do diretório:
/home/rinaldo/arquivos/arquivo1.txt
/home/rinaldo/arquivos/arquivo3.txt
/home/rinaldo/arquivos/subpasta1
/home/rinaldo/arquivos/arquivo2.txt
5. É possível recuperar todas as linhas de um arquivo como um Stream utilizando o método lines.
220
src/org/j6toj8/fileio/fileimprovements/Improvements_Lines.java
try {
System.out.println("\nConteúdo do arquivo: ");
Files.lines(path) // recupera todas as linhas do arquivo como Stream
.forEach(System.out::println);
} catch (IOException e) {
e.printStackTrace();
}
try {
System.out.println("\nConteúdo do arquivo maior que 2: ");
Files.lines(path)
.filter(s -> Integer.parseInt(s) > 2) // filtra maior que 2
.forEach(System.out::println);
} catch (IOException e) {
e.printStackTrace();
}
Saída no console
Path: /home/rinaldo/arquivos/subpasta1/arquivo11.txt
Conteúdo do arquivo:
1
2
3
4
5
Conteúdo do arquivo:
3
4
5
221
Referências
Boyarsky, Jeanne; Selikoff, Scott. OCP: Oracle Certified Professional Java SE 8 Programmer
II Study Guide (p. 486). Wiley. Edição do Kindle.
WatchService
Objetivo
O WatchService é uma API para monitorar mudanças em arquivos e diretórios. Serão apresentadas
as principais formas de realizar essa monitoração.
• WatchEvent → representa um evento em si, onde é possível obter informações do que foi
alterado.
222
src/org/j6toj8/fileio/watchservice/WatchService_CreateDelete.java
Saída no console
Path: /home/rinaldo/arquivos
Eventos capturados. Quantidade: 1
Evento ocorrido. Tipo : ENTRY_DELETE. Contexto: arquivo1.txt
Eventos capturados. Quantidade: 1
Evento ocorrido. Tipo : ENTRY_CREATE. Contexto: arquivo1.txt
Isso é o que seria impresso no console caso o arquivo1.txt fosse apagado e depois criado
novamente.
d. Foi chamado o método take, que aguarda até haver eventos e assim, retorná-los
223
e. Foi chamado o método pollEvents para recuperar os eventos que ocorreram
Esse é o básico de um WatchService. Perceba que ele é um recurso que deve ser fechado, por isso
está na sintaxe de try-with-resources.
src/org/j6toj8/fileio/watchservice/WatchService_Modify.java
Saída no console
Path: /home/rinaldo/arquivos
Eventos capturados. Quantidade: 1
Evento ocorrido. Tipo : ENTRY_MODIFY. Contexto: .arquivo1.txt.kate-swp
Eventos capturados. Quantidade: 1
Evento ocorrido. Tipo : ENTRY_MODIFY. Contexto: arquivo1.txt.h26197
Eventos capturados. Quantidade: 1
Evento ocorrido. Tipo : ENTRY_MODIFY. Contexto: arquivo1.txt.h26197
224
Esses foram os eventos que ocorreram ao abrir o arquivo1.txt com o editor Kate, acrescentar
um caracter, e salvar o arquivo.
src/org/j6toj8/fileio/watchservice/WatchService_File.java
Saída no console
Path: /home/rinaldo/arquivos/arquivo1.txt
java.nio.file.NotDirectoryException: /home/rinaldo/arquivos/arquivo1.txt
at sun.nio.fs.LinuxWatchService$Poller.implRegister(LinuxWatchService.java:249)
at sun.nio.fs.AbstractPoller.processRequests(AbstractPoller.java:260)
at sun.nio.fs.LinuxWatchService$Poller.run(LinuxWatchService.java:364)
at java.lang.Thread.run(Thread.java:748)
225
src/org/j6toj8/fileio/watchservice/WatchService_Poll.java
Saída no console
Path: /home/rinaldo/arquivos
Horário antes do poll sem timeout: 14:55:10.298
WatchKey do poll: null
Horário depois do poll sem timeout: 14:55:10.298
Horário antes do poll com timeout: 14:55:10.298
WatchKey do poll com timeout: null
Horário depois do poll com timeout: 14:55:15.300
Perceba que o primeiro poll retorna imediatamente, mesmo que nenhum evento tenha
ocorrido. Já o segundo aguarda por 5 segundos para retornar, mesmo que não haja evento.
Nos cenários de monitoração, o ideal é utilizar o take, caso contrário seria necessário invocar o
poll inúmeras vezes, enquanto o take apenas aguarda indefinidamente até que haja um evento.
226
Referências
Boyarsky, Jeanne; Selikoff, Scott. OCP: Oracle Certified Professional Java SE 8 Programmer
II Study Guide (p. 625). Wiley. Edição do Kindle.
227
Java Collections
As seções de Expressões Lambda e Streams já possuem bastante conteúdo sobre os tópicos dos
objetivos deste capítulo, e as explicações em detalhes podem ser encontradas lá. Aqui serão
apresentados apenas exemplos adicionais especificamente em coleções (Collections), pois alguns
exemplos das outras seções utilizam outros tipos de Streams.
O Diamond Operator (ou Operador Diamante) foi criado no Java 7 para remover código
desnecessário ao declarar classes que usam Generics (ou tipos genéricos). Abaixo um exemplo que é
possível omitir o tipo de algumas classes utilizando o Diamond Operator.
src/org/j6toj8/collections/diamond/Collections_Diamond.java
228
Referências
Boyarsky, Jeanne; Selikoff, Scott. OCP: Oracle Certified Professional Java SE 8 Programmer
II Study Guide (p. 596). Wiley. Edição do Kindle.
Collections e lambda
Objetivo
Develop code that iterates a collection, filters a collection, and sorts a collection
by using lambda expressions
-
Desenvolver código que itera uma coleção, filtra uma coleção, e classifica em ordem
uma coleção utilizando expressões lambda
src/org/j6toj8/collections/lambda/CollectionsLambda_ForEach.java
Saída no console
1
6
7
2
9
src/org/j6toj8/collections/lambda/CollectionsLambda_Filter.java
229
Saída no console
6
7
9
src/org/j6toj8/collections/lambda/CollectionsLambda_Sort.java
Saída no console
1
2
6
7
9
src/org/j6toj8/collections/lambda/CollectionsLambda_Combined.java
List<Integer> list = Arrays.asList(1, 6, 7, 2, 9, 54, 13, 87, 23, 97, 34, 17, 34,
89, 35, 26);
list.stream()
.sorted()
.filter(n -> n > 10)
.filter(n -> n % 2 == 0)
.forEach(System.out::println);
Saída no console
26
34
34
54
230
Referências
• Using Streams
Boyarsky, Jeanne; Selikoff, Scott. OCP: Oracle Certified Professional Java SE 8 Programmer
II Study Guide (p. 185). Wiley. Edição do Kindle.
src/org/j6toj8/collections/datasearch/DataSearch_FindFirstAny.java
List<Integer> list = Arrays.asList(1, 6, 2, 9, 54, 13, 87, 23, 97, 34, 17, 34);
list.parallelStream()
.findFirst()
.ifPresent(n -> System.out.println("First: " + n));
list.parallelStream()
.findAny()
.ifPresent(n -> System.out.println("Any: " + n));
Saída no console
First: 1
Any: 9
231
src/org/j6toj8/collections/datasearch/DataSearch_Match.java
List<Integer> list = Arrays.asList(1, 6, 2, 9, 54, 13, 87, 23, 97, 34, 17, 34);
Saída no console
anyMatch: true
allMatch: false
noneMatch: false
Referências
• Using Streams
Boyarsky, Jeanne; Selikoff, Scott. OCP: Oracle Certified Professional Java SE 8 Programmer
II Study Guide (p. 185). Wiley. Edição do Kindle.
Perform calculations on Java Streams by using count, max, min, average, and sum
methods and save results to a collection by using the collect method and Collector
class, including the averagingDouble, groupingBy, joining, partitioningBy methods
-
Realizar cálculos em Streams usando os métodos count, max, min, average, e sum e
salvar resultados em uma coleção usando o método collect e a classe Collector,
incluindo os métodos averagingDouble, groupingBy, joining, partitioningBy
232
src/org/j6toj8/collections/calculations/Collections_MaxMinCount.java
Saída no console
Max: 9
Min: 1
Count: 9
src/org/j6toj8/collections/calculations/Collections_AveragingDouble.java
Saída no console
Média: 5.0
233
src/org/j6toj8/collections/calculations/Collections_GroupingBy.java
Saída no console
Mapa de resto da divisão por 3: {0=[3, 6, 9], 1=[1, 4, 7], 2=[2, 5, 8]}
src/org/j6toj8/collections/calculations/Collections_Joining.java
Saída no console
5. É possível separar os valores da coleção em um mapa com chaves true e false, de acordo com
uma função lambda.
src/org/j6toj8/collections/calculations/Collections_PartitioningBy.java
Saída no console
234
Referências
• Using Streams
Boyarsky, Jeanne; Selikoff, Scott. OCP: Oracle Certified Professional Java SE 8 Programmer
II Study Guide (p. 185). Wiley. Edição do Kindle.
O Java 8 trouxe vários métodos em Collections que utilizam como argumento uma função lambda,
facilitando algumas operações. Serão apresentados exemplos dos 4 métodos relacionados a esse
objetivo.
1. É possível remover um item de uma coleção condicionalmente com uma função lambda.
src/org/j6toj8/collections/improvements/Collections_RemoveIf.java
Saída no console
2. É possível modificar todos os elementos da coleção de acordo com uma operação lambda.
235
src/org/j6toj8/collections/improvements/Collections_ReplaceAll.java
Saída no console
3. É possível colocar valores em um Map a partir de uma função lambda, apenas se a chave não
estiver presente no Map.
src/org/j6toj8/collections/improvements/Collections_ComputeIfAbsent.java
Saída no console
4. É possível alterar valores em um Map a partir de uma função lambda, apenas se a chave estiver
presente no Map.
src/org/j6toj8/collections/improvements/Collections_ComputeIfPresent.java
236
Saída no console
Referências
• Using Streams
Boyarsky, Jeanne; Selikoff, Scott. OCP: Oracle Certified Professional Java SE 8 Programmer
II Study Guide (p. 185). Wiley. Edição do Kindle.
Maps e Streams
Objetivo
Develop code that uses the merge(), flatMap(), and map() methods on Java Streams
-
Desenvolver código que usa os métodos merge(), flatMap(), e map() em Streams Java.
Dos objetivos desta seção, apenas o método merge ainda não foi apresentado em outras seções.
1. É possível colocar um novo valor em um mapa, ou alterar o valor que já estava presente,
utilizando o método merge.
src/org/j6toj8/collections/mergemap/Collections_Merge.java
Saída no console
Perceba que, para as chaves que já estavam presentes no Map, foi aplicada a função lambda.
237
Para as chaves que ainda não estavam presentes, foi apenas inserida a String passada como
valor.
src/org/j6toj8/collections/mergemap/Collections_Map.java
list.stream()
.map(e -> e * 2)
.forEach(System.out::println);
Saída no console
2
4
6
8
10
12
14
16
18
3. É possível percorrer outro Stream, em sequência com o Stream atual, utilizando o método
flatMap.
src/org/j6toj8/collections/mergemap/Collections_FlatMap.java
238
Saída no console
Com map:
java.util.stream.ReferencePipeline$Head@e9e54c2
Com flatMap:
M
a
n
o
e
l
Perceba que uma transformação que resulta em outro Stream é percorrida como se fosse o
próprio Stream original.
Referências
• Additions in Java 8
Boyarsky, Jeanne; Selikoff, Scott. OCP: Oracle Certified Professional Java SE 8 Programmer
II Study Guide (p. 152). Wiley. Edição do Kindle.
• Using Streams
Boyarsky, Jeanne; Selikoff, Scott. OCP: Oracle Certified Professional Java SE 8 Programmer
II Study Guide (p. 185). Wiley. Edição do Kindle.
239
Assume the following
• Ausência das instruções de pacote e importação:
Se uma pergunta não indicar os nomes de arquivo ou os locais de diretório das classes, assuma
um dos seguintes, o que permitirá que o código seja compilado e executado:
◦ Cada classe está contida em um arquivo separado e todos os arquivos estão em um diretório
O código de exemplo pode ter quebras de linha não intencionais. Se você vir uma linha de
código que parece ter sido quebrada, e isso criar uma situação em que a quebra de linha é
significativa (por exemplo, um literal de String citado foi divido), suponha que a quebra de linha
seja uma extensão da mesma linha e não contém um retorno explícito (carriage return) que
causaria uma falha de compilação.
• Fragmentos de código:
Um fragmento de código é uma pequena seção do código-fonte que é apresentada sem seu
contexto. Suponha que todo o código de suporte necessário exista e que o ambiente ofereça
suporte completo à compilação e execução corretas do código mostrado e seu ambiente omitido.
• Comentários descritivos:
Considere comentários descritivos, como "setter e getters vão aqui", pelo valor nominal.
Suponha que o código correto exista, compile e execute com êxito para criar o efeito descrito.
240
Apêndice A: Dicas para ter sucesso na prova!
Além de validar conceitos importantes da linguagem Java a prova de certificação também exigirá
que você esteja atento a detalhes específicos nos códigos de cada questão. Sem o auxílio da IDE,
você será o compilador.
Veja alguns exemplos simples que se mostram óbvios mas podem te enganar na hora de marcar a
resposta certa.
Cenário 1
class Duke {
static int version = 6;
3. Não compila
Sem pensar muito a reposta seria a opção 1, certo? Errado. Olhando mais atentamente ao código é
possível notar que na primeira linha do método main está faltando um ; (ponto-e-vírgula). Esse
pequeno detalhe mostra que a opção certa é a 3.
Sempre que existir uma resposta falando código não compila, verifique duas vezes
as regras de compilação antes de testar o comportamento do código e sua possível
resposta.
• Ponto-e-vírgula
• Visibilidade
• Escopo de variáveis
• …
Resposta: 3
241
Cenário 2
class Duke {
static int version = 6;
Se você escolheu a opção 1, você errou… Esse exemplo tem outra pegadinha com o conceito de
shadowing. Usa-se o mesmo nome de variável mas com um escopo diferente. Inicialmente o tipo int
engana sua reposta mas esse código não compila ao tentar atribuir um valor int à uma variável do
tipo String[].
Resposta: 2
Cenário 3
class Duke {
1. Imprime 1
2. Imprime 0
3. Imprime false
4. Imprime true
5. Imprime null
6. Erro de execução
7. Não compila
242
A escolha mais comum seria a opção 3, onde confirma que o valor padrão de cada posição de um
array do tipo boolean é false. Esta opção estaria certa, se não fosse uma pegadinha. Este código na
verdade não compila. A opção certa seria a número 7. Isso porque a variável dukeClones é um
boolean simples tentando receber um array do tipo boolean.
• boolean → false
Resposta: 7
243
Apêndice B: Teste seu conhecimento!
Hora de colocar em prática tudo que foi visto neste livro. Existem algumas opções gratuitas e várias
opções pagas. A opção mais interessante para ambos os casos é a Whizlabs.
Todos os testes listados abaixo são em inglês, provavelmente o mesmo idioma que você fará seu
teste.
Gratuito
Link Qtd. Questões
Whizlabs 25
Oracle 8
Pagos
Link Qtd. Questões Preço
Pass-Guaranteed.com ? ?
244
Apêndice C: Referências
Em todas sessões do livro são feitas referências diretas as fontes de inspiração ou argumentação
base para a criação deste conteúdo. A seguir o resumo das principais e de outras referências que
também fizeram parte dessa jornada de aprendizado.
Para criação da estrutura do projeto deste livro foi utilizado como base o projeto ebook-with-
asciidoctor.
Boa parte das fontes estão em inglês (en-US), mas algumas também podem ser
encontradas em português (pt-BR).
• (en-US) Excelente livro que mostra o passo-a-passo para tirar a certificação completa do Java 8
ou atualizar para a versão 8, objetivo deste livro.
Boyarsky, Jeanne; Selikoff, Scott. OCP: Oracle Certified Professional Java SE 8 Programmer II
Study Guide: Exam 1Z0-809 (English Edition) 1st Edition. Wiley. Edição do Kindle.
• (en-US) Blog do Eugen (Baeldung) com dezenas de artigos focados em Java, entre outros
assuntos.
https://www.baeldung.com/category/java/
https://docs.oracle.com/javase/tutorial/java/
https://docs.oracle.com/javase/8/docs/
• (pt-BR) Curso da Alura, que apesar de focar no Java 7 (ao menos no momento da escrita desse
texto), mostra como não cair em pegadinhas na prova. Principalmente em relação à situações
que exigem pensar como um compilador e apontar a falta de um ;. São mais de 80 horas de
conteúdo. Vale muito a pena para quem quer ir a fundo de cada detalhe da linguagem (pelo
menos até a versão 7).
https://www.alura.com.br/formacao-certificacao-java
Material Complementar
• (pt-BR) Canal do YouTube RinaldoDev com vídeos explicativos de diversos conceitos da
linguagem Java.
• (en-US) Java Challengers, uma playlist no Youtube do canal do Rafael Del Nero que explica
desafios de Java para ensinar conceitos importantes da linguagem.
• (en-US) Oracle Dev Gym, desafios online e gratuitos para testar suas habilidades de Java.
245