Java Avançado
Exceções
Erro: Algo irreparável, a aplicação trava ou é encerrada drasticamente Exceções: Fluxo inesperado, não previstos pela aplicação
try {
// bloco de código conforme esperado
}
catch(Exception e) { // identifica exceção
// bloco de código que captura as exceções que podem acontecer
// em caso de um fluxo não previsto
}
finally {
// bloco de código a ser executado, independente de ocorrer um erro ou não
}O bloco try / catch pode conter um conjunto de catchs, correspondentes a cada exceção prevista em uma funcionalidade do programa
Hierarquia das exceções
Checked Exceptions: São aquelas que são verificadas em tempo de compilação; o compilador exige que qualquer método que possa lançar uma exceção checada declare essa exceção (throws) ou trate a exceção (try-catch)
Unchecked Exceptions: São aquelas que não precisam ser declaradas ou tratadas explicitamente pelo compilador, incluem subclasses de RuntimeException e erros de tempo de execução, representam erros de programação, como acessar um objeto nulo (NullPointerException) ou realizar operações matemáticas inválidas (ArithmeticException)
A principal diferença é a obrigatoriedade de tratamento ou declaração dessas exceções no código
Exceções checadas exigem tratamento explicito, garantindo que o código seja robusto frente a condições excepcionais conhecidas
Exceções customizadas
Pode-se criar exceções personalizadas, baseadas em regras de negócio
Interface
Tipo abstrato utilizado para especificar o comportamento de uma classe, dado que o Java não permite Herança Múltipla
Todos os métodos são implicitamente públicos e abstratos
Uma interface não pode conter um método construtor, não é possível criar uma instância da própria interface --> É preciso criar uma instância de alguma classe que implemente a interface para fazer referência a ela
Collection Framework API
Uma coleção (collection) é uma estrutura de dados que serve para agrupar muitos elementos em uma única unidade
Aceita somente objetos como elementos
Pode ter coleções homogêneas e heterogêneas, normalmente se utiliza coleções homogêneas de um tipo específico
➡ Contém: Interfaces: Tipos de dados abstratos que representam coleções; permitem manipular a coleção independentemente do nível de detalhe que elas representam Implementações: Estruturas de dados reutilizáveis; implementações concretas das interfaces de coleta Algoritmos: Funcionalidades reutilizáveis; métodos que executam cálculos úteis, como pesquisa e classificação, em objetos que implementam interfaces de coleta; são polimórficos, isto é, o mesmo método pode ser usado em muitas implementações diferentes da interface de coleta apropriada
Todas as interfaces e classes são encontradas dentro do pacote
java.util.
Collections (classe): Classe utilitária do Java para operações comuns em coleções; fornece métodos para ordenação, busca, manipulação e sincronização de coleções

Embora a interface Map não seja filha direta da interface Collection, ela também é considerada uma coleção devido à sua função.

Generics
Classe (ou interface) genérica ou uma interface que é parametrizada em relação a tipos
O símbolo
<>é chamado de "diamond" ou "diamond operator" (introduzido no Java 7) e é usado no contexto de tipos genéricos em Java para inferir automaticamente o tipo com base no contextoUma variável de tipo pode ser qualquer tipo não primitivo
Sem o Generics, seria necessário realizar um Casting (conversão) do tipo da variável
Os nomes de parâmetros de tipo mais comumente usados são:
E - Element (usado extensivamente pelo Java Collections Framework)
K - Key
N - Number
R - Result
T - Type
V - Value
S, U, V, etc. - 2º, 3º, 4º tipos
Bounds
Estabelece um limite (bound) dos tipos que podem ser passados como parâmetro
Também pode ser utilizado com interfaces, porém necessita na sintaxe
extendsao invés de "implements"Podem haver mais de um bound, com apenas uma classe, pois Java não comporta múltiplas Heranças de classes (listar classes primeiro)
public class Printer <T extends Animals & Serializable> {}
Wildcards
(?) símbolo que representa um tipo desconhecido
Interfaces Funcionais
Single Abstract Method (SAM) Interfaces
São interfaces que possuem um único método abstrato a ser implementado
Podem conter métodos
defaultestatic, mas ainda assim são consideradas interfaces funcionaisForam incluídos no Java 8 junto das expressões Lambda e referências de método para tornar o código mais legível, limpo e direto
A anotação
@FunctionalInterfaceé recomendada para interfaces funcionais, pois além de um indicador, também permite que o compilador gere um erro caso a interface não cumpra com as condições de ser uma interface funcional

Runnable: É frequentemente usado para executar tarefas em threads separados |
run()Callable: Similar ao Runnable, mas tem um retorno de valor e lança exceções |
call()ActionListener: É usado principalmente para manipular eventos de UI |
actionPerformed()Comparator: Usado para comparar dois objetos |
compare()Predicate: Representa uma operação booleana que aceita um argumento e retorna verdadeiro ou falso |
test()Function: Aceita um argumento e retorna um resultado |
apply()Consumer: Aceita um argumento e não retorna nenhum resultado |
accept()Supplier: Não aceita argumentos e retorna um resultado |
get()BiConsumer, BiFunction,BiPredicate, Supplier: Variantes de Consumer, Function, Predicate, e Supplier que aceitam dois argumentos em vez de um
Optional: Usado para evitar a necessidade de verificar explicitamente para null antes de acessar métodos ou campos de um objeto; reduz a chance de NullPointerExceptions (Exceções de Ponteiro Nulo)
Comparable X Comparator
Comparable
Fornece uma única sequência de ordenação de coleções
Afeta a classe original; a classe atual é modificada
Método
compareTo()para ordenar elementosEstá presente no pacote
java.lang.Ordenação dos elementos da lista do tipo Comparable usando o método
Collections.sort(List)
Comparator
Fornece múltiplas sequências de ordenação de coleção (com base em multiplos elemento, como id, nome e preço)
Não afeta a classe original
Método
compare()para ordenar elementosEstá presente no pacote
java.util.Ordenação dos elementos da lista do tipo Comparator usando o método
Collections.sort(List, Comparator)
Stream API
Foca no total da aplicação ao invés de elementos individuais; se preocupa com O QUE precisa ser feito,
Traz princípios da programação funcional, de maneira declarativa
Usado com Collections
Combinado com as Expressões Lambda e Method reference
Um pipeline de Stream, consistem em uma fonte, seguido por zero ou mais operações intermediárias e uma operação terminal.

Operações Intermediárias: Retornam uma nova Stream e permitem encadear várias operações, formando um pipeline de processamento de dados
filter(Predicate<T> predicate)-> Filtra os elementos da Stream com base em um predicado; retorna uma nova Stream contendo apenas os elementos que atendem ao critério do predicado | exemplo: stream.filter(n -> n > 5)map(Function<T, R> mapper)-> Transforma cada elemento da Stream usando a função especificada e retorna uma nova Stream contendo os elementos resultantes | exemplo: stream.map(s -> s.toUpperCase())sorted()-> Classifica os elementos da Stream em ordem natural (se os elementos forem comparáveis) ou de acordo com um comparador fornecido | exemplo: stream.sorted()distinct()-> Remove elementos duplicados da Stream, considerando a implementação do método equals() para comparação | exemplo: stream.distinct()limit(long maxSize)-> Limita o número de elementos da Stream aos maxSize primeiros elementos | exemplo: stream.limit(10)skip(long n)-> Pula os primeiros n elementos da Stream e retorna uma nova Stream sem eles | exemplo: stream.skip(5)
Operações Terminais: São aquelas que encerram o pipeline e produzem um resultado final
forEach(Consumer<T> action)-> Executa uma ação para cada elemento da Stream | exemplo: stream.forEach(System.out : : println)toArray()-> Converte a Stream em um array contendo os elementos da Stream | exemplo: stream.toArray()collect(Collector<T, A, R> collector)-> Coleta os elementos da Stream em uma estrutura de dados específica, como uma lista ou um mapa | exemplo: stream.collect(Collectors.toList())count()-> Retorna o número de elementos na Stream | exemplo: stream.count()anyMatch(Predicate<T> predicate)-> Verifica se algum elemento da Stream atende ao predicado especificado | exemplo: stream.anyMatch(s -> s.startsWith("A"))allMatch(Predicate<T> predicate)-> Verifica se todos os elementos da Stream atendem ao predicado especificado | exemplo: stream.allMatch(n -> n > 0)noneMatch(Predicate<T> predicate)-> Verifica se nenhum elemento da Stream atende ao predicado especificado | exemplo: stream.noneMatch(s -> s.isEmpty())min(Comparator<T> comparator)emax(Comparator<T> comparator)-> Encontra o elemento mínimo e máximo da Stream, respectivamente, de acordo com o comparador fornecido | exemplo: stream.min(Comparator.naturalOrder()) ou stream.max(Comparator.naturalOrder()reduce(T identity, BinaryOperator<T> accumulator)-> Combina os elementos da Stream usando o acumulador especificado e retorna o resultado final | exemplo: stream.reduce(0, (a, b) -> a + b)
Lambdas
Expressões lambda permitem representar interfaces funcionais de forma mais concisa
Função lambda: Função sem declaração; não é necessário colocar um nome, tipo de retorno e modificador de acesso; método é declarado no mesmo lugar em que será usado
Sintaxe: (argumento) --> {corpo}
Method Reference: Tipo especial de expressão lambda para referenciar métodos existentes
Atualizado