Temp - Java Programmer - Modulo II (Online) PDF
Temp - Java Programmer - Modulo II (Online) PDF
Temp - Java Programmer - Modulo II (Online) PDF
Módulo II (online)
Cód.: TE 1726/0_EAD
Créditos
2
Todos os direitos autorais reservados. Este material de estudo (textos, imagens, áudios e vídeos) não pode
ser copiado, reproduzido, traduzido, baixado ou convertido em qualquer outra forma eletrônica, digital
ou impressa, ou legível por qualquer meio, em parte ou no todo, sem a aprovação prévia, por escrito, da
Impacta Participações e Empreendimentos Ltda., estando o contrafator sujeito a responder por crime de
Violação de Direito Autoral, conforme o art.184 do Código Penal Brasileiro, além de responder por Perdas
e Danos. Todos os logotipos e marcas utilizados neste material pertencem às suas respectivas empresas.
“As marcas registradas e os nomes comerciais citados nesta obra, mesmo que não sejam assim identificados,
pertencem aos seus respectivos proprietários nos termos das leis, convenções e diretrizes nacionais e internacionais.”
Coordenação Geral
Marcia M. Rosa Roteirização
Sandro Luiz de Souza Vieira
Coordenação Editorial
Henrique Thomaz Bruscagin Aula ministrada por
Sandro Luiz de Souza Vieira
Supervisão de Desenvolvimento Digital
Alexandre Hideki Chicaoka Edição e Revisão final
Cristiana Hoffmann Pavan
Produção, Gravação, Edição de Vídeo e Finalização
Bruno Michel Vasconcellos de Andrade Diagramação
(Impacta Produtora) Bruno de Oliveira Santos
Luiz Felipe da Silva Porto (Impacta Produtora)
Xandros Luiz de Oliveira Almeida (Impacta Produtora)
Edição nº 1 | 1726/0_EAD
junho/2015
Este material é uma nova obra derivada da seguinte obra original, produzida por
TechnoEdition Editora Ltda., em Set/2014: Java Programmer
Autoria: Sandro Luiz de Souza Vieira
Apresentação....................................................................................................................................................... 06
1. Arrays................................................................................................................................................... 07
1.1. Introdução......................................................................................................................08
1.2. Tipos de array.................................................................................................................08
1.2.1. Array unidimensional......................................................................................................08
1.2.2. Array bidimensional........................................................................................................10
1.2.3. Array multidimensional...................................................................................................10
1.3. Acessando elementos de um array..................................................................................11
1.3.1. Acesso aos elementos em um loop.................................................................................12
1.4. Modos de inicializar e construir um array........................................................................13
1.4.1. Por meio de uma única instrução....................................................................................13
1.4.2. Por meio de um array anônimo.......................................................................................17
1.5. Passando um array como parâmetro...............................................................................18
1.6. Atribuindo valor a um elemento do array........................................................................19
1.6.1. Variáveis de referência para arrays unidimensionais........................................................22
1.6.2. Variáveis de referência para arrays multidimensionais.....................................................23
1.7. Array de argumentos......................................................................................................24
Teste seus conhecimentos............................................................................................................................ 29
3. Exceções.............................................................................................................................................. 73
3.1. Introdução......................................................................................................................74
3.2. Bloco try/catch................................................................................................................74
3.2.1. Manipulando mais de um tipo de exceção.......................................................................77
3.3. throws............................................................................................................................77
3.4. finally.............................................................................................................................78
3.5. try-with-resource.............................................................................................................79
3.5.1. Interfaces AutoCloseable e Closeable..............................................................................79
3.5.2. Exceções suprimidas.......................................................................................................80
3.6. Exceções e a pilha de métodos em Java...........................................................................80
3.7. Hierarquia de exceções...................................................................................................82
3.7.1. Exceções verificadas.......................................................................................................82
3.7.2. Exceções não verificadas.................................................................................................83
3.8. Principais exceções.........................................................................................................83
3.8.1. Throwable.......................................................................................................................83
3.8.1.1. Exceções encadeadas......................................................................................................85
3.8.1.2. Principais construtores....................................................................................................86
3.8.1.3. Principais métodos..........................................................................................................87
3.8.2. Error...............................................................................................................................89
3.8.3. Exception........................................................................................................................89
3.8.4. NullPointerException.......................................................................................................90
3.8.5. NumberFormatException.................................................................................................90
3.8.6. ArrayIndexOutOfBoundsException..................................................................................91
3.8.7. ArithmeticException........................................................................................................91
3.8.8. ClassCastException.........................................................................................................92
3.8.9. IOException....................................................................................................................92
3.8.10. Classe SQLException.......................................................................................................93
3.9. Exceções personalizadas.................................................................................................94
Teste seus conhecimentos............................................................................................................................ 97
Apêndice................................................................................................................................................................ 155
1.1. Introdução......................................................................................................................156
1.2. Equivalência de variáveis.................................................................................................156
1.2.1. Variáveis primitivas.........................................................................................................156
1.2.2. Variáveis de referência....................................................................................................157
1.3. Equivalência de objetos...................................................................................................157
1.3.1. As regras de equals()......................................................................................................159
1.4. Hashing..........................................................................................................................160
1.4.1. As regras de hashCode().................................................................................................161
Apresentação
6
Bem-vindo!
É um prazer tê-lo como aluno do nosso curso online de Java Programmer – Módulo II. Este é o
curso ideal para você que já domina os recursos essenciais da linguagem Java e deseja aprimorar
o seu conhecimento.
No decorrer das aulas, você estudará diversos recursos muito utilizados na linguagem Java.
Conhecerá arrays, exceções e características da programação funcional. Por fim, verá como criar
estruturas de dados e manipular a API de conjuntos e coleções do Java, que é muito útil quando
precisamos lidar com um grande número de elementos.
Para ter um bom aproveitamento deste curso, é imprescindível que você tenha participado
do primeiro módulo do curso Java Programmer, ou possua conhecimentos equivalentes. Bom
aprendizado!
Como estudar?
Material de Apoio!
Videoaulas sobre os assuntos que você precisa saber no segundo módulo de Java
Programmer.
Parte teórica, com mais exemplos e detalhes para você que quer se aprofundar no
assunto da videoaula.
1.1. Introdução
Os arrays são variáveis indexadas. Isto quer dizer que você pode definir uma variável cujos
elementos são referenciados por um índice. Assim, os arrays são definidos como objetos em
cujo conteúdo estão diversos valores de um mesmo tipo. Os tipos podem ser básicos ou tipos
construídos.
O índice é um valor inteiro responsável por determinar uma posição dentro de um array.
Podemos dizer, então, que elementos de um mesmo tipo são agrupados em arrays. Os arrays
podem armazenar variáveis de tipos primitivos ou de referência.
1.2. Tipos de array
Um array pode ter uma ou mais dimensões, podendo ser classificado nos tipos descritos
adiante.
1.2.1. Array unidimensional
A construção de arrays unidimensionais, também chamados de vetores, requer a utilização da
palavra-chave new, a definição do tipo do array e seu tamanho, que é um número inteiro que
deve estar entre colchetes.
Assim, em Java, os arrays são instâncias de objetos, os quais serão definidos de acordo com
o tipo de dado que será manipulado.
Em que:
O exemplo descrito a seguir mostra como construir um único objeto de array do tipo Ponto:
Arrays
9
Este código insere um único objeto na pilha, que é o array atribuído à variável de referência
pontos. Embora esse objeto contenha oito variáveis de referência pontos, não há qualquer
objeto pontos que tenha sido criado ou, ainda, atribuído a tais variáveis. É de extrema
importância que você saiba identificar a quantidade de objetos que serão adicionados à pilha
assim que o código for executado ou a instrução for executada.
Ao criar um array, é necessário atribuir um tamanho a ele, uma vez que a JVM precisa ter essa
informação a fim de alocar o espaço adequado. Sendo assim, o exemplo descrito a seguir é
inválido, uma vez que o tamanho não é especificado:
1.2.2. Array bidimensional
Um array bidimensional é aquele com dois índices e que possibilita o armazenamento de
valores na forma de matrizes. Apesar de a linguagem Java não suportar arrays bidimensionais,
é possível, entretanto, criar um array de arrays, o qual deve ser declarado da seguinte maneira:
Para realizar o cadastro das notas finais de 100 alunos em uma determinada matéria,
considerando que essa matéria teve três notas distintas, o código deve ser escrito da seguinte
maneira:
1.2.3. Array multidimensional
Os arrays bidimensionais são mais comuns, mas você também pode criar arrays de várias
dimensões. Para que uma variável array multidimensional seja declarada, basta usar um novo
par de colchetes para cada índice adicional, conforme a sintaxe a seguir:
Para realizar o cadastro de uma sequência de notas finais de 100 alunos em 4 matérias,
considerando que cada aluno tem 3 notas distintas para cada matéria, o código deve ser
escrito da seguinte maneira:
A referência a um vetor pode ser feita tanto para incluir valores quanto para obter valores e
utilizá-los em outra operação. Destacamos que os índices de um array são sempre iniciados
em zero.
É importante que você tenha em mente como inicializar um array, ou seja, como adicionar
elementos a ele, que podem ser valores primitivos ou objetos.
Um array de objetos não armazena propriamente objetos; o que ele armazena de fato são
referências a esses objetos. Sendo assim, um array de três strings, na verdade, é um array de
três referências a objetos String. Você deve saber se essas referências são nulas, ou seja, se
não têm um objeto atribuído, ou se estão referenciando de fato objetos String.
Java Programmer – Módulo II (online)
12
Ao utilizar uma referência nula para chamar um método, ocorre a exceção NullPointerException.
Observe o exemplo a seguir, no qual é criado um array com três frutas. Nele, temos um objeto
de array na pilha e três referências do tipo Fruta que são nulas:
A seguir, criaremos alguns objetos do tipo Fruta, atribuindo os mesmos valores que determinam
a posição de índice no array, o qual é referenciado por alimento. Veja:
Preste atenção em qualquer trecho de código que tente acessar um índice que não está dentro
do intervalo de um array. A tentativa de acessar o índice 6 de um array de seis elementos é
um exemplo típico. Isso não é possível, uma vez que o índice de um array inicia-se sempre em
zero, ou seja, neste caso, o intervalo é de 0 a 5. Há circunstâncias em que é possível encontrar
a tentativa de acesso a um índice por meio de um número negativo. Em ambas as situações,
será lançada a exceção ArrayIndexOutBoundsException. Observe o exemplo a seguir:
Por meio de uma instrução como a descrita a seguir, é possível declarar, criar e inicializar um
array. Somente a sexta linha dessa instrução realiza quatro tarefas distintas:
•• Declaração de uma variável de referência do tipo array, de inteiros, a qual possui o nome
codigos;
•• Criação do objeto array que é atribuído a codigos e atribuição dos nove valores 0, 1, 1,
2, 3, 5, 8, 13 e 21 a esse array;
Este código é muito mais longo do que o descrito em somente duas linhas, mas diversos
desenvolvedores o utilizam por certas razões, como:
Depois de compilado e executado qualquer um dos códigos anteriores, o resultado será como
o exibido na imagem a seguir:
Java Programmer – Módulo II (online)
16
•• A criação do array de nome novosUsuarios, que conterá referências para objetos do tipo
Usuario. Seu tamanho corresponde a três elementos;
•• A criação de dois novos objetos Usuario com os atributos nome atribuídos respectivamente
via construtor: Ernesto e Alfredo;
•• As duas instâncias que acabam de ser criadas são atribuídas aos dois últimos elementos
da variável de referência do array novosUsuarios.
•• Construção do array que conterá elementos do tipo int. Ele contém outros objetos do tipo
array e é o objeto que será atribuído à variável de referência elementos;
Arrays
17
•• O array elementos possui três elementos, os quais são representados pelos itens que
estão entre as chaves externas, sendo que cada um deles é um array int cujo tamanho é
determinado pela quantidade de itens presentes nas chaves internas, da seguinte forma:
•• No total, até aqui, são quatro objetos: um array, no qual estão contidos arrays int, e outros
três arrays int, sendo que cada um de seus elementos refere-se a um valor do tipo int;
•• Cada um dos itens que estão entre as chaves externas representa uma variável de
referência a um array que conterá elementos do tipo int. Isto significa que os três arrays
int construídos são atribuídos aos elementos do array elementos;
•• Com os valores int presentes em suas chaves internas, os arrays foram inicializados.
Este código permite criar um array int no qual estão contidos três elementos, bem como utiliza
os valores 17, 38 e 10 para inicializar esses elementos. Além disso, atribui um novo array à
variável de referência elementos.
Java Programmer – Módulo II (online)
18
Os procedimentos que você viu até aqui referem-se à criação de um array anônimo. Você verá,
no exemplo a seguir, que os arrays que não são atribuídos a uma variável de referência podem
ser utilizados na criação de um array just-in-time:
Um array just-in-time pode ser utilizado como argumento de um método, o qual, por sua vez,
utiliza como parâmetro um array. Vale destacar que, quando você cria um array anônimo, o
tamanho não deve ser determinado.
Perceba que, no exemplo, o array int é capaz de armazenar qualquer valor possível para uma
variável int de 32 bits.
Se você tiver uma classe como tipo declarado para o array, é possível inserir, neste array, os
objetos de uma das subclasses desse tipo declarado. Veja os exemplos a seguir:
Nestes exemplos, o array novosCursos possui variáveis de referência Cursos como seus
elementos, ou seja, quaisquer itens cuja atribuição à variável de referência Cursos é válida
também poderão ser atribuídos a um elemento pertencente ao array novosCursos.
Arrays
21
Você também pode usar interfaces como tipo de array. Assim, os arrays declarados como um
tipo de interface possuem elementos capazes de referenciar qualquer instância das classes
responsáveis por implementar as interfaces declaradas.
Veja, no código desenvolvido a seguir, uma interface utilizada como tipo de array:
Java Programmer – Módulo II (online)
22
Como era de se esperar, no código anterior, o compilador aceita todos os objetos que
implementam a interface Produto como tipo válido. A classe CategoriaProduto, por não
implementar a interface, não é tipo válido para o array e o compilador acusa o erro.
Para compreender o que ocorre com atribuições de referência de arrays de tipos primitivos,
a regra é simples: somente é possível reatribuir arrays de um determinado tipo para outros
arrays do mesmo tipo.
Os arrays que referenciam objetos não apresentam muitas restrições, conforme você pode
observar no código descrito a seguir:
Arrays
23
Neste caso, você deve ter percebido que é possível atribuir um array cujo tipo é
DesenvolvimentoWeb a uma variável de referência do array de tipo Cursos, uma vez que
DesenvolvimentoWeb é um tipo de Cursos. Porém, tendo em vista que Livro não é tipo de
Cursos, a atribuição cursos = livros é inválida.
As regras de atribuição de arrays vistas até aqui se aplicam tanto às interfaces quanto às
classes. Assim, todo array declarado como um tipo de interface pode referenciar um array
pelo qual essa interface é implementada, independentemente de seu tipo.
Os objetos de uma determinada classe que implementa uma interface sempre serão
considerados membros dessa interface.
Lembre-se que as atribuições válidas não podem ser invertidas, isto significa que não é possível
que o array DesenvolvimentoWeb seja atribuído ao array Curso, uma vez que nem todos os
cursos são DesenvolvimentoWeb.
Como você já sabe, é possível atribuir um array a uma variável de referência de array já
declarada. Porém, para isso, é preciso que tanto o array como a variável de referência estejam
na mesma dimensão. Por conta disso, o código descrito a seguir é inválido:
Java Programmer – Módulo II (online)
24
Como você observou neste último código, em que o array int é um array simples, não é
possível atribuir um array bidimensional de arrays int a uma variável de referência de um array
int que não seja bidimensional.
1.7. Array de argumentos
A Java Virtual Machine invoca o método principal, ou main, o qual deve empregar o parâmetro
do array de String. Este último é responsável por alocar os argumentos que são enviados
juntamente ao comando cuja função é executar o programa.
Este erro ocorre porque o array args, recebido pelo método main de seu programa, está vazio
e, ao tentar acessar uma de suas posições, ocorre o erro de índice fora dos limites do array.
Esse array tem o intuito de receber argumentos de linha de comando, passados pelo executor
do programa, após o comando:
Veja como simular esse comportamento no Eclipse, de forma a passar argumentos no momento
da execução do programa:
Arrays
25
4. Após inserir os argumentos, clique no botão Run. O resultado será exibido no console:
O parâmetro do array de String não deve ser necessariamente nomeado como arg ou args,
ele pode receber qualquer outro nome. Outro fato que deve ser considerado é que o array
args é um simples array que foi passado para main pela Java Virtual Machine.
Arrays
27
Este código ilustra uma situação em que o comando else é executado e ocorre o encerramento
do programa pela Virtual Machine, caso haja tentativa de acesso a algo que ultrapasse length-1.
1
Teste seus conhecimentos
Arrays
Java Programmer – Módulo II (online)
30
☐☐ a) Unidimensional
☐☐ b) Bidimensional
☐☐ c) Adimensional
☐☐ d) Multidimensional
☐☐ a) array
☐☐ b) create
☐☐ c) malloc
☐☐ d) type
☐☐ e) new
Arrays
31
☐☐ a) size
☐☐ b) length
☐☐ c) type
☐☐ d) index
2.1. Introdução
Como o pacote java.lang é responsável por definir as classes mais importantes, as quais são
importadas de forma automática, ele é de suma importância para a linguagem de programação
Java. Nesta leitura complementar, abordaremos os conceitos relacionados a três grupos de
classes distintos, encontrados nesse pacote: String, Math e Wrapper. Além disso, abordaremos
a manipulação de data e hora através das classes LocalDateTime e DateTimeFormatter do
pacote java.time.
2.2. Classe String
Você deve conhecer algumas informações importantes a respeito da classe String. Grande
parte das linguagens de programação utilizadas tem a manipulação de strings de caracteres
como um de seus aspectos essenciais.
Em Java, strings são objetos. Assim, com a palavra-chave new, é possível criar a instância de
um string, como ocorre com quaisquer outros objetos, da seguinte maneira:
Por meio da linha de código descrita a seguir, um novo objeto da classe String será criado,
bem como será atribuída a ele uma variável de referência, que, neste caso, é nome. Observe:
nome = “João”.
Contudo, como a classe String possui diversos construtores, você pode utilizar outro tipo de
construção para criar um string, da seguinte forma:
Dentre as duas formas usadas, dê preferência sempre à segunda forma, pois é a mais adequada,
rápida, e indicada pela especificação da linguagem Java.
Classes utilitárias
35
Além dessas tarefas, você também pode criar uma segunda referência ao mesmo objeto e
fazer com que a variável de referência nome aponte para essa referência, da seguinte forma:
2.2.1. Inalterabilidade
Uma das características do objeto String é a inalterabilidade. Uma vez atribuído um valor a
ele, este não mais poderá sofrer alterações. Embora não seja possível alterar o objeto String,
é possível alterar sua variável de referência sem problemas. Veja o exemplo a seguir:
Neste exemplo, o valor do string nome, que é “João”, teve “ cliente especial” adicionado ao
seu final, ou seja, o valor resultante é “João cliente especial”. Como não é possível alterar um
string, a JVM não é capaz de inserir um novo String àquele referenciado por nome. Sendo assim,
um novo objeto String, cujo valor é “João cliente especial”, foi criado e, então, referenciado
por nome.
Perceba que o exemplo tem três objetos String: o primeiro com valor “João”; o segundo
com valor “João cliente especial” e o terceiro, que, na verdade, é um argumento literal
concatenado, também é um objeto String (“ cliente especial”), embora apenas “João” e “João
cliente especial” sejam referenciados por nome2 e por nome, respectivamente.
Java Programmer – Módulo II (online)
36
Se antes do chamado de nome = nome.concat(“ cliente especial”) não fosse criada uma
segunda variável de referência para “João”, esta seria considerada perdida, uma vez que não
haveria um código do programa que fosse capaz de referenciá-la. Lembre-se que, por ser
inalterável, o String original “João” não foi modificado, as alterações foram feitas somente na
variável de referência nome a fim de que pudesse referenciar outro string. O exemplo a seguir
demonstra claramente os detalhes expostos:
•• Dentro do main, na primeira linha, é criado um objeto String, cujo valor é “Aluno” e que
está sendo referenciado por a;
•• Na segunda linha, outro objeto String é criado pela Java Virtual Machine. O valor desse
objeto é “Aluno Módulo Avançado”, porém, nada o referencia. Sendo assim, esse segundo
objeto é perdido de forma imediata;
•• Então, a continua a referenciar o primeiro objeto String criado, cujo valor é Aluno.
Classes utilitárias
37
Nesse último exemplo, inicialmente, foi criado um novo objeto String cujo valor é “ALUNO”,
porém, em seguida, ele foi perdido. Apesar disso, o string “Aluno” original continua sem
alterações, bem como a ainda o referencia. Na sequência, um novo objeto String, cujo valor é
“eluno”, foi criado pela VM. Esse objeto, contudo, também é perdido. Dessa forma, o objeto
String original, cujo valor é “Aluno”, permanece sem alterações, e a continua referenciando-o.
Embora diversos métodos tenham sido chamados para criar um novo string que altere o
existente, este não foi alterado em razão de uma variável de referência não ser atribuída ao
novo string.
Neste exemplo, as variáveis de referência são m1 e m2, resultando em “m1 = passa para fim”
e “m2 = passa para começo”. Os objetos String criados foram:
•• “passa para”;
No total, foram criados oito objetos String, mas seis foram perdidos, restando apenas dois:
“passa para fim” e “passa para começo”.
Assim que encontra um string literal, o compilador verifica o pool constante de strings, com a
finalidade de identificar se há um string literal igual ao encontrado. Caso haja essa coincidência,
não será criado qualquer novo objeto String, e uma referência será direcionada ao novo valor
literal para o string já existente.
Para assegurar sua inalterabilidade, uma classe String é marcada como final e, assim, os
métodos do objeto String não são modificados de forma alguma.
Java Programmer – Módulo II (online)
40
2.2.2. Métodos
A classe String possui diversos métodos. Veja, a seguir, a descrição de alguns dos principais:
•• charAt(Numero_do_Indice)
Este método retorna o caractere que se encontra no índice determinado da classe String, que
é iniciado em zero.
•• concat(Texto_A_Ser_Concatenado)
A função deste método é retornar um novo objeto String, composto pelo valor do string que
foi passado para ele, acrescido ao final do string que foi utilizado a fim de chamar esse objeto.
Após a compilação e execução do código anterior, o resultado será como ilustrado na imagem
a seguir:
Observe, nesse exemplo, que o valor atribuído a p foi alterado de fato. Em razão de += ser um
operador de atribuição, na linha de código p += “ Jurídica”; um novo objeto String é criado e
atribuído à variável p. Após a execução dessa linha, o string que a variável p estava referenciando
originalmente é abandonado, neste caso “Pessoa”, resultando agora em “Pessoa Jurídica”.
•• equalsIgnoreCase(Argumento)
Este método retorna um valor booleano baseado em uma comparação entre o valor referente
ao string do argumento e o valor utilizado com a finalidade de chamar o método.
Em razão de os valores booleanos serem true ou false, o método retornará true se houver
coincidência entre os valores comparados, independente do uso de caixa alta ou baixa em
qualquer ponto dos strings comparados.
•• length()
Este método tem como função retornar o tamanho apresentado pelo string utilizado com a
finalidade de chamar o método, como no exemplo a seguir:
Lembre-se que há um atributo dos arrays que também é chamado de length. Assim, a utilização
do método length() em um array ou, ainda, do atributo length em um objeto String, gera
erros de compilação.
•• replace(Caractere_Antigo, Caractere_Novo)
Este método retorna um novo objeto String que apresenta como valor a String utilizada com
a finalidade de chamar o método e modificada pelas substituições decorrentes.
Ao atualizar esse valor, as ocorrências do tipo char referentes ao primeiro argumento são
substituídas por ocorrências do tipo char referentes ao segundo argumento.
•• substring(Posição_Inicial, Posição_Final)
A função deste método é retornar uma parte do objeto String, o qual foi utilizado na chamada do
método. Essa parte do objeto String também é denominada substring. O primeiro argumento
(Posição_Inicial) diz respeito ao local em que o substring inicia e começa em zero.
Assim, caso este argumento seja 5, o último caractere do string retornado será da posição 5 do
string original, e isso corresponde a 4. Ou seja, como regra geral, o método substring inclui
o caractere apontado pelo delimitador inicial e exclui o caractere apontado pelo delimitador
final.
Após a compilação e execução do código anterior, o resultado será como o indicado na imagem
a seguir:
Java Programmer – Módulo II (online)
44
•• toLowerCase()
A função deste método é retornar um novo objeto String, cujo valor refere-se ao string que
foi utilizado para chamar o método, em caixa baixa. Ao utilizar toLowerCase(), o resultado
obtido terá todos seus caracteres convertidos para caixa baixa. Veja:
•• toUpperCase()
A função deste método é retornar o objeto String que contém o valor do string utilizado para
chamar o método com todos os caracteres convertidos em caixa alta. Observe:
•• trim()
Assim como os outros métodos, trim() também retorna um novo objeto String que apresenta
como valor o string utilizado sem espaços em branco no seu início e fim, caso existam. Veja
o exemplo:
Após a compilação e execução do código anterior, o exemplo será como o mostrado na imagem
a seguir:
•• toString()
A função deste método é retornar o valor referente ao string utilizado com a finalidade de
chamar o método. Lembre-se que todos os objetos da linguagem possuem uma versão deste
método, herdado da classe Object, o qual retorna um string capaz de descrever os objetos.
Observe a seguir:
Por serem adequados para a manipulação de blocos de dados, a utilização mais comum dos
objetos StringBuilder e StringBuffer é na entrada e saída de arquivos, nas situações em que
o programa manipula diversas alterações e há um grande fluxo de entrada de dados.
Além de serem a melhor opção para a manipulação de dados, estes objetos também são capazes
de passá-los adiante após manipulá-los para que, assim, o espaço na memória destinado a
essa manipulação possa ser reutilizado.
A classe StringBuffer possui determinada sequência de caracteres, que pode ser alterada por
meio de chamadas de métodos determinados. Seus métodos são sincronizados, o que torna
possível que todas as operações comportem-se como se ocorressem em série, de acordo com
a ordem das chamadas de método feitas por cada um dos threads individuais envolvidos.
•• append (“Texto_a_acrescentar”)
Este método é responsável por atualizar o valor do mesmo objeto StringBuilder que o chamou,
bem como tem a função de retornar o objeto StringBuilder. Em ambos os casos, no objeto
StringBuilder original, será inserido o objeto String passado para o segundo argumento,
sendo que o início será no primeiro argumento, que começa em zero. Observe:
Embora o argumento String seja visto com mais frequência, este método
também permite que diversos tipos de dados sejam passados pelo segundo
argumento, como boolean, char, int, long, entre outros.
Java Programmer – Módulo II (online)
48
•• reverse()
Este método é responsável por atualizar o valor do mesmo objeto StringBuilder que o chamou,
bem como retorna o objeto StringBuilder. Em ambos os casos, há uma inversão dos caracteres
do objeto StringBuilder, como descrito no exemplo a seguir:
•• toString()
A função deste método é retornar o valor do objeto StringBuilder que chamou o método. Esse
valor é retornado como um objeto String.
2.2.4. Métodos encadeados
Neste tópico, apresentamos o conceito de métodos encadeados. Veja, a seguir, a sintaxe de
uma instrução que apresenta métodos encadeados:
resultado = método1().método2().método3();
Para compreender esses métodos encadeados, em primeiro lugar, você deve verificar o que
será retornado da chamada do método que se encontra na extrema esquerda. Esse valor
retornado será chamado de A.
•• O valor literal “teste” foi concatenado (concat) a “final”. A partir de então, foi criado um
objeto String cujo valor é “teste final”. Este objeto é intermediário e temporário, ou seja,
será perdido em breve;
•• Em seguida, um novo objeto String de caráter temporário foi criado pelo método
toUpperCase(), cujo valor é “TESTE FINAL”;
•• Então, um objeto String final, de valor “TxSTx FINAL”, foi criado pelo método replace().
Java Programmer – Módulo II (online)
50
Este exemplo retornará FÍSICA JURÍDICA porque String a recebeu o texto “Pessoa Física”.
Perceba que String b recebeu o encadeamento dos métodos concat, toUpperCase e substring.
Assim, o método a.concat acrescentou o texto “Jurídica”, o método toUpperCase transformou
o texto em letras maiúsculas e o método substring retornou uma parte do conteúdo da String
b a partir da 7ª posição.
1. Crie uma classe com o nome ExemploString2 e insira a estrutura básica de um programa
Java;
3. Inicialize a variável nomeCompleto com o seu nome completo e as outras 2 variáveis com
um string vazio;
4. Faça um for que percorra cada caractere da variável nomeCompleto e, dentro dele, verifique
se o caractere da posição atual é igual ao caractere vazio (‘ ‘). Se sim, atribua o substring de 0
até a posição atual à nome e da posição atual até o final à sobrenome, depois insira um break
para parar o loop for;
5. Imprima as variáveis nome e sobrenome na tela sem espaços no começo e fim dos strings;
2.3. Classe Math
Você deve usar a classe Math com a finalidade de efetuar operações matemáticas comuns.
Uma vez que todos os métodos da classe Math são definidos como static, eles não precisam
ser instanciados para serem utilizados.
•• Em virtude de seu construtor ser privado, não é possível criar uma instância da classe
Math, afinal, ela é uma classe criada com cunho utilitário;
•• Essa classe também não pode ser estendida por ser definida como final.
Além disso, a classe Math é responsável por determinar aproximações para as constantes
pi e e, cujas assinaturas são public final static double Math.PI e public final static double
Math.E, respectivamente.
2.3.1. Métodos
Em razão de serem estáticos, os métodos da classe Math podem ser acessados a partir do
nome da classe. Normalmente, esses métodos são chamados da seguinte maneira:
resultado = Math.umMétodoMathEstático();
A seguir, descrevemos esses métodos Math seguidos pelos exemplos de sua utilização:
Java Programmer – Módulo II (online)
52
•• abs()
Veja um exemplo:
O retorno será 1.
•• max()
Este método retorna o maior entre dois números distintos. Possui quatro assinaturas:
Veja um exemplo:
•• min()
Oposto ao max(), retorna o menor entre dois números distintos. Também possui quatro
assinaturas:
Veja um exemplo:
•• random()
Sem utilizar quaisquer parâmetros, retorna um valor entre 0.0 e 1.0, do tipo double. A assinatura
deste método é public static double random ( ).
Veja um exemplo:
•• ceil()
Veja um exemplo:
O retorno será 4.
Java Programmer – Módulo II (online)
54
•• floor()
Diferente do método ceil(), retorna um número inteiro correspondente ao inteiro inferior mais
próximo ao argumento, também em formato double. A assinatura deste método é public
static double floor (double a).
Veja um exemplo:
O retorno será 3.
•• round()
Este método retorna um número inteiro que esteja mais próximo ao número utilizado no
argumento. Possui duas assinaturas:
Veja um exemplo:
•• sin()
Este método retorna o seno de um ângulo, sendo que o argumento é um double que refere-
se ao ângulo em radianos. Por meio de Math.toRadians(), é possível converter graus em
radianos. A assinatura deste método é public static double sin (double a).
•• cos()
Veja um exemplo:
O retorno será 1.
•• tan()
Veja um exemplo:
•• sqrt()
Retorna a raiz quadrada de um valor double. A assinatura deste método é public static double
sqrt (double a).
Veja um exemplo:
O retorno será 3.
•• toDegrees()
Este método retorna um ângulo em graus a partir do argumento, o qual representa o ângulo
em radianos. A assinatura deste método é public static double toDegrees (double a).
Veja um exemplo:
•• toRadians()
Veja um exemplo:
1. Crie uma classe com o nome ExemploMath e insira a estrutura básica de um programa Java;
2. Declare um array do tipo int com o nome numeros de tamanho 10 e uma variável do tipo
int com o nome maior e a inicialize com 0;
3. Faça um loop for que percorra todo o array numeros. Dentro do loop declare uma variável
com o nome n do tipo Double e atribua a ele um número randômico entre 1 e 100;
5. Utilizando o método max() da classe Math, verifique se o número da posição atual do array
números é maior que a variável maior e atribua o valor retornado pelo método à variável
maior;
Classes utilitárias
57
2.4. Classes Wrapper
As classes Wrapper da linguagem Java são responsáveis por fornecer:
Os valores agrupados podem ser adicionados em tarefas que são reservadas a objetos.
Além disso, a maioria dos métodos existentes nessas classes é utilitária aos tipos primitivos
correspondentes e tem relação com diversos tipos de conversões, como a conversão de objetos
String em tipos primitivos e de tipos primitivos em objetos String, entre outras.
Cada um dos tipos primitivos contém uma classe Wrapper, cujo nome corresponde ao nome
do tipo, porém, escrito em caixa alta conforme a convenção de nomes da linguagem Java.
Dessa forma, o nome da classe Wrapper para o tipo float é Float, por exemplo.
Porém, nem todos os nomes de classes seguem as mesmas regras: o nome da classe Wrapper
para o tipo char é Character, e para o tipo int é Integer.
Quanto aos objetos Wrapper, algumas abordagens para a sua criação utilizam o string de um
tipo primitivo como argumento. Caso não seja possível converter o String utilizado no tipo
primitivo adequado, essas abordagens lançam exceções NumberFormatException.
A inalterabilidade também é uma característica dos objetos Wrapper, tendo em vista que os
valores recebidos por eles não podem ser alterados.
Java Programmer – Módulo II (online)
58
A seguir, apresentamos uma relação dos tipos primitivos e dos argumentos do construtor de
cada classe Wrapper:
Classe Descrição
Possui o tipo primitivo boolean, e seu construtor tem
Boolean
os argumentos booleano ou String.
Possui o tipo primitivo byte, e seu construtor tem os
Byte
argumentos byte ou String.
Possui o tipo primitivo char, e seu construtor tem o
Character
argumento char.
Possui o tipo primitivo double, e seu construtor tem
Double
os argumentos double ou String.
Possui o tipo primitivo float, e seu construtor tem os
Float
argumentos float ou String.
Possui o tipo primitivo int, e seu construtor tem os
Integer
argumentos int ou String.
Possui o tipo primitivo long, e seu construtor tem os
Long
argumentos long ou String.
Possui o tipo primitivo short, e seu construtor tem os
Short
argumentos short ou String.
2.4.1. Construtores Wrapper
Com exceção da classe Character, que fornece somente um construtor, cada uma das classes
Wrapper apresenta dois construtores Wrapper, que são:
A classe Wrapper Character fornece um construtor que utiliza como argumento um tipo char.
Veja na linha Character c1 = new Character (‘A’); do exemplo a seguir:
2.4.2. Métodos de conversão
As classes Wrapper possuem métodos de conversão. A seguir, apresentamos alguns dos
métodos mais utilizados.
2.4.2.1. value()
Estes métodos não contêm argumentos e são utilizados quando o valor de um objeto Wrapper
numérico deve ser convertido em um tipo primitivo.
Java Programmer – Módulo II (online)
60
Cada uma das seis classes Wrapper numéricas contém seis métodos, o que torna possível
converter um objeto primitivo Wrapper numérico em um tipo primitivo. Observe o exemplo
descrito a seguir:
2.4.2.2. valueOf()
Estes métodos são estáticos e fornecidos por grande parte das classes Wrapper. Além disso,
apresentam uma abordagem capaz de permitir a criação de objetos Wrapper.
Os métodos valueOf() representam o tipo adequado de seu primeiro argumento por meio
de um objeto String. Se houver um segundo método, este utiliza um argumento de caráter
opcional, cuja função é determinar em que base foi representado o primeiro argumento. Essa
base pode ser binária, octal ou hexadecimal.
Após a compilação e execução do código anterior, o resultado será como o ilustrado na imagem
a seguir:
2.4.2.3. parse()
O método valueOf(), descrito anteriormente, se relaciona aos seis métodos parse(), os quais
correspondem a cada tipo Wrapper numérico. Isso ocorre porque eles apresentam algumas
semelhanças entre si, como:
•• Eles realizam a conversão de objetos String, os quais apresentam bases distintas nas
situações em que um tipo primitivo oculto é um dos quatro tipos inteiros (byte, integer,
long e short).
•• O método valueOf() retorna um objeto Wrapper a partir do tipo que realizou a sua
chamada, sendo que esse objeto acaba de ser criado.
Após a compilação e execução do código anterior, o resultado será como o ilustrado na imagem
a seguir:
2.4.2.4. toString()
Este é um método da classe Object, principal classe em Java, e a partir da qual todas as
outras classes são herdadas. Como consequência, todas as classes Java possuem o método
toString(), o qual permite obter uma representação significativa de um objeto.
O método toString() retorna um String, sendo que o valor do tipo primitivo está encapsulado
no objeto. Além disso, a versão de uma instância desse método, a qual não é estática e não
possui argumentos, está contida nas classes Wrappers, as quais são marcadas como classes
finais.
O exemplo a seguir demonstra um terceiro método toString() fornecido pelas classes Integer
e Long:
O terceiro método toString(), fornecido pelas classes Long e Integer, apresenta as seguintes
características:
•• É um método estático.
O segundo argumento informa ao método que ele deve converter o primeiro argumento de
acordo com a base definida. Em seguida, o resultado deverá ser retornado como um String.
Normalmente, o primeiro argumento encontra-se na base 10.
Para retornar o valor convertido como uma representação em String, os métodos toString()
usam os tipos int e long. Veja o exemplo a seguir:
Java Programmer – Módulo II (online)
64
Após a verificação da descrição dos métodos de conversão, confira uma lista dos métodos
Wrapper mais comuns para conversão:
2.4.3. Autoboxing e auto-unboxing
A conversão entre valores de tipos primitivos e objetos Wrapper, antes do Java 5, era realizada
por meio de código adicional. A partir dessa versão, isso se tornou desnecessário. São utilizados
dois tipos de conversão, boxing (encaixotamento) e unboxing (desencaixotamento). Veja a
descrição de cada um desses tipos de conversão:
•• Conversão boxing: Esta conversão torna possível que um valor de um tipo primitivo seja
transformado em um objeto da classe empacotadora de tipo correspondente. Quando
realizada de maneira automática, esta conversão é denominada autoboxing;
Não devemos utilizar autoboxing de maneira deliberada, uma vez que o processo de
autoboxing e auto-unboxing pode afetar implicitamente a performance e o throughput (taxa
de transferência) das aplicações, em razão da mistura de wrappers e objetos primitivos nas
expressões aritméticas, em um loop estreito.
Essa conversão automática facilita o manuseio da API de Coleções Java, uma vez que toda coleção
somente aceita receber tipos de referência e nunca tipos primitivos. Com essa característica,
é possível intercambiar automaticamente entre as duas formas.
2.5. Classe LocalDateTime
A classe LocalDateTime, pertencente ao pacote java.time faz parte da nova API de manipulação
de data e hora lançada pelo Java 8.
LocalDateTime variavel =
LocalDateTime.parse(“0000-00-00T00:00:00.000”);
Para criar uma máscara de conversão com o padrão desejado, utilize o método estático
ofPattern() da classe DateTimeFormatter:
DateTimeFormatter mascara =
DateTimeFormatter.ofPattern(“[FORMATO_MASCARA]”);
Podemos, então, aplicar esta máscara ao método parse() para obter a data/hora a partir de
um string no formato especificado:
LocalDateTime variavel =
LocalDateTime.parse(“[DATA_HORA]”, mascara);
Java Programmer – Módulo II (online)
68
Símbolo Descrição
y Ano
M Mês
d Dia do mês (1-31)
h Hora (1-12)
H Hora (0-23)
m Minuto
s Segundo
S Milissegundo
E Dia da semana
D Dia do ano
w Semana do ano
W Semana do mês
a Marcador AM/PM
2.5.4. Métodos get()
Conforme já visto, uma instância da classe LocalDateTime contém as informações a respeito
de um instante na linha do tempo. Esta classe possui diversos métodos get(), a partir dos
quais podemos obter cada um dos detalhes isoladamente, como ano, hora, dia, minuto, etc.
Método Descrição
getYear() Retorna o ano.
getMonth() Retorna uma das instâncias da enumeração Month.
getMonthValue() Retorno o número do mês (1-12).
getDayOfMonth() Retorna o dia do mês (1-31).
getDayOfWeek() Retorna uma das instâncias da enumeração DayOfWeek.
getHour() Retorna a hora do dia (0-23).
getMinute() Retorna o minuto (0-59).
getSecond() Retorna o segundo (0-59).
getNano() Retorna o nanossegundo.
Java Programmer – Módulo II (online)
70
Veja um exemplo:
2.5.5. Métodos with()
Os métodos with() são utilizados para gerar um novo instante no tempo a partir de um instante
original, copiando todos os seus dados e modificando apenas o atributo especificado.
Método Descrição
withYear(int) Gera uma cópia com o ano modificado.
withMonth(int) Gera uma cópia com o mês modificado.
withDayOfMonth(int) Gera uma cópia com o dia modificado.
withHour(int) Gera uma cópia com a hora modificada.
withMinute(int) Gera uma cópia com o minuto modificado.
withSecond(int) Gera uma cópia com o segundo modificado.
withNano(int) Gera uma cópia com o nanossegundo modificado.
Classes utilitárias
71
Os métodos with() geralmente são utilizados em cascata quando desejamos manipular mais
de um atributo de data/hora. Veja o exemplo:
3.1. Introdução
Normalmente, os aplicativos que desenvolvemos possuem diversas tarefas a serem executadas.
É possível que em algum momento uma delas gere um erro ou exceção, isto é, uma ocorrência
que faça com que o fluxo normal do programa seja alterado. Isso pode ocorrer por diversos
motivos, como uma falha de hardware, por exemplo. Um erro ou exceção pode ser simples
ou complexo: em alguns casos, a execução do programa continua, em outros ela pode ser
interrompida.
3.2. Bloco try/catch
Encerrar o código que pode gerar exceções dentro de um bloco try é a primeira etapa para
criar um manipulador de exceções. A estrutura de um bloco try é a seguinte:
try {
bloco de código
}
blocos catch e finally. . .
Você pode fechar seu código dentro de um bloco try de duas maneiras:
Exceções
75
•• Colocando cada linha do código separada em um bloco try próprio, gerando, assim, um
manipulador de exceção para cada linha. Veja um exemplo:
•• Colocando o código inteiro dentro de um único bloco try, gerando diversos manipuladores
associados a ele. Veja um exemplo:
Encerrando o código em um bloco try, as possíveis exceções que forem lançadas serão tratadas
por um manipulador de exceções associado a elas.
Para associar um manipulador de exceções a um bloco try, é preciso inserir um bloco catch
imediatamente após o bloco try, sem nenhum código entre eles. Veja a seguir:
try {
}catch(tipo_de_exceção identificador){
}catch(tipo_de_exceção identificador){
}
Java Programmer – Módulo II (online)
76
Cada bloco catch representa um manipulador próprio para um tipo de exceção e contém o
código que será executado caso o manipulador seja chamado. O argumento tipo_de_exceção
representa o tipo de exceção que o manipulador pode tratar. Esse argumento deve consistir
no nome de uma classe que herda da classe Throwable. O argumento identificador indica o
nome da própria instância da exceção.
Em um programa onde diversos tipos diferentes de exceção estão sendo capturados por meio
de diversos blocos catch, a ordem com que esses blocos são posicionados abaixo do bloco try
influencia no comportamento desse desvio. Caso as diversas exceções tenham relacionamento
por herança entre si, devem ser declaradas do tipo mais específico para o tipo mais genérico,
ou seja, as exceções que representam tipos de classes pai na árvore de exceções devem vir
por último na ordem das declarações catch.
Exceções
77
Para manipular diferentes exceções, basta definir os tipos de exceção, separados por uma
barra vertical (conhecido como pipe em ambientes Unix-like). Veja um exemplo:
Caso a classe definida em catch possua subclasses, o catch captura qualquer exceção que
tenha subclasses dessa classe que foi definida. Se a classe não possuir subclasses, apenas a
própria classe definida em catch é capturada.
Recomenda-se que não seja desenvolvido apenas um manipulador para capturar todas as
exceções lançadas. Além disso, tipos de exceções que possuam relação de herança não devem
ser usados em declarações de blocos multi-catch. Caso haja a necessidade, por exemplo, de
se capturar FileNotFoundException (herda de IOException) e IOException, não o faça em um
bloco único, use blocos separados para este fim.
3.3. throws
Para indicar que um método pode gerar uma exceção, utilizamos o comando throws na sua
declaração. Isso é necessário, por exemplo, em uma operação com arquivos. Considere que
o programa espera um nome de arquivo qualquer para abri-lo e apresentar o conteúdo na
tela. Se o usuário informar o nome do arquivo errado, ou mesmo se não digitá-lo, poderá ser
gerada uma interrupção do programa.
Em casos assim, é necessário declarar as exceções que podem ocorrer durante a utilização do
método para que sejam tratadas. Isso é feito com o comando throws, cuja utilização você vê
a seguir:
Java Programmer – Módulo II (online)
78
Esse comando é colocado após a declaração do método seguido por um ou vários tipos de
exceções, separados por vírgula, conforme a necessidade. Isso permitirá ao método utilizar
código que potencialmente pode lançar todas as exceções declaradas.
3.4. finally
Um bloco finally (e as instruções contidas nele) é sempre executado após a finalização de um
bloco try, mesmo quando ocorre uma exceção inesperada.
Uma boa prática para o bloco finally é colocar um código de limpeza ou de depuração dentro
dele. Assim, você garante que esse código será sempre executado e impede que ele seja
eventualmente ignorado na execução por causa de um return, break, etc. Isso pode ser feito
sem problemas, mesmo quando não houver uma exceção prevista.
Usar o finally é importante também para evitar o desperdício de recursos, podendo ser usado
para fechar um arquivo ou recuperar recursos. Basta colocar o código correspondente dentro
do finally e assegurar que os recursos sejam sempre recuperados.
O uso do bloco finally é opcional. Em determinadas situações, o bloco catch também não é
necessário, por isso, pode ser que encontremos um bloco try seguido de um bloco finally em
um código.
Exceções
79
3.5. try-with-resource
O bloco de instrução try-with-resource pode ser considerado como um simples try que declara
um ou mais recursos com o propósito de fechá-lo(s) automaticamente com o término do bloco.
Veja um exemplo do mesmo código usado no exemplo anterior, mas declarado com a técnica
em questão:
Uma lista de classes dessas interfaces pode ser verificada nos docs oficiais da linguagem
Java. Acesse e confira:
•• AutoCloseable:
http://docs.oracle.com/javase/7/docs/api/java/lang/AutoCloseable.html
•• Closeable:
http://docs.oracle.com/javase/7/docs/api/java/io/Closeable.html
•• Uma exceção do tipo IOException é lançada pelo método close da interface Closeable;
•• Uma exceção do tipo Exception, por sua vez, é lançada pelo método close da interface
AutoCloseable. Esse aspecto permite que subclasses desta interface possam sobrepor
o comportamento do método close com o objetivo de lançar exceções especializadas,
como IOException, ou mesmo para deixar de lançá-las.
Java Programmer – Módulo II (online)
80
3.5.2. Exceções suprimidas
A preocupação com as denominadas exceções suprimidas (suppressed exceptions) surgem a
partir do bloco try-with-resources trazido pelo Java SE 7 para os efeitos gerados em objetos
que implementam a interface AutoCloseable.
•• Uma exceção ocorre no bloco try e, dessa forma, o controle passa para um bloco catch
devido e;
•• Tendo em vista que, nesse momento, o bloco try-with-resources tenta fechar todos os
recursos abertos sob sua responsabilidade, caso ocorra uma exceção nessa tentativa com
os recursos AutoCloseable, exceções são lançadas, porém, suprimidas.
Nessa situação, as exceções lançadas pela tentativa de fechamento dos recursos são suprimidas
e podem ser acessadas no bloco catch onde se encontra o fluxo por meio de uma construção
como a apresentada adiante:
A exceção pode ser lançada para outros métodos da pilha, mas também
podemos lançá-la fora do método main(), no final da pilha. Neste caso, a
JVM é interrompida e o rastreamento da pilha é apresentado na saída.
Java Programmer – Módulo II (online)
82
3.7. Hierarquia de exceções
Na linguagem Java, as exceções são objetos e os tipos de exceções são representados por
classes. Estas classes obedecem a uma hierarquia bem definida. Existem centenas de classes
de exceções nativas do Java. O diagrama a seguir mostra algumas das principais:
3.7.1. Exceções verificadas
Para confirmar se as exceções foram declaradas ou manipuladas, o compilador verifica o
código. As exceções verificadas correspondem àquelas derivadas da classe java.lang.Exception
e que não derivam de java.lang.RuntimeException. Estas exceções precisam ser capturadas
(tratadas) em alguma parte do código do programa. Caso a exceção não seja capturada ou
tratada, quando um método que a lança for chamado, o código não será compilado.
Exceções
83
3.8. Principais exceções
Várias exceções verificadas e não verificadas são lançadas pelos métodos existentes nas
bibliotecas de Java e, conforme já mencionado, existem centenas de exceções nativas do Java.
No entanto, algumas destas exceções destacam-se por serem de uso comum.
3.8.1. Throwable
Hierarquicamente, esta é a classe base de todas as exceções utilizadas pelo Java. Nesta classe,
encontramos as subclasses Error e Exception, que são utilizadas de maneira convencional
para indicar que situações incomuns ocorreram. Como você viu, em uma cláusula catch, um
dos argumentos pode ser Throwable ou uma de suas subclasses.
Java Programmer – Módulo II (online)
84
Entre os objetos que herdam dessa classe, estão os descendentes diretos e os indiretos,
que são aqueles que herdam a partir de descendentes diretos da classe. As subclasses Error
e Exception são as duas descendentes imediatas de Throwable. Veja, a seguir, como é a
estrutura da hierarquia de Throwable:
A JVM lança somente objetos que são instâncias da própria classe Throwable ou de suas
subclasses. Quando uma situação incomum ocorre, as instâncias são criadas para fornecerem
informações importantes.
•• Uma string de mensagem, cuja função é fornecer informações a respeito do erro ocorrido;
3.8.1.1. Exceções encadeadas
Muitas vezes, uma exceção pode causar outra exceção, ou seja, um programa pode lançar
uma segunda exceção como resposta à primeira. Para saber quando isso ocorre, você pode
usar o recurso de encadeamento de exceções.
Veja um exemplo:
Algumas vezes, a classe que lança um objeto Throwable está inserida em uma abstração
de camadas baixas e uma operação em um nível mais superior falha por causa de um erro
na camada inferior. Usando uma exceção que contém um cause, você possibilita à camada
superior enviar detalhes da falha ao seu chamador, evitando a propagação de um erro da
camada inferior e mantendo flexibilidade para alterar a implementação da camada superior
sem alterar sua API e, particularmente, as exceções lançadas pelos seus métodos.
Java Programmer – Módulo II (online)
86
Outras vezes, o método que lança um objeto Throwable está de acordo com uma interface
com propósitos gerais, que não permite ao método lançar o cause diretamente. Também
nestes casos, um objeto Throwable pode ter um cause.
Você pode associar um Throwable com um cause por meio de um construtor que tenha
cause como um de seus argumentos ou por meio do método initCause(Throwable).
3.8.1.2. Principais construtores
A seguir, são apresentados os construtores utilizados para criarmos uma nova classe Throwable.
Tais construtores também estão presentes em todas as classes abaixo desta. Podemos usar
diferentes construtores, de acordo com as diferentes situações:
•• public Throwable()
Constrói um objeto Throwable com uma mensagem de detalhe cujo valor é nulo. O recurso
cause não é inicializado, o que poderá ser feito com uma chamada ao método Throwable.
initCause(java.lang.Throwable). Você pode inicializar os dados de rastreamento de pilha no
objeto por meio do método fillInStackTrace().
•• public Throwable(String message)
Constrói um objeto Throwable com uma mensagem de detalhe. O parâmetro message indica
a mensagem a ser salva e posteriormente recuperada com o método getMessage(). Com
este construtor, o recurso cause também não é inicializado, o que poderá ser feito com uma
chamada ao método initCause(java.lang.Throwable).
•• public Throwable(Throwable cause)
Cria um objeto com um parâmetro cause especificado. É útil para objetos Throwable que
funcionam como invólucros de outros objetos desse tipo, como PrivilegedActionException.
•• public Throwable(String message, Throwable cause)
3.8.1.3. Principais métodos
•• getCause
Se houver um cause na classe Throwable, este método vai retorná-lo. Contudo, se o cause não
for reconhecido, ou mesmo não existir, o valor retornado será null. A sintaxe deste método é
a seguinte:
O cause retornado (no caso de haver um cause) pode ser um dos seguintes: aquele configurado
depois da criação pelo método initCause(Throwable); ou o cause fornecido a partir de um
dos construtores pelo qual a classe Throwable foi solicitada.
Para que um cause configurado de outra forma também seja retornado, este método pode ser
substituído por uma subclasse. Isso, porém, não é necessário, assim como não há necessidade
de que algum dos métodos printStackTrace seja substituído. Isso ocorre porque o método
getCause é chamado por qualquer um deles para definir o cause da classe Throwable.
•• getMessage
Método utilizado para retornar a string da mensagem de detalhe da instância Throwable. Essa
instância pode conter o valor null. A seguinte sintaxe é utilizada para o método getMessage:
•• printStackTrace
Na primeira linha do campo System.err, está o resultado retornado pelo método toString()
para o objeto Throwable. Já os dados que foram gravados anteriormente pelo método
fillInStackTrace() estão localizados nas demais linhas. O formato da informação apresentada
varia conforme a implementação.
Java Programmer – Módulo II (online)
88
Em que s refere-se ao PrintStream para ser utilizado na saída. Com essa variação do método,
apresentamos a classe Throwable e o backtrace para o autor da impressão que está definido
na sintaxe, que é a seguinte:
•• getStackTrace
Para que o acesso às informações de rastreamento de pilha que foram apresentadas pelo
método printStackTrace() seja programático, utilizamos o método getStackTrace. A sua
sintaxe é a seguinte:
•• setStackTrace
Em que stackTrace refere-se aos elementos aos quais a Throwable será associada. Esta
chamada efetua uma cópia do array especificado. O rastreamento de pilha da classe Throwable
não será afetado por nenhuma das alterações efetuadas no array determinado, depois que a
chamada ao método retornar.
Se um dos elementos de stackTrace tiver valor null, ou mesmo se o próprio stackTrace tiver
valor null, será lançada uma exceção, cujo nome é NullPointerException.
3.8.2. Error
Muitos erros que ocorrem durante a execução de um programa são situações bastante incomuns
e, por isso, são considerados graves. Para indicar que esses erros ocorreram e que não devem
ser pegos por uma aplicação adequada, existe uma subclasse de Throwable: a classe Error.
O erro ThreadDeath é considerado como uma situação normal. Contudo, o fato de ele não
poder ser pego por uma aplicação adequada o faz ser uma subclasse de Error.
As subclasses de Error podem ser lançadas durante a execução de um método, mas não
podem ser pegas e geralmente representam problemas de ambiente, como falta de memória,
versão errada do JVM ou algum outro problema fora da alçada da aplicação que está sendo
executada.
Exceções do tipo Error não precisam ser declaradas na cláusula de lançamento do método,
caso os erros não correspondam a situações incomuns que não deveriam ocorrer.
3.8.3. Exception
A classe Exception e suas subclasses têm como função indicar determinadas condições que
uma aplicação pode capturar em um bloco catch. Funcionam como uma extensão da classe
Throwable.
3.8.4. NullPointerException
Trata-se de uma exceção não verificada pertencente ao pacote java.lang.
3.8.5. NumberFormatException
É uma exceção não verificada também pertencente ao pacote java.lang.
3.8.6. ArrayIndexOutOfBoundsException
Essa é outra exceção não verificada, também pertencente ao pacote java.lang.
3.8.7. ArithmeticException
Trata-se de uma exceção não verificada, também pertencente ao pacote java.lang.
3.8.8. ClassCastException
Outra exceção não verificada pertencente ao pacote java.lang.
3.8.9. IOException
Já esta é uma exceção verificada pertencente ao pacote java.io.
Possui diversas classes filhas que representam subtipos de problemas do gênero I/O:
Exceções
93
3.8.10. Classe SQLException
Exceção verificada pertencente ao pacote java.sql.
Ocorre ao tentar realizar operações de acesso a bancos de dados que resultam em problemas,
tais como:
Veja um exemplo:
Java Programmer – Módulo II (online)
94
3.9. Exceções personalizadas
Quando precisa lançar uma exceção, você tem duas opções: uma é usar uma exceção já criada,
seja nativa da própria linguagem Java ou criada por terceiros; a outra é criar você mesmo
sua exceção. Isso pode ser útil em muitos casos. Para saber se criar sua própria exceção é
adequado em determinada situação, baseie-se nas perguntas a seguir:
•• O tipo de exceção que você precisa realmente não pode ser encontrado em Java?
Ao criar uma exceção própria, você estende a classe Exception para criar uma nova classe
dela. Veja no exemplo a seguir como fazer isso:
Exceções
95
Veja, agora, um exemplo utilizando a classe que acabamos de criar para tratar exceções. Neste
exemplo, a classe Nota recebe uma determinada nota e verifica se ela é válida, permitindo que
apenas os valores maiores ou iguais a zero e menores ou iguais a 10 sejam aceitos:
Java Programmer – Módulo II (online)
96
No método setNota, observando a linha if(nota < 0 || nota > 10), se esta condição for verdadeira,
é gerada uma nova exceção (NotaInvalidaException e = new NotaInvalidaException()). Na
classe CadastroNotas, a seguir, trataremos esta exceção:
Após compilar o código, ao tentar executá-lo, o resultado será como o exibido na figura a
seguir:
3
Teste seus conhecimentos
Exceções
Java Programmer – Módulo II (online)
98
☐☐ a) try
☐☐ b) catch
☐☐ c) throw
☐☐ d) finally
4.1. Introdução
Nesta leitura complementar, exploraremos um novo paradigma de programação já utilizado
por algumas das linguagens da atualidade e que, a partir da versão 8, também pode ser
utilizado pelo Java. Trata-se da programação funcional.
Tal interface abstrai a existência de um método (uma operação aritmética) que recebe dois
valores double, realiza alguma atividade (a ser implementada por alguma classe) e, por fim,
retorna um único valor double como resultado.
Programação funcional
101
Um exemplo de implementação para essa interface pode ser visto na classe Soma:
Criamos a classe Soma como uma implementação de operação aritmética e podemos instanciá-
la a qualquer momento.
Seguindo esse conceito, para cada operação aritmética que possa ser utilizada pela aplicação,
devemos criar uma nova classe implementadora conforme a necessidade (soma, divisão,
potência etc.).
Repare, no último código (classe Executando), que o objeto operacao não tem atributos (objeto
sem estado) e possui um único método, chamado execute(). É essa forma de desenvolvimento
que chamamos de programação funcional. Nesse formato de programação, encapsulamos as
rotinas de execução (funções) em simples objetos que podem ser chamados posteriormente
a qualquer momento.
Java Programmer – Módulo II (online)
102
Adicionalmente, o Java 8 incorporou uma nova sintaxe de comandos para elaboração desses
objetos funcionais.
Assim sendo, podemos realizar uma implementação imediata por meio da nova notação e, em
seguida, utilizar o objeto funcional normalmente:
4.2. Interface funcional
Chamamos de interface funcional aquela que possui somente um método abstrato (sem corpo),
exigindo a implementação de apenas um método pela classe responsável.
Uma interface funcional pode possuir vários métodos estáticos ou default, porém, um único
método abstrato.
O método abstrato, por sua vez, pode possuir qualquer tipo de retorno, podendo até não
haver retorno (void), e também pode possuir quaisquer quantidades e tipos de argumentos
(parâmetros de entrada). O uso de genéricos também é permitido.
Esse tipo de interface é amplamente utilizado na programação funcional, pois é através dela
que definimos a assinatura das funções que serão utilizadas ao longo do código-fonte.
Além disso, a versão 8 do Java traz o novo package java.util.function contendo inúmeras
outras interfaces funcionais que foram criadas especificamente para auxiliar a programação
funcional.
4.3. Expressões lambda
Uma expressão lambda, também chamada de função anônima, trata-se de código utilizado
na implementação de uma interface funcional baseada em uma nova nomenclatura dentro da
linguagem Java.
Uma expressão lambda sempre deve ser atribuída a uma variável de tipo funcional (interface
funcional), como no exemplo anterior, ou passada como argumento na chamada de um método
que aceita um tipo funcional, como no exemplo a seguir:
Programação funcional
105
4.3.1. Forma geral
De forma geral, uma expressão lambda possui duas seções separadas pelo símbolo -> (menos-
maior):
Em que temos, ao lado esquerdo, os parâmetros a serem recebidos pelo método funcional e,
ao lado direito, o conjunto de instruções que implementam esse método.
•• Interface
•• Expressão
Java Programmer – Módulo II (online)
106
Repare que a expressão lambda não declara os tipos dos parâmetros de entrada, representados
por (n, i, s), no exemplo anterior. Os tipos são sempre aqueles mesmos declarados no método
da interface.
•• Interface
•• Expressão
•• Interface
Programação funcional
107
•• Expressão
Quando temos duas ou mais instruções no corpo da expressão, devemos utilizar chaves em
volta do corpo e ponto e vírgula ao final de cada instrução:
Java Programmer – Módulo II (online)
108
No exemplo adiante, o método funcional a ser implementado deve retornar um valor double:
Se a expressão lambda utilizada possui diversas linhas, ela poderá ser implementada
normalmente utilizando a cláusula return ao final do código:
Programação funcional
109
Mas, se a expressão lambda for pequena, composta por uma única instrução, podemos
simplesmente indicar a instrução de retorno após o símbolo -> (menos-maior), sem chaves e
sem a cláusula return:
4.4. Referenciando métodos
Existem situações em que a lógica a ser utilizada pela expressão lambda já foi implementada
em alguma outra parte de sua aplicação por algum método. Nesses casos, é possível evitar a
duplicação da lógica já implementada e utilizar uma referência ao método já existente.
Para exemplificar esse recurso, considere a mesma interface utilizada no primeiro exemplo
desta leitura:
Java Programmer – Módulo II (online)
110
Suponhamos que, em sua aplicação, exista uma classe que possui um método pronto que
realiza exatamente aquilo que precisamos implementar em nossa função:
Em situações como esta, podemos “linkar”, ou criar uma referência desse método, para que
ela seja utilizada como a função que queremos implementar:
No exemplo anterior, utilizamos a seguinte notação para criar uma referência funcional a um
método estático de alguma classe pré-existente de nossa aplicação:
Para referenciar um método de instância (não estático), devemos utilizar a seguinte notação,
conforme o próximo exemplo:
Referenciando métodos
A fim de suportar o uso de ELs, o Java 8 traz uma vasta quantidade de interfaces funcionais
predefinidas que facilitam na implementação dessas rotinas de processamento. Tais interfaces
foram disponibilizadas em um novo package denominado java.util.funcion.
Muitas das APIs do Java 8 que utilizam o conceito de programação funcional fazem uso das
interfaces mencionadas na tabela, conforme veremos nas próximas leituras.
4
Teste seus conhecimentos
Programação
funcional
Java Programmer – Módulo II (online)
114
5
99 O que são coleções;
99 Generics;
99 Coleção Set;
99 Coleção Map;
99 Coleção List;
99 Manipulando coleções com Streams;
99 Framework de coleções.
Java Programmer – Módulo II (online)
118
As coleções estão divididas em três versões básicas: listas (List), mapas (Map) e conjuntos
(Set). As listas referem-se às listas de itens, nas quais estão as classes que implementam a
interface List. Em mapas, estão os itens que possuem identificação exclusiva, cujas classes
implementam a interface Map. Já conjuntos referem-se a itens exclusivos, cujas classes
implementam a interface Set.
Coleções e conjuntos
119
•• Iteração pela coleção, sendo que cada objeto ou elemento é verificado, um em seguida do
outro;
•• Recuperação de um objeto na coleção sem que, para isso, ele seja removido, caso comum
observado em estruturas conhecidas como pilhas.
•• Collection;
•• List;
•• Set;
•• Map;
•• Sorted Set;
•• Sorted Map.
As interfaces Set e List derivam de Collection, porém, o mesmo não acontece com as interfaces
e classes relacionadas a mapas. Com relação às classes de implementação, elas são divididas
nas seguintes categorias: conjuntos, mapas e listas.
Java Programmer – Módulo II (online)
120
•• Mapa (Map)
•• Conjunto (Set)
•• Lista (List)
•• Ordenada e classificada;
Ao ordenar uma determinada coleção, o processo de iteração nos elementos segue uma ordem
definida. Já a classificação de uma coleção refere-se a uma ordem natural, que varia conforme
o tipo de dado.
Para entender melhor, considere, por exemplo, a classe LinkedHashSet, que é ordenada.
Sua ordem é definida pela inserção dos elementos, ou seja, o último elemento inserido no
conjunto ocupa a última posição.
A ordem natural de um objeto é definida pela sua classe. Dessa forma, a ordem natural de
objetos Integer é definida pelo valor numérico, em que 1 vem antes de 2, 5, que vem antes de
10, e assim por diante. Além de serem ordenadas, as coleções que possuem a ordem natural
são, também, classificadas.
5.2. Generics
Generics (ou genéricos) são um recurso que permite tornar tipos em parâmetros para a
definição de classes, métodos e interfaces. Isso permite que você reutilize o mesmo código
em diferentes entradas. Código construído com generics possui as seguintes vantagens em
relação a estruturas de código que não os utilizem:
•• A eliminação de casts;
5.2.1. Tipos genéricos
Tipos genéricos são classes ou interfaces parametrizadas de acordo com tipos. Para entender
melhor o conceito de tipo genérico, acompanhe o exemplo a seguir:
Os tipos genéricos podem ser invocados quando for necessário referenciar uma classe genérica
de dentro do código. Invocar um tipo genérico é como invocar um método, a única diferença
é que, em vez de passar um argumento a um método, você passa um argumento de tipo à
classe genérica em questão.
Classes compostas por um tipo genérico são conhecidas como tipos parametrizados.
Coleções e conjuntos
123
Para criar uma instância da classe genérica, você deve usar a palavra chave new e declarar o
tipo (fechado com sinais de maior e menor, <>) entre o nome da classe e os parênteses, da
seguinte maneira:
Note, nas linhas em que a classe genérica é instanciada, que o tipo parametrizado, usado
em cada uma das classes instanciadas, é aposto entre os sinais “<” e “>” e essa configuração
só é exigida para o lado esquerdo da atribuição. A partir do Java 7, não é necessário repetir
o trecho que determina o tipo a ser utilizado na classe genérica. Considerando o exemplo
anterior, <String> e <Integer>, bastando para isso, usar o operador <> vazio entre o nome da
classe e o par de parênteses relativos ao construtor. Esse operador passou a ser chamado de
Diamond Operator (Operador Diamante).
Java Programmer – Módulo II (online)
124
5.3. Coleção Set
Consiste em um conjunto que não possui elementos duplicados. Isto quer dizer que, se dois
objetos verificados pelo método equals() forem considerados equivalentes, somente um
deles poderá permanecer na coleção. A coleção Set possui diversas implementações. A seguir,
apresentamos as características das mais importantes:
•• HashSet
A classe HashSet é um conjunto não ordenado e não classificado, que não permite duplicações
entre seus elementos. Você deve usá-la se a ordem na iteração não for necessária. A HashSet
utiliza o código de hashing do objeto que está sendo inserido, fazendo com que o desempenho
obtido no acesso seja melhorado à medida que a implementação de hashCode() for mais
eficiente.
•• LinkedHashSet
Quando for necessário estabelecer uma ordem na iteração, utilize a classe LinkedHashSet, na
qual os elementos são iterados na mesma ordem em que foram inseridos. Esta classe é uma
versão ordenada de HashSet.
A LinkedHashSet mantém uma lista com encadeamento duplo para todos os elementos. Os
elementos podem ser iterados pela ordem do último elemento acessado, o que significa que
a ordem de iteração dos elementos pode ser invertida.
•• TreeSet
5.3.1. Classe HashSet
Se você quiser realizar buscas, é mais eficiente utilizar a implementação HashSet. Contudo,
não é aconselhável utilizá-la para realizar navegações sequenciais pelos elementos.
Coleções e conjuntos
125
5.3.2. Classe Iterator
Para navegar pelos elementos, utilize a classe Iterator. Ela interage com classes não indexadas
e possui um método chamado iterator(), que retorna um objeto da classe Iterator e é definido
pela interface Set. Veja os métodos da classe Iterator:
•• boolean hasNext()
Este método retorna true se a iteração tiver mais elementos. Isto quer dizer que o valor true
é retornado quando o método não lançar uma exceção.
•• E next()
Este método retorna o próximo elemento na iteração. Se a iteração não tiver mais elementos,
lança NoSuchElementException. Perceba que seu retorno é parametrizado com “E”, ou seja,
qualquer objeto atribuído como elemento da coleção à qual se refere poderá ser retornado
pelo método.
•• void remove()
O último elemento retornado pelo iterador é removido da coleção subjacente. Este método é
chamado apenas uma vez em cada chamada de next(). Além disso, se a coleção subjacente
for modificada enquanto a iteração está em curso por qualquer outra forma que não pela
chamada deste método, o comportamento de um iterador não é especificado.
5.3.3. Interface Set
Para garantir que a ordem dos elementos seja ascendente, tomando por base sua ordem
natural, utilize a classe java.util.TreeSet, a qual é responsável por implementar a interface
Set. Outra maneira de garantir a ordem ascendente dos elementos é a partir do uso de classes
que implementem a interface Comparator, uma vez que podem ser passadas como parâmetro
na construção do TreeSet, ou, ainda, a partir de elementos na coleção que implementem a
interface Comparable. Ambas serão vistas adiante.
Java Programmer – Módulo II (online)
126
Depois de compilado e executado o código anterior, o resultado será como o exibido na figura
a seguir:
O método forEach() permite a utilização de uma expressão lambda que interage com cada um
dos itens da coleção, sem que seja necessário criar um loop no código.
Coleções e conjuntos
127
Através deste método, podemos remover de nossa coleção todos os itens que não satisfaçam
a um determinado critério.
Java Programmer – Módulo II (online)
128
O exemplo a seguir remove todos os produtos cujo preço seja superior a R$ 2,50:
5.3.6. Interface Comparable
O método de comparação int compareTo(Object o) é definido pela interface Comparable. O
resultado deste método pode ser um número negativo, positivo ou zero.
Veja:
•• Número negativo indica que o objeto é menor do que o objeto passado como parâmetro;
•• Número positivo indica que o objeto é maior do que o objeto passado como parâmetro;
•• Zero indica que o objeto e o objeto passado como parâmetro são iguais.
Coleções e conjuntos
129
5.3.7. Interface Comparator
O método de comparação int compare(Object o) é definido por esta interface. Como na
interface Comparable, o método da Comparator resulta no mesmo número positivo, negativo
ou zero.
5.4. Conjunto Map
Esta interface substituiu a classe Dictionary, que era uma classe totalmente abstrata. Você
deve saber que este conjunto mapeia pares chave-valor. Lembre-se que um mapa não pode
conter chaves duplicadas, pois cada chave pode mapear, no máximo, um valor.
Além disso, o conteúdo de um mapa pode ser visualizado como uma coleção de chaves, uma
coleção de valores ou um conjunto de mapeamentos chave-valor. A ordem de um mapa é a
ordem em que os iteradores retornam seus elementos na visualização de coleção do mapa.
Enquanto algumas implementações de mapas têm garantias específicas quanto à ordem, como
a classe TreeMap, outras não apresentam garantias, como a classe HashMap.
Java Programmer – Módulo II (online)
130
5.4.1. Classe HashMap
É por meio da classe AbstractMap que a interface Map implementa a classe HashMap, que
não é sincronizada.
Você pode construir uma classe HashMap sem valores. Para isso, utilize o construtor HashMap().
Para mapas parametrizados, use HashMap<>(). Mas você pode, também, construir um Map já
com os pares de chave-valor do Map m. Para essa tarefa, utilize o construtor HashMap<>(Map
m).
Apenas um valor null pode ser inserido na classe HashMap, pois esta classe pode conter
apenas uma chave. Use a sintaxe descrita a seguir para adicionar um par chave-valor à classe
HashMap (Note que todas as referências são parametrizadas):
Para verificar se um determinado valor ou uma chave foi inserida na classe HashMap, utilize
uma das seguintes sintaxes:
Para que seja possível retornar o valor associado à chave passada como parâmetro, utilize a
sintaxe a seguir:
V get(Object key)
Coleções e conjuntos
131
Para verificar se existe uma chave no HashMap, utilize o método contaisKey(Object Key).
Esta chave pode estar associada a um valor null, o qual é retornado quando a chave não é
encontrada.
Para remover o par chave-valor localizado no conjunto HashMap, utilize a seguinte sintaxe:
V remove(Object key)
int size()
5.4.2. Classe Hashtable
A classe Hashtable possui métodos sincronizados e está presente na linguagem Java desde
a primeira versão. Sua interface e seus objetivos são praticamente os mesmos da classe
HashMap. Use esta classe quando precisar de sincronismo ou compatibilidade com código
legado.
A classe mais conhecida na linguagem Java que representa uma HashTable é a classe
Properties, utilizada, geralmente, como arquivo de configuração externo e banco de texto no
formato de pares chave=valor.
5.5. Conjunto List
No conjunto List, o índice é muito importante e, por isso, existem diversos métodos relacionados
a ele. Para definir a posição do índice, é preciso configurar um objeto em um determinado
índice. Outra possibilidade é adicionar esse objeto ao índice sem que a posição seja definida.
Neste caso, o objeto é inserido no final do índice.
Coleções e conjuntos
133
Confira, a seguir, três implementações importantes de List, ordenadas pela posição do índice:
•• ArrayList
Este conjunto foi implementado na versão 1.4 de Java e diz respeito a um array que pode
aumentar de tamanho. Ele não é classificado, mas é ordenado.
Você deve utilizar ArrayList quando não forem necessárias muitas inserções e exclusões.
•• LinkedList
Para realizar inserções, bem como exclusões com rapidez, utilize esse tipo de lista. Contudo,
é preciso ressaltar que o processo de iteração neste conjunto pode não ser tão rápido se
comparado ao conjunto ArrayList.
•• Vector
O Vector é uma coleção bem antiga, lançada com a primeira versão da linguagem Java. Esta
coleção é semelhante ao ArrayList, porém, em Vector(), os métodos são sincronizados para
que as threads sejam seguras.
Como você sabe, a classe ArrayList implementa a interface RandomAccess, que é uma
interface marcadora.
5.5.1. Java.util.List
Os objetos que implementam a interface List permitem obter controle do local em que os
elementos são inseridos. Isso é possível porque a interface List define uma sequência, ou seja,
uma coleção ordenada.
Com isso, um índice inteiro é utilizado para acessar os elementos. Estes podem receber valor
null e podem ser duplicados, o que é permitido pelas classes que implementam a interface
List.
Java Programmer – Módulo II (online)
134
5.5.2. Java.util.ArrayList
Esta classe é uma implementação da interface List e não é sincronizada. Esta classe permite
inserir quaisquer tipos de objetos, incluindo o null. Além disso, o tamanho do array pode ser
alterado em tempo de execução. Veja, a seguir, os construtores utilizados para construir um
ArrayList:
•• public ArrayList (int initialCapacity)
Constrói uma lista vazia, porém especifica a capacidade inicial. O parâmetro initialCapacity
determina a capacidade inicial da lista. Além disso, se a capacidade inicial especificada for
negativa, o construtor lança IllegalArgumentException.
•• public ArrayList()
É usado para construir uma lista vazia com a capacidade inicial de dez.
É usado para construir uma lista com os elementos da coleção especificada, na ordem em que
estes são retornados pelo iterador da coleção. Além disso, o parâmetro c determina a coleção
cujos elementos devem ser colocados nesta lista. Note que o parâmetro a ser recebido utiliza
parametrização de tipos genéricos e os símbolos Collection<? extends E> significam: uma
coleção qualquer parametrizada com o tipo E ou quaisquer de seus subtipos. Se a coleção
especificada for nula, o construtor lança NullPointerException.
Para adicionar um elemento no conjunto ArrayList, há duas sintaxes. Elas estão descritas a
seguir, todas utilizando a notação parametrizada para tipos genéricos:
boolean add(E e)
Essa sintaxe retorna o valor true, quando o objeto solicitado for encontrado no conjunto
ArrayList. Para verificar a quantidade de elementos ou o tamanho dos elementos do conjunto,
a sintaxe utilizada é int size().
As outras duas tarefas que você pode realizar no ArrayList referem-se à recuperação de um
elemento e à remoção de elementos desse conjunto. Para esta última tarefa, use a sintaxe E
remove(int index). Já para a recuperação, use uma das seguintes sintaxes:
Neste caso, quando o objeto obj não for encontrado, o valor retornado será 1.
E get(int pos)
•• Para reorganizar todos os elementos da lista (ordenar) através de uma expressão lambda:
void sort(Comparator<E> c)
Java Programmer – Módulo II (online)
136
Observe o exemplo:
Além dos métodos forEach() e removeIf() já vistos nesta leitura, a API de coleções traz muitos
outros métodos utilitários que podem ser aplicados a coleções na realização de filtragem,
ordenação e muitas outras tarefas.
Um Stream trata-se de um manipulador dos dados de uma coleção. Ele está sempre associado
a uma coleção e acoplamos a ele instruções de busca, filtragem ou quaisquer outros tipos de
processamento de coleções.
Um stream possui um genérico do mesmo tipo da coleção associada. Para obter um stream a
partir de uma coleção, basta executar o método stream():
Em geral, as operações com streams são realizadas em cascata, a fim de produzir um resultado
final após sucessivos processamentos:
colecao.stream()
.algumMetodo(lambda1)
.outroMetodo(lambda2)
.maisUmMetodo(lambda3);
É importante ressaltar que a manipulação de streams não afeta a coleção associada. A execução
de métodos sucessivos sobre um stream apenas afeta a forma com que os dados são buscados
sobre a coleção.
Java Programmer – Módulo II (online)
138
5.6.1. Método sorted()
Obtém um stream ordenado dos itens da coleção:
Coleções e conjuntos
139
Caso desejemos uma ordenação diferente da natural ou mesmo se os elementos não possuírem
um critério natural, podemos chamar este método passando uma expressão lambda de
ordenação:
Veja o resultado:
Java Programmer – Módulo II (online)
140
Neste exemplo, na linha 17, criamos uma expressão lambda que implementa o critério de
comparação entre dois funcionários (representados por f1 e f2) e realizamos a comparação
pelo nome (método getNome()).
Confira o resultado:
Coleções e conjuntos
141
Neste exemplo, estamos ordenando inversamente por cargo e, em seguida, diretamente por
salário.
5.6.2. Método filter()
Obtém um stream contendo alguns dos elementos do stream original.
O método filter() permite a utilização de uma expressão lambda de filtragem, que desconsidera
os itens que não atendam ao critério especificado.
O exemplo adiante filtra uma coleção de funcionários, exibindo somente aqueles que são
desenvolvedores e que possuem salário superior a R$ 5.000,00:
5.6.3. Método limit()
Limita a quantidade de itens contidos no stream original gerando um stream com, no máximo,
a quantidade especificada.
Confira o resultado:
Coleções e conjuntos
143
5.6.4. Método skip()
Este método desconsidera os primeiros n itens do stream original. Tem o efeito oposto ao do
método limit().
O exemplo adiante exibe todos os funcionários ordenados inversamente por salário, com
exceção dos três primeiros:
Veja o resultado:
Java Programmer – Módulo II (online)
144
5.6.5. Método map()
Utiliza uma expressão lambda de transformação para gerar um stream de elementos de um
tipo diferente.
Veja um outro exemplo, com a mesma lista de funcionários. Mas desta vez, utilizamos o stream
de Funcionario para obter um stream de String, contendo simplesmente os seus cargos:
5.6.6. Método distinct()
Este método desconsidera os elementos repetidos do stream original. Em outras palavras, os
itens iguais são considerados apenas uma vez. O critério de igualdade é estabelecido pelo
método equals() de cada objeto da coleção.
Veja o resultado:
Coleções e conjuntos
147
5.6.7. Método count()
Este método retorna um número inteiro longo (long), informando a quantidade de itens que
está sendo considerada no stream.
O exemplo adiante exibe a quantidade de funcionários que recebem salário maior que R$
5.000,00:
Veja o resultado:
Java Programmer – Módulo II (online)
148
Veja um exemplo:
Para obter o possível valor contido nesta entidade, utilize o método get().
Coleções e conjuntos
149
5.7. Framework de coleções
A plataforma Java inclui um framework de coleções, que é uma arquitetura unificada para
representar e manipular coleções, permitindo que estas sejam manipuladas de forma
independente dos detalhes da implementação.
•• Implementações concorrentes, que são aquelas projetadas para uso altamente concorrente;
•• Algoritmos: São métodos estáticos com funções úteis nas coleções, como ordenar uma
lista;
•• Utilitários de array: São funções utilitárias para arrays de tipos primitivos e objetos de
referência. Esta característica foi adicionada à plataforma Java junto com o framework de
coleções e se baseia em algumas estruturas do framework.
5
Teste seus conhecimentos
Coleções e
conjuntos
Java Programmer – Módulo II (online)
152
☐☐ a) Adição de objetos.
☐☐ a) Stack
☐☐ b) Set
☐☐ c) Map
☐☐ d) List
☐☐ e) Collection
Coleções e conjuntos
153
1.1. Introdução
Entre os métodos mais importantes da classe object estão o hashCode(), usado para gerar
código hash, e o equals(), usado para testes de igualdade. Nas versões anteriores à Java 7,
era preciso usar APIs para implementar esses métodos. A versão 7, porém, introduz a classe
java.util.Objects, que facilita a implementação desses métodos, tendo a vantagem de ser uma
classe nativa de Java.
Para operar sobre objetos, a classe java.util.Objects é composta por métodos utilitários
estáticos, os quais incluem métodos null-safe ou null-tolerant para calcular o código de
hashing de um objeto, retornar uma string para um objeto, bem como comparar dois objetos.
Sua sintaxe é a seguinte:
Veja, adiante, como usar os métodos equals() e hashCode() para, respectivamente, realizar
testes de igualdade e gerar código hash.
1.2. Equivalência de variáveis
Quando for necessário verificar se duas entidades (variáveis ou objetos) são equivalentes, você
deve fazer uma comparação entre elas. Essa comparação é distinta para cada tipo de entidade
descrita, conforme apresentado a seguir.
1.2.1. Variáveis primitivas
Para comparar as variáveis primitivas, utilize o operador ==. Como resultado, você terá um
valor booleano, veja:
1.2.2. Variáveis de referência
Para comparar as variáveis de referência, utilize o operador ==. O resultado dessa comparação
também é um valor booleano, veja:
•• true: Valor retornado quando os dois padrões de bits das variáveis comparadas são
equivalentes;
•• false: Valor retornado quando os dois padrões de bits das variáveis comparadas não são
equivalentes.
Assim, o operador == só pode comparar as variáveis que apontam para objetos de uma mesma
classe ou de uma mesma hierarquia de classes.
O tamanho de uma variável de referência não pode ser determinado de uma implementação
Java para a outra. Mas você deve saber que o tamanho e o formato de todas as variáveis de
referência que são executadas na mesma Virtual Machine são os mesmos, independentemente
de onde o programa possa ser executado.
1.3. Equivalência de objetos
Para comparar o conteúdo de dois objetos, ou seja, para comparar se dois objetos são
significativamente equivalentes, utilize o método equals(). Use o operador == para verificar se
um mesmo objeto string é referenciado por duas variáveis de referência string. Neste caso,
se ambos forem equivalentes, o valor retornado é true.
Nos objetos wrapper, o valor booleano true é retornado pelo método equals() quando as
classes wrapper e os valores primitivos são iguais. Isso ocorre porque, ao comparar dois
objetos desse tipo, você verifica os valores primitivos encapsulados.
Java Programmer – Módulo II (online)
158
No momento em que as classes são criadas, é preciso definir o que determina que dois objetos
distintos sejam significativamente equivalentes. Para fazer a comparação entre dois objetos
distintos, o método equals() é substituído nas classes wrapper e string. Desse modo, é
possível comparar as instâncias de uma classe, sendo que o valor de uma instância pode ser
representado coletivamente por variáveis de referência contidas em uma classe.
O método equals () retorna true se os argumentos forem iguais entre si, caso contrário,
retorna false. Assim, ele retorna true se ambos os argumentos forem nulos. Porém, retorna
false se apenas um argumento for nulo. Se nenhum dos argumentos for nulo, a igualdade é
determinada usando o método equals do primeiro argumento. Sua sintaxe é a seguinte:
public static boolean equals(Object a,
Object b)
Este método apresenta como parâmetros os objetos a e b, os quais são comparados para
determinar a igualdade.
O exemplo a seguir demonstra a utilização deste método para testar a equivalência de objetos:
equals() e hashCode()
159
•• Reflexivo: Considere a variável a. O valor true é retornado por a.equals() para qualquer
valor de referência atribuído à variável a;
O método equals() recebe também como argumento o valor null. Considerando a variável
a, por exemplo, a operação a.equals(null) retorná false para qualquer valor de a que seja
diferente de null.
Você deve saber que o método hashCode() e o método equals() estão vinculados por um
contrato de associação, no qual fica determinado que os valores de hashing de dois objetos
devem ser iguais, caso o método equals() considere esses objetos equivalentes. Por isso, para
substituir o método equals(), é preciso substituir também o método hashCode().
Java Programmer – Módulo II (online)
160
1.4. Hashing
Ao armazenar um tipo de elemento em um endereço da estrutura de dados, você realiza um
processo chamado hashing. Esse endereço da estrutura de dados é gerado a partir de um
algoritmo que toma por base o valor a ser armazenado.
Para evitar as colisões, os métodos hashing são criados conforme o tamanho e o tipo de
estrutura de dados utilizados. Algumas classes de conjuntos utilizam o valor de hashing de
um objeto. Esse valor não é exclusivo, contudo, ele pode ser considerado como um número
por meio do qual o objeto é identificado.
Você deve saber que uma classe pode conter um método de código de hashing ineficiente.
No entanto, o contrato definido na documentação da classe Object não pode ser violado por
este método.
public static int hashCode(Object o)
•• É possível chamar o método hashCode() mais de uma vez no mesmo objeto durante a
execução de um aplicativo Java. Neste caso, o método retorna o mesmo inteiro, o qual
deve ser constante em todas as execuções do aplicativo, desde que não tenham sido
realizadas alterações nas informações utilizadas na comparação do método equals() do
mesmo objeto;
•• Uma sobrescrita (overriding) válida do método hashCode() pode ser feita quando se
deseja alterar a estratégia de distribuição dos objetos na tabela de hashing.