Sintaxe Da Linguagem Java
Sintaxe Da Linguagem Java
Declarando variáveis
Usando variáveis
Tipos inteiros
1
Char
O char contém um único caractere Unicode, delimitado por aspas simples:
Tipo booleano
O tipo booleano pode armazenar um valor booleano, que é um valor que só pode
ser verdadeiro ou falso. Esses valores são especificados com as palavras-chave
true ou false:
Operadores aritméticos
Existem quatro operadores aritméticos básicos, bem como o operador de módulo
(%), que é usado para obter o restante da divisão:
Operadores de comparação
Os operadores de comparação comparam dois valores e retornam verdadeiro ou
falso.
2
Operadores lógicos
Operador precedente
String
A classe String em Java é um tipo de dados que pode conter literais de string.
String é um tipo de dados de referência, assim como todos os tipos de dados não
e vem sempre entre aspas duplas.
Concatenação de Strings
O sinal de + é usado para concatenar(unir) duas strings.
Caracteres de escape
3
Comparação de Strings
A maneira de comparar duas strings é usar o método equals da classe String. Se
o operador de igualdade (==) for usado, os endereços de memória serão
comparados:
Classe StringBuffer
As strings em Java são imutáveis. Depois que um objeto String é criado, o
conteúdo não pode ser alterado, a menos que toda a string seja completamente
substituída.
Arrays
Declaração de array
int [] x;
Alocação de matriz
A matriz é alocada com a palavra-chave new, seguida novamente pelo tipo de
dados e um conjunto de colchetes contendo o comprimento da matriz - o número
fixo de elementos que a matriz pode conter.
Atribuição de matriz
Referenciando os valores aos elementos
y [0] = 1;
y [1] = 2;
y [2] = 3;
Ou declarar de forma mais compacta:
4
Para acessar os elementos:
System.out.print (x [0] + x [1] + x [2]); // "6"
Matrizes multidimensionais
Os arrays multidimensionais têm colchetes adicionais. Eles podem ter qualquer
número de dimensões e, para cada dimensão, outro conjunto de colchetes é
adicionado:
Classe ArrayList
Algo importante a se ter em mente sobre os arrays é que seu comprimento é fixo
e não há como alterar seu tamanho.
Condicionais
Declaração If
A instrução if só será executada se a condição entre parênteses for avaliada
como verdadeira. A condição pode incluir qualquer um dos operadores lógicos e
de comparação:
5
Para testar outras condições
Switch
A instrução switch verifica a igualdade entre um inteiro e uma série de rótulos
case. Em seguida, ele executa o caso correspondente. A instrução pode conter
qualquer número de casos e pode terminar com um rótulo default para lidar com
todos os outros casos:
Operador ternário
O operador usa três expressões. Se a primeira for avaliada como verdadeira, a
segunda expressão será retornada, mas se for falsa, a terceira será avaliada e
retornada:
Loops
While
O loop while percorre o bloco de código apenas se a condição especificada for
verdadeira e continuará o loop enquanto a condição permanecer verdadeira.
Do while
O loop do while funciona da mesma maneira que o loop while, exceto que verifica
a condição após o bloco de código. Portanto, ele sempre será executado no bloco
de código pelo menos uma vez:
6
For
O loop for é usado para passar por um bloco de código um número específico de
vezes. Ele usa três parâmetros. O primeiro parâmetro inicializa um contador e é
sempre executado uma vez, antes do loop. O segundo parâmetro contém a condição
para o loop e é verificado antes de cada iteração. O terceiro parâmetro contém
o incremento do contador e é executado ao final de cada iteração:
For-each
O loop “for each” fornece uma maneira fácil de iterar por meio de arrays.
Dentro do for declara-se um “apelido qualquer” do mesmo tipo do array .
Break e continue
A palavra-chave break termina a estrutura de loop e continue ignora o resto da
iteração atual e continua no início da próxima iteração:
Para sair de um loop acima do atual, esse loop deve primeiro ser rotulado
adicionando-se um nome seguido de dois pontos antes dele. Com esse rótulo no
lugar, ele agora pode ser usado como um argumento para a instrução break,
informando de qual loop sair. Isso também funciona com a palavra-chave continue
para pular para a próxima iteração do loop nomeado.
Métodos
Métodos são blocos de código reutilizáveis que só são executados
quando chamados.
Métodos de definição
Você pode criar um método digitando void seguido pelo nome do método, um
conjunto de parênteses e um bloco de código. A palavra-chave void significa que
o método não retornará um valor. A convenção de nomenclatura para métodos é a
7
mesma que para variáveis - um nome descritivo com a primeira palavra em
minúsculas e a primeira letra de qualquer palavra subsequente em maiúscula:
Chamando método
O método anterior simplesmente imprimirá uma mensagem de texto. Para invocá-lo
(chamá-lo) do método principal, uma instância da classe MyApp deve ser criada
primeiro. O operador ponto ( . ) é então usado após o nome da instância para
acessar seus membros, que incluem o método myPrint:
Parâmetros do método
Os parênteses que seguem o nome do método são usados para passar argumentos
para o método. Para fazer isso, os parâmetros correspondentes devem primeiro
ser adicionados à declaração do método na forma de uma lista separada por
vírgulas:
Um método pode ser definido para receber qualquer número de argumentos e eles
podem ter quaisquer tipos de dados. Apenas certifique-se de que o método seja
chamado com os mesmos tipos e número de argumentos:
8
Return
instrução de salto que faz com que o método saia e retorne o valor especificado
para o local onde o método foi chamado. Por exemplo, o método anterior pode ser
passado como um argumento para o método getString porque o método é avaliado
como uma string:
Sobrecarga de método
É possível declarar vários métodos com o mesmo nome, desde que os parâmetros
variem em tipo ou número . É um recurso poderoso que permite a um método lidar
com uma variedade deargumentos sem que o programador precise estar ciente do
uso de métodos diferentes:
Passando argumentos
Em Java, todos os parâmetros do método são passados por valor. Na verdade, eles
não podem ser passados por referência. Para tipos de dados de valor (tipos
primitivos), isso significa que apenas uma cópia local da variável é alterada
dentro do método, portanto, a alteração não afetará a variável original. Para
tipos de dados de referência (classes, interfaces e arrays), isso significa que
apenas uma cópia do endereço de memória é passada para o método. Portanto, se
todo o objeto é substituído, a alteração não se propagará de volta para o
chamador, mas as alterações no objeto afetarão o original, pois a cópia aponta
para o mesmo local da memória:
9
Classes
Uma classe é um modelo usado para criar objetos. As classes são compostas por
membros, sendo os dois principais campos e métodos. Os campos são variáveis que
mantêm o estado do objeto, enquanto os métodos definem o que o objeto pode
fazer - os chamados comportamentos do objeto:
Criação de objeto
Para acessar um campo ou método de classe não estático de fora da classe de
definição, um objeto da classe deve primeiro ser criado. Isso é feito usando a
palavra-chave new, que criará um novo objeto na memória do sistema:
Os membros deste objeto agora podem ser usados através o operador ponto( . )
após o nome da instância:
Construtor
Uma classe pode ter um construtor , um tipo especial de método usado para
instanciar (construir) o objeto. Ela sempre tem o mesmo nome da classe e não
tem um tipo de retorno, pois retorna implicitamente uma nova instância da
classe. Para ser acessível a partir de outra classe que não esteja em seu
10
pacote, ele precisa ser declarado com o modificador de acesso public Quando uma
nova instância da classe MyRectangle é criada usando a nova sintaxe, o método
construtor é chamado, que no exemplo a seguir define os campos com os valores
padrão especificados:
O construtor pode ter uma lista de parâmetros, como qualquer outro método.
Conforme mostrado no código a seguir, isso pode ser usado para fazer com que os
valores iniciais dos campos dependam dos parâmetros passados quando o objeto é
criado:
Palavra this
Dentro do construtor, assim como em outros métodos pertencentes ao objeto, uma
palavra-chave especial chamada this pode ser usada. Esta palavra-chave é uma
referência para a instância atual da classe. Se, por exemplo, os parâmetros do
construtor têm os mesmos nomes que os campos correspondentes, então os campos
ainda podem ser acessados usando a palavra-chave this.
Sobrecarga do construtor
Para oferecer suporte a listas de parâmetros diferentes, o construtor pode ser
sobrecarregado. No exemplo a seguir, se a classe for instanciada sem nenhum
parâmetro, os campos serão atribuídos aos valores padrão especificados. Com um
parâmetro, ambos os campos serão definidos com o valor fornecido e, com dois
parâmetros, cada campo receberá um valor separado. A tentativa de criar um
objeto com o número errado de argumentos ou com tipos de dados incorretos
11
resultará em um erro em tempo de compilação, assim como com qualquer outro
método:
Encadeamento de construtor
Você também pode usar a palavra-chave this para chamar um construtor de outro.
Conhecido como encadeamento de construtor , permite maior reutilização de
código. Observe que a palavra-chave aparece como uma chamada de método e deve
estar na primeira linha do construtor:
Construtor padrão
É possível criar uma classe mesmo que nenhum construtor seja definido. Isso
ocorre porque o compilador criará automaticamente um construtor sem parâmetros
padrão:
Null
A constante null é utilizada para representar um objeto não inicializada. Ele
só pode ser atribuído a objetos e não a variáveis de tipos primitivos. O
operador igual (==) pode ser usado para testar se um objeto é nulo:
12
Valores padrão
O valor padrão de um objeto é null. Para tipos de dados primitivos, os valores
padrão são os seguintes:
Tipos numéricos = 0,
char tem o caractere Unicode para zero (\0000)
booleano é falso.
Estatic
A palavra-chave static é usada para criar campos e métodos que podem ser
acessados sem a necessidade de criar uma instância da classe. Membros estáticos
(classe) existem apenas em uma cópia, que pertence à própria classe, enquanto
membros de instância (não estáticos) são criados como novas cópias para cada
novo objeto. Isso significa que os métodos estáticos não podem usar membros de
instância porque esses métodos não fazem parte de uma instância. Por outro
lado, os métodos de instância podem usar membros estáticos e de instância:
13
Métodos estáticos
A vantagem dos membros estáticos é que eles podem ser usados por outras classes
sem a necessidade de criar uma instância da classe. Os campos devem, portanto,
ser declarados estáticos quando apenas uma única instância da variável é
necessária. Os métodos devem ser declarados estáticos se executarem uma função
genérica independente de quaisquer variáveis de instância. Um bom exemplo disso
é a classe Math, que contém apenas métodos e campos estáticos:
Campos estáticos
Os campos estáticos têm a vantagem de persistir durante toda a vida útil do
aplicativo. Isso significa que eles podem ser usados, por exemplo, para
registrar o número de vezes que um método foi chamado em todas as instâncias da
classe. O valor inicial para um campo estático será definido apenas uma vez,
algum tempo antes da classe ou campo ser usado:
14
Herança
A herança permite que uma classe adquira os membros de outra classe. No exemplo
a seguir, Apple herda de Fruit. Isso é especificado com a palavra-chave
extends. A fruta então se torna a superclasse da Maçã, que por sua vez se torna
uma subclasse de Frutas. Além de seus próprios membros, a Apple ganha todos os
membros acessíveis no Fruit, exceto para seus construtores:
Object
Uma classe em Java pode herdar apenas de uma superclasse e, se nenhuma classe
for especificada, ela herdará implicitamente de Object. Portanto, Object é a
classe raiz de todas as classes:
Upcasting
Conceitualmente, uma subclasse é uma especialização da superclasse. Isso
significa que a Maçã é um tipo de Fruta, bem como um Objeto, e pode, portanto,
ser usada em qualquer lugar onde uma Fruta ou Objeto seja esperado. Por
exemplo, se uma instância da Apple for criada, ela pode ser upcast para Fruit
porque a subclasse contém tudo na superclasse:
A maçã é então vista como uma fruta, então apenas os membros da fruta podem ser
acessados:
15
Downcasting
Quando a classe é reduzida de volta para um Apple, os campos que são
específicos para o Apple terão sido preservados. Isso porque a fruta continha
apenas a maçã - ela não a converteu. O downcast deve ser feito explicitamente
usando o formato Java Cast porque o downcast de um objeto Fruit real em um
Apple não é permitido:
Operador de instância
Como precaução de segurança, você pode testar para ver se um objeto pode ser
lançado em uma classe específica usando o operador instanceof. Este operador
retorna verdadeiro se o objeto do lado esquerdo puder ser convertido no tipo do
lado direito sem causar uma exceção:
Overriding
Um membro em uma subclasse pode redefinir um membro em sua superclasse. Isso
geralmente é feito para dar novas implementações aos métodos de instância.
Substituindo membros
No exemplo a seguir, o método getArea de Rectangle é substituído no Triangle,
redeclarando-o com a mesma assinatura de método. A assinatura inclui o nome,
parâmetros e tipo de retorno do método. No entanto, o nível de acesso pode ser
alterado para permitir mais acesso do que o método que está sendo substituído:
Override annotation
Para mostrar que essa substituição foi intencional, a anotação @Override deve
ser colocada antes do método. Esta anotação foi adicionada no Java 5 para
evitar modificações acidentais:
16
Invocar o método getArea de uma instância de Triangle chamará a versão de
Triangle do método:
Hiding members
Se um método de classe chamado newArea for adicionado a Rectangle e redefinido
em Triangle, a versão de Triangle do método ocultará apenas a implementação de
Rectangle. Por causa disso, a anotação @Override não é usada:
17
Chamando construtor pai
Outro lugar onde a palavra-chave super pode ser usada é na primeira linha de um
construtor. Lá, ele pode realizar uma chamada de método que invoca o construtor
da superclasse:
Se a primeira linha de um construtor não for uma chamada para outro construtor,
o compilador Java adicionará automaticamente uma chamada para o construtor sem
parâmetros da superclasse. Isso garante que todas as classes ancestrais sejam
construídas corretamente:
Packages e Import
Os pacotes são usados para evitar conflitos de nomenclatura e organizar
arquivos de código em diretórios diferentes. Para atribuir um arquivo de código
a um pacote – por exemplo, mypackage - ele deve ser movido para uma pasta com
esse nome, no diretório do projeto. Além disso, o arquivo deve especificar a
qual pacote ele pertence usando a palavra-chave do pacote seguida pelo nome do
pacote (e caminho).
Pode haver apenas uma instrução de pacote em cada arquivo de origem e deve ser
a primeira linha do código, exceto para quaisquer comentários. Observe que a
convenção de nomenclatura para pacotes é toda em letras minúsculas:
Acessando pacotes
Para ilustrar como acessar os membros do pacote, um arquivo chamado
MyClass.java é colocado na pasta sub \ mypackage no diretório de origem do
projeto. O arquivo contém uma única classe pública chamada MyClass:
18
Além de importar uma classe específica, todos os tipos (classes ou interfaces)
dentro de um pacote podem ser importados usando um asterisco (*). Observe que
isso não importa nenhum sub pacote:
Níveis de Acesso
Existem quatro níveis de acesso disponíveis em Java: públic, protect, private e
package-private.
Private access
O nível de acesso mais restritivo é privado. Membros com este nível só podem
ser usados dentro da classe envolvente:
19
Acesso protegido
Os membros protegidos podem ser acessados em subclasses e no pacote que o
contém. Observe que o significado de protected em Java é diferente de outras
linguagens, como C ++ e C #, onde os membros protegidos são acessíveis apenas a
partir de subclasses e da classe contida:
Acesso público
O modificador public dá acesso irrestrito de qualquer lugar onde o membro pode
ser referenciado:
20
Acesso de nível superior
Os membros declarados diretamente no pacote ( package-private) podem escolher
apenas entre o acesso do pacote privado e público. Por exemplo, uma classe de
nível superior sem um modificador de acesso terá como padrão o pacote privado.
Essa classe só estará acessível dentro do pacote que o contém. Em contraste,
uma classe de nível superior explicitamente declarada como pública pode ser
alcançada de outros pacotes também:
Constantes
Uma variável em Java pode ser transformada em uma constante adicionando a
palavra-chave final antes do tipo de dados. Este modificador significa que a
variável não pode ser reatribuída depois de definida e qualquer tentativa de
fazer isso resultará em um erro de tempo de compilação.
Constantes locais
Uma constante local sempre deve ser inicializada ao mesmo tempo em que é
declarada. A convenção de nomenclatura Java para constantes é usar todas as
letras maiúsculas e separar as palavras com sublinhados:
Campos constantes
Variáveis de classe e instância também podem ser declaradas como finais:
21
Em contraste com as constantes locais, os campos de constantes não precisam ser
atribuídos na declaração. Um campo de instância constante pode opcionalmente
ser atribuído em um construtor, e um campo estático constante pode ser
atribuído usando um bloco de inicialização estático. Essas atribuições
alternativas podem ser úteis se o valor da constante precisar ser calculado e
não couber em uma única linha de código:
22
Diretriz constante
Em geral, é uma boa ideia sempre declarar variáveis como final e campos
constantes como final estático, se eles não precisarem ser reatribuídos. Isso
garante que os campos e variáveis não sejam alterados por engano em qualquer
lugar do programa, o que por sua vez ajuda a prevenir bugs.
Interface
O tipo de interface é usado para especificar métodos que as classes que
implementam a interface devem definir. Esses métodos são criados com a palavra-
chave interface seguida por um nome e um bloco de código. A convenção de
nomenclatura é a mesma das classes: a primeira letra de cada palavra é
maiúscula:
Quando uma interface não está aninhada em outro tipo, seu nível de acesso pode
ser privado de pacote ou público, assim como qualquer outro membro de nível
superior .
Membros da interface
O bloco de código para uma interface pode, em primeiro lugar, conter
assinaturas para métodos de instância. Esses métodos não têm implementações. Em
vez disso, seus corpos são substituídos por ponto e vírgula. Os membros da
interface têm acesso público por padrão, então este modificador pode ser
deixado de fora:
O segundo membro que uma interface pode conter são constantes. Qualquer campo
criado em uma interface será declarado implicitamente como final estático,
portanto, esses modificadores também podem ser deixados de fora:
Exemplo de interface
O exemplo a seguir mostra uma interface chamada Comparable, que tem um único
método denominado compare:
23
A classe a seguir implementa essa interface usando a palavra-chave implements
após o nome da classe. Por convenção, a cláusula implements é colocada após a
cláusula extends se a classe tiver uma. Observe que embora uma classe só pode
herdar de uma superclasse, pode implementar qualquer número de interfaces,
especificando-as em uma lista separada por vírgulas:
Interface de funcionalidade
Comparable demonstra o primeiro uso de interfaces, que é definir uma
funcionalidade específica que as classes podem compartilhar. Torna possível
usar os membros da interface sem ter que saber o tipo real de uma classe. Para
ilustrar, o próximo exemplo mostra um método simples que pega dois objetos
Comparable e retorna o maior deles. Este método funcionará para qualquer classe
que implemente a interface Comparable porque o método usa apenas a
funcionalidade exposta por meio dessa interface:
Interface de classe
Uma segunda maneira de usar uma interface é fornecer uma interface real para
uma classe, por meio da qual a classe pode ser usada. O exemplo a seguir define
uma interface para MyClass chamada MyInterface. Essa interface inclui apenas a
funcionalidade de que os programadores que usam MyClass podem precisar:
24
O tipo de interface é então usado para manter a classe de implementação, de
forma que a classe só seja vista por meio desta interface:
Essa abstração oferece dois benefícios. Primeiro, torna mais fácil para outros
programadores usarem a classe porque agora eles só têm acesso aos métodos que
são relevantes. Em segundo lugar, torna a classe mais flexível porque sua
implementação pode mudar, sem ser perceptível por outros programadores usando a
classe, desde que a interface seja seguida.
Classes de interface
Conforme mencionado, uma interface pode conter tipos aninhados, como classes.
Em contraste com os métodos, esses tipos são implementados dentro da interface.
Isso pode, por exemplo, ser usado para fornecer uma classe que contém métodos
estáticos úteis para implementar classes. Esses tipos aninhados são visíveis
apenas para classes que implementam a interface, e não para objetos dessas
classes:
Métodos de interface
Esse método é especificado usando a palavra-chave padrão e pode incluir uma
implementação dentro da interface:
25
Um método padrão será usado, a menos que seja substituído por uma classe de
implementação. Isso fornece uma maneira compatível com versões anteriores de
adicionar novos métodos a uma interface sem quebrar as classes existentes que
usam a interface:
A partir do Java 9, os métodos estáticos podem ter acesso privado. Isso permite
que métodos padrão longos sejam divididos entre métodos privados, o que permite
menos
duplicação de código:
26
Abstract
Uma classe abstrata fornece uma implementação parcial sobre a qual outras
classes podem construir. Quando uma classe é declarada abstrata, significa que
ela pode conter métodos incompletos que devem ser implementados em subclasses,
além dos membros normais da classe. Esses métodos não foram implementados e
especificam apenas suas assinaturas, enquanto seus corpos são substituídos por
ponto-e-vírgulas:
Uma classe abstrata não pode ser instanciada, mas pode ser usada para conter
instâncias de suas subclasses:
Mesmo que uma classe abstrata não possa ser instanciada, ela pode ter
construtores, que podem ser chamados a partir dos construtores da subclasse
usando a palavra-chave super:
27
Classes abstratas e interfaces
As classes abstratas são semelhantes às interfaces de várias maneiras. Ambos
podem definir assinaturas de método que as subclasses devem implementar e
nenhum deles pode ser instanciado. Uma diferença importante é que uma classe
abstrata pode conter qualquer membro abstrato ou não abstrato , enquanto uma
interface é limitada a membros abstratos, tipos aninhados e constantes
estáticas, bem como métodos estáticos e métodos padrão a partir do Java 8.
Outra diferença é que uma classe pode implementar qualquer número de
interfaces, mas apenas herdar de uma classe, abstrata ou não.
Uma interface é usada para definir uma funcionalidade específica que uma classe
pode ter ou para fornecer uma interface para outros desenvolvedores que usam
uma classe. Em contraste, uma classe abstrata é usada para fornecer uma
implementação parcial da classe, deixando que as subclasses a completem. Isso é
útil quando as subclasses têm alguma funcionalidade em comum, mas também têm
alguma funcionalidade que deve ser implementada de maneira diferente para cada
subclasse.
Enum
Uma enumeração , ou enum , é um tipo que consiste em uma lista fixa de
constantes nomeadas. Para criar um, a palavra chave enum é usada seguida por um
nome e um bloco de código contendo uma lista separada por vírgulas de elementos
constantes. O nível de acesso para um enum é o mesmo que para uma classe.
Pacote privado por padrão, mas também pode ser definido como público se for
declarado em um arquivo com o mesmo nome. Assim como acontece com as classes,
um enum pode estar contido em uma classe, onde pode ser definido para qualquer
nível de acesso:
Um objeto do tipo enum que acabou de ser mostrado pode conter qualquer uma das
quatro constantes definidas. As constantes enum são acessadas como se fossem
campos estáticos de uma classe:
A instrução switch fornece um bom exemplo de quando uma enumeração pode ser
útil. Comparado ao uso de constantes comuns, um enum tem a vantagem de permitir
que o programador especifique claramente quais valores constantes são
permitidos. Isso fornece segurança de tipo em tempo de compilação .
Observe que ao usar um enum em uma instrução switch, os rótulos de maiúsculas e
minúsculas não são qualificados com o nome do enum:
Classe Enum
Em Java, o tipo enum é mais poderoso do que suas contrapartes em outras
linguagens, como C ++ ou C #. Essencialmente um tipo especial de classe, pode
incluir qualquer coisa que uma classe possa incluir. Para adicionar um membro
28
da classe, a lista de constantes deve terminar com um ponto-e-vírgula e o
membro deve ser declarado após as constantes. No exemplo a seguir, um inteiro é
adicionado ao enum, que manterá a velocidade real que os elementos
representam:
Outra diferença que os tipos enum têm quando comparados às classes regulares é
que eles se estendem implicitamente da classe java.lang.Enum. Além dos membros
herdados dessa classe, o compilador também adicionará automaticamente dois
métodos estáticos à enumeração, a saber, values e valueof. O método de valores
retorna uma matriz dos elementos constantes declarados no enum, e valueof
retorna a constante de enum do nome do enum especificado:
Manipulação de exceção
O tratamento de exceções permite que os programadores lidem com situações
inesperadas que podem ocorrer em seus programas. Por exemplo, a classe
FileReader no pacote java.io é usada para abrir um arquivo. A criação de uma
instância desta classe fará com que o Netbeans forneça um lembrete de que o
construtor da classe pode lançar uma FileNotFoundException. A tentativa de
executar o programa também fará com que o compilador aponte o seguinte:
29
Try-catch
Para lidar com esse erro de tempo de compilação, a exceção deve ser detectada
usando uma instrução try-catch . Esta instrução consiste em um bloco try
contendo o código que pode causar as exceções e uma ou mais cláusulas catch. Se
o bloco try for executado com sucesso, o programa continuará em execução após a
instrução try-catch , mas se ocorrer uma exceção, a execução será então passada
para o primeiro bloco catch capaz de lidar com esse tipo de exceção:
Catch block
No exemplo anterior, o bloco catch é definido apenas para manipular a
FileNotFoundException. Se o código no bloco try pudesse lançar mais tipos de
exceções, e todos eles devessem ser tratados da mesma maneira, uma exceção mais
geral pode ser capturada, como a própria classe Exception da qual todas as
exceções derivam. Essa cláusula catch seria então capaz de lidar com todas as
exceções que herdam dessa classe, incluindo FileNotFoundException. Tenha em
mente que uma exceção mais geral precisa ser capturada após uma exceção mais
específica. A cláusula catch deve sempre definir um objeto de exceção. Este
objeto pode ser usado para obter mais informações sobre a exceção, como uma
descrição da exceção usando o método getMessage:
Finally block
Como a última cláusula em uma instrução try-catch , um bloco finally pode ser
adicionado. Este bloco é usado para limpar recursos alocados no bloco try e
sempre será executado independentemente de haver ou não uma exceção. Neste
exemplo, o arquivo aberto no bloco try deve ser fechado, mas somente se foi
aberto com sucesso. Para poder acessar o objeto FileReader da cláusula finally,
30
ele deve ser declarado fora do bloco try. Além disso, como o método close
também pode lançar uma exceção, o método precisa ser cercado por outro bloco
try-catch . Lembre-se de que, se você esquecer de fechar um objeto de recurso,
o coletor de lixo do Java acabará fechando o recurso para você, mas fechá-lo é
uma boa prática de programação:
31
Throwing exceptions
Quando ocorre uma situação da qual um método não pode se recuperar, ele pode
gerar sua própria exceção para sinalizar ao chamador que o método falhou. Ele
faz isso usando a palavra-chave throw seguida por uma nova instância de um tipo
Throwable:
Hierarquia de exceção
Exceções, como quase tudo em Java, são classes que existem em uma hierarquia.
Na raiz dessa hierarquia (abaixo de Object) está a classe Throwable, e todos os
descendentes dessa classe podem ser lançados e pegos. Herdadas de Throwable
estão as classes Error e Exception. As classes descendentes de Error são usadas
para indicar exceções não recuperáveis , como OutOfMemoryError. Eles não são
verificados porque, uma vez ocorridos, é improvável que o programador possa
fazer algo a respeito, mesmo que sejam pegos. Descendentes da Exceção estão as
RuntimeExceptions, que também estão desmarcadas. Essas são exceções que podem
ocorrer em quase qualquer código e, portanto, seria complicado capturá-las e
especificá-las. Por exemplo, uma divisão por zero lançará uma
ArithmeticException, mas cercar cada operação de divisão com um try-catch seria
incômodo. Também há alguma sobrecarga associada à verificação de exceções, e o
custo de verificar essas exceções supera o benefício de detectá-las. Os outros
descendentes de Exception, aqueles que não herdam de RuntimeExceptions, são
todos verificados. Essas são exceçõesque podem ser recuperadas e que devem ser
capturadas e especificadas.
Boxing e Unboxing
Colocar uma variável primitiva em um objeto é conhecido como boxing . O boxing
permite que o primitivo seja usado onde os objetos são necessários. Para este
32
propósito, Java fornece classes de wrapper para implementar boxing para cada
tipo primitivo
Byte, Short, Integer, Long, Float, Double, Character e Boolean. Um objeto
Integer, por exemplo, pode conter uma variável do tipo int:
Observe que este é apenas um açúcar sintático projetado para tornar o código
mais fácil de ler. O compilador adicionará o código necessário para encaixotar
e desencaixotar os primitivos para você, usando os métodos valueOf e intValue:
33
Lembre-se de que as conversões entre objetos primitivos e wrapper devem ser
mantidas baixas se a velocidade for importante. Há uma penalidade de desempenho
herdada
associada a qualquer operação de boxing e unboxing.
Generics
Genérico se refere ao uso de parâmetros de tipo, que fornecem uma maneira de
definir métodos, classes e interfaces que podem operar com diferentes tipos de
dados. Os benefícios dos genéricos são que eles fornecem segurança de tipo em
tempo de compilação e eliminam a necessidade da maioria das conversões de tipo.
Classes genéricas
As classes genéricas permitem que os membros da classe usem parâmetros de tipo.
Tal classe é definida adicionando uma seção de parâmetro de tipo após o nome da
classe, que contém um parâmetro de tipo entre colchetes angulares. A convenção
de nomenclatura para parâmetros de tipo é que eles devem consistir em uma única
letra maiúscula. Normalmente, a letra T para tipo é usada. O exemplo a seguir
define uma classe de contêiner genérica que pode conter um único elemento do
tipo genérico:
Como alternativa, a partir do Java 7, uma classe genérica pode ser instanciada
com um conjunto vazio de parâmetros de tipo. Esse tipo de instanciação é
possível, desde que o compilador possa inferir (determinar) os parâmetros de
tipo do contexto:
Métodos genéricos
Um método pode se tornar genérico, declarando-o com uma seção de parâmetro de
tipo antes do tipo de retorno do método. O parâmetro type pode ser usado como
34
qualquer outro tipo dentro do método. Você também pode usá-lo para o tipo de
retorno do método, na cláusula throws e para seus tipos de parâmetro. O próximo
exemplo mostra um método de classe genérico que aceita um parâmetro de matriz
genérico, o conteúdo do qual é impresso:
A classe mostrada acima não é genérica. Os métodos podem ser declarados como
genéricos, independentemente de a classe ou interface envolvente ser ou não
genérica. O mesmo é verdade para construtores.
Na maioria dos casos, o compilador Java pode inferir o argumento de tipo de uma
chamada de método genérico, portanto, ele não precisa ser incluído. Mas se esse
não for o caso, o argumento de tipo precisará ser especificado explicitamente:
Interfaces genéricas
As interfaces que são declaradas com parâmetros de tipo tornam-se interfaces
genéricas. As interfaces genéricas têm os mesmos dois propósitos que as
interfaces regulares: elas são criadas para expor membros de uma classe que
será usada por outras classes ou para forçar uma classe a implementar uma
funcionalidade. Quando uma interface genérica é implementada, o argumento de
tipo deve ser especificado. A interface genérica pode ser implementada por
classes genéricas e não genéricas :
35
Parâmetros de tipo genérico
O argumento de tipo passado para um genérico pode ser um tipo de classe, tipo
de interface ou outro parâmetro de tipo, mas não pode ser um tipo primitivo. Os
genéricos podem ter mais de um parâmetro de tipo definido, adicionando mais
deles entre os colchetes angulares em uma lista separada por vírgulas.
Lembre-se de que cada parâmetro entre colchetes deve ser único:
36
O processo de remoção de informações de tipo do código genérico é conhecido
como eliminação de tipo . Por exemplo, MyBox <Integer> seria reduzido a MyBox,
que é chamado de tipo bruto . Esta etapa é executada para manter a
compatibilidade com versões anteriores com o código escrito antes que os
genéricos se tornassem parte da linguagem em Java 5.
37
métodos Object. No entanto, ao aplicar uma superclasse ou limite de interface,
os membros acessíveis desse tipo também ficarão disponíveis:
38
Com a alternativa genérica, apenas o argumento de tipo especificado será
permitido na coleção ArrayList. Além disso, os valores obtidos da coleção não
precisam ser convertidos para o tipo correto, porque o compilador cuida disso.
Expressões Lambda
Java 8 introduziu a expressão lambda , que fornece uma maneira concisa de
representar um método usando uma expressão. Uma expressão lambda consiste em
três partes: uma lista de argumentos, o operador de seta (->) e um corpo. O
lambda a seguir recebe dois argumentos inteiros e retorna a soma deles:
Objetos lambda
Uma expressão lambda é uma representação de uma interface funcional , que é uma
interface que define um único método abstrato. Ele pode, portanto, ser
vinculado a um objeto de tal interface, desde que seu método funcional tenha
uma assinatura correspondente:
Parâmetros Lambda
Ao contrário dos métodos, as expressões lambda não pertencem a nenhuma classe.
Eles são objetos em si mesmos, pois são instâncias de interfaces funcionais. Um
benefício disso é que eles fornecem uma maneira conveniente de passar a
funcionalidade como um argumento para outro método. No exemplo a seguir, é
usada a interface Runnable, que possui um método funcional que não aceita
parâmetros e não retorna valor. Esta interface pertence a java.lang e seu
método abstrato é denominado run:
Você também pode obter essa funcionalidade definindo uma classe interna anônima
(sem nome), mas essa abordagem é consideravelmente mais detalhada do que a
expressão lambda:
Uma expressão lambda pode capturar variáveis de seu contexto, desde que a
variável referenciada seja final ou efetivamente final (apenas atribuída uma
vez). No próximo exemplo, a interface funcional do Consumidor é usada, que
representa uma função que aceita um parâmetro e não retorna nenhum valor:
40
Nos bastidores, o compilador irá instanciar uma classe anônima contendo um
único método para representar uma expressão lambda. Isso permite que lambdas
sejam totalmente compatíveis com versões anteriores do Java Runtime
Environment.
41
Inicialização tardia de campos estáticos em Java é realizada através de blocos de inicialização estáticos. Estes blocos são definidos no nível da classe e são executados uma única vez, no momento da inicialização do campo estático, permitindo executar lógica complexa necessitando múltiplas linhas antes que os campos estáticos sejam efetivamente utilizados. Tal abordagem garante que todos os preparativos necessários ao uso dos campos estáticos são realizados antes de sua utilização real .
Generics in Java improve type safety by allowing classes, interfaces, and methods to operate on specified types while preventing type cast exceptions at runtime. By specifying a type parameter, generics enable compile-time checks, ensuring that only the allowed types are used with the generic class or method. Unlike the use of Object, which requires casting and is prone to ClassCastException, generics eliminate most runtime errors related to type mismatches. However, generics are limited because they only exist at compile-time due to type erasure—generic type arguments are erased and replaced with their bounds or Object, limiting their use for runtime type creation and checks like 'instanceof' .
Lambda expressions, introduced in Java 8, enhance functional programming by allowing the implementation of functional interfaces in a more concise and readable manner. They consist of three core components: a list of formal parameters, the '->' arrow token, and a body (which can be an expression or a block of statements). Lambdas enable the simplification of code by removing boilerplate code typically associated with anonymous inner classes. By facilitating a functional style, they enable parallel and asynchronous programming constructs that align with modern programming requirements .
A palavra-chave 'default' é usada em métodos de interface para fornecer implementações padrão, permitindo que novas funcionalidades sejam adicionadas a interfaces sem obrigar as classes que as implementam a definir essas funcionalidades. Isso oferece uma maneira flexível e prática de evoluir interfaces e fornecer código reutilizável, pois as classes existentes não são obrigadas a alterar sua implementação quando uma interface é estendida com novos métodos .
Para declarar um método genérico em uma classe não genérica, você insere uma seção de parâmetro de tipo imediatamente antes do tipo de retorno do método. Esses parâmetros de tipo podem ser usados dentro do método, como em declarações de variáveis ou retorno do método. Métodos genéricos são invocados como métodos regulares; na maioria dos casos, o compilador Java infere o argumento do tipo. No entanto, se necessário, o argumento de tipo deve ser especificado explicitamente .
Static initialization blocks in Java are used to perform complex static variable initialization, executing only once when the class is first loaded into memory, unlike constructors which run each time an instance is created. These blocks are useful for setting up class-level resources that need initialization prior to any instance or static method usage. They provide an advantage over constructors by handling scenarios where initialization spans multiple steps or dependencies, thus ensuring that variables are ready for use the first time a class or its static context is accessed .
O operador 'instanceof' é útil para garantir a segurança ao realizar downcasting, verificando se um objeto é instância de uma classe específica antes de o lançar, para evitar exceções de tempo de execução. É particularmente indicado quando há necessidade de acessar métodos ou campos que pertencem à subclasse após ter trabalhado com a superclasse, certificando-se de que o objeto realmente suporta tal operação .
A palavra-chave 'this' é utilizada dentro de construtores para referenciar a instância atual da classe. Isso é especialmente útil quando os parâmetros de um construtor têm os mesmos nomes que os campos da classe, permitindo acessar os campos através de 'this'. Além disso, 'this' pode ser usado para chamar outros construtores da mesma classe, uma técnica conhecida como encadeamento de construtor, onde deve aparecer como uma chamada de método na primeira linha do construtor .
Genéricos em Java não suportam diretamente tipos primitivos, apenas tipos de classe. Isso é devido ao fato de que genéricos são implementados através de eliminação de tipo, funcionando apenas com referências de objeto. Para contornar essa limitação, tipos primitivos são autoboxed em suas contrapartes de classe, como int para Integer. Essa conversão permite que eles sejam usados em contextos de genérico, embora introduza alguma sobrecarga .
Interfaces genéricas permitem a criação de interfaces que podem operar sobre qualquer tipo de dado sem depender de tipos específicos, promovendo a reutilização e a flexibilidade do código. Além disso, o uso de genéricos proporciona segurança de tipo em tempo de compilação, reduzindo erros que só seriam detectados em tempo de execução ao usar tipos não genéricos, como Object .