https://github.com/isaacalves7/java
☕ It's a repository of Java programming language and his content.
https://github.com/isaacalves7/java
android gradle java java-programming javaee javafx jboss jpa jsf jsp junit5 kotlin maven oci oracle-database oracle-db primefaces spring spring-boot
Last synced: about 1 month ago
JSON representation
☕ It's a repository of Java programming language and his content.
- Host: GitHub
- URL: https://github.com/isaacalves7/java
- Owner: IsaacAlves7
- Created: 2020-08-27T01:34:11.000Z (over 4 years ago)
- Default Branch: master
- Last Pushed: 2025-03-22T19:02:14.000Z (about 1 month ago)
- Last Synced: 2025-03-22T20:18:32.113Z (about 1 month ago)
- Topics: android, gradle, java, java-programming, javaee, javafx, jboss, jpa, jsf, jsp, junit5, kotlin, maven, oci, oracle-database, oracle-db, primefaces, spring, spring-boot
- Language: Java
- Homepage:
- Size: 1.17 MB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# It's a repository of Java language ☕
> ☕ **Preparação**: Para este conteúdo, o aluno deverá dispor de um computador com acesso à internet, um web browser com suporte a HTML 5 (Google Chrome, Mozilla Firefox, Microsoft Edge, Safari, Opera etc.), um editor de texto ou IDE (VSCode etc.) e o software JDK, com a versão mais recente, instalado na sua máquina local.
# ☕ The History of Java language
**Java** é o ambiente computacional, ou plataforma, criada pela empresa estadunidense Sun Microsystems, e vendida para a Oracle depois de alguns anos. A plataforma permite desenvolver programas utilizando a linguagem de programação Java. A tecnologia Java foi desenvolvida na década de 1990, a partir de um projeto pessoal de um funcionário da Sun Microsystems. A ideia inicial estava ligada à criação de uma linguagem de programação que pudesse ser utilizada em diferentes sistemas, alterando o paradigma de que uma aplicação só poderia ser desenvolvida para uso em um único ambiente de hardware e sistema operacional, como era bastante comum na época.
As grandes empresas desenvolviam suas aplicações voltadas para seu ambiente de hardware e software (sistema operacional - SO), e estas aplicações não eram capazes de serem executadas em diferentes plataformas, principalmente de outros fabricantes. Se analisarmos a linguagem C, criada junto com o sistema operacional UNIX, temos uma biblioteca muito vasta de funções, mas poucas são consideradas padrão para atender a diferentes sistemas; e, mesmo assim, uma aplicação compilada em um sistema operacional (ambiente) não pode ser executada em outro.
A linguagem Java rompeu este paradigma e passou a permitir que uma aplicação desenvolvida em um ambiente - hardware + software (SO) - possa ser executada em outro sem necessidade de qualquer outro procedimento. A Sun Microsystems, ao tomar conhecimento desta ideia, deu total apoio ao seu desenvolvimento e criou um grupo com 13 membros, liderado por James Gosling, que passaram a trabalhar exclusivamente neste projeto. A equipe foi batizada de “Green Team” e o grupo passou a trabalhar em um conjunto de escritórios fora das dependências físicas da Sun, e sem qualquer tipo de comunicação com a matriz, durante 18 meses para a concretização desta ideia.
Com a tecnologia Java, as aplicações passaram a ser portáveis de um sistema para o outro, sem nenhuma necessidade de alteração. Por isso, afirmamos que a portabilidade é uma das mais importantes características da linguagem Java.
Ainda naquela época, o grupo já havia antecipado uma nova onda na computação, na convergência entre dispositivos controlados digitalmente e computadores. Hoje em dia, percebemos bem isso quando analisamos um smartphone, um dispositivo digital que possui inúmeras funções de computadores; entre elas, podemos destacar a execução de aplicativos. Inicialmente, a linguagem foi batizada de **Oak**, pois o grupo tinha como vista da janela do escritório *um carvalho*. Posteriormente, a linguagem foi rebatizada como **Java**, em função do *gosto do grupo pelo tipo de café*. Por isso, temos como ícone da linguagem uma xícara de café com sua fumaça característica.
A linguagem é muito poderosa para o desenvolvimento de aplicações, seja para o desenvolvimento de aplicações menos sofisticadas ou para uso em dispositivos menos complexos que computadores, conhecidos como dispositivos inteligentes, tais como cafeteiras, micro-ondas, geladeiras e uma gama de outros dispositivos que possam ser controlados por software. A linguagem ainda é muito eficiente no desenvolvimento de sistemas de entretenimento doméstico, dando suporte a streaming de vídeo e televisão digital, que ainda não era tão desenvolvida na época.
A tecnologia Java permite ainda o desenvolvimento de todos os tipos de aplicações, indo do mais simples controle de um eletrodoméstico, passando por aplicações domésticas, comerciais, de automação, até o desenvolvimento de aplicações mais complexas, com comunicação de dados e aplicações para supercomputadores.
A linguagem Java teve início ao incorporar a tecnologia Java ao navegador de internet *Netscape navigator*, em sua versão de 1995. A tecnologia ganhou a aceitação do mercado e dos desenvolvedores, sendo uma das mais importantes linguagens de programação para o desenvolvimento de sistemas. São dezenas de milhões de desenvolvedores Java no mundo e, atualmente, esta tecnologia é encontrada em supercomputadores, servidores, desktops, notebooks, máquinas de cartões de crédito e débito, robôs, automóveis, jogos eletrônicos, bem como uma gama de dispositivos digitais, redes e demais tecnologias de programação. A linguagem Java ainda é a linguagem nativa para o desenvolvimento de aplicações para o Android (sistema operacional para smartphones).
A tecnologia Java foi totalmente gratuita por muito tempo, mas recentemente a Oracle, que passou a deter os direitos da linguagem após adquirir a Sun Microsystems, está licenciando o uso para empresas com custos. A empresa deve permitir o licenciamento gratuito somente para desenvolvedores avulsos que criam aplicações pessoais sem custo ou para simples aprendizado.
Principais características e vantagens da tecnologia Java:
- Orientada a objetos, com uma grande diversidade de bibliotecas de classes disponível;
- Independe de plataforma: write once, run everywhere ;
- Segurança - Mecanismos para sistemas livres de vírus, pacotes para criptografia;
- Simplicidade;
- Sintaxe dos comandos básicos segue o padrão do C;
- Sintaxe da parte OO bem mais simples que o C++;
- Internacionalização;
- Unicode: padrão que permite manipular textos de qualquer sistema de escrita;
- Robustez;
- Tratamento de exceções;
- JVM (Java Virtual Machine) impede que uma aplicação mal comportada paralise o sistema;
- Distribuída e multitarefa;
- Os programas podem utilizar recursos da rede com a mesma facilidade que acessam arquivos locais;
- Trabalha com diversos protocolos (TCP/IP, HTTP, FTP);
- Execução simultânea de múltiplas threads;
- Gerenciamento de memória;
- Memória virtual gerenciada pela JVM (Java Virtual Machine);
- Garbage collection (limpeza de memória);
- Desempenho;
- Mais rápida que linguagens de script, porém mais lenta que as linguagens compiladas puras;
- Hoje, os problemas de desempenho são resolvidos com compilação just-in-time.
A **plataforma Java** é uma plataforma de software desenvolvida pela Sun Microsystems (agora parte da Oracle), projetada para fornecer um ambiente para o desenvolvimento e execução de aplicações independentes de sistema operacional e hardware, mais especificamente ela é o sistema de implementação do Java. Isso é possível graças ao conceito de *Write Once, Run Anywhere (WORA)*, onde um programa Java pode ser escrito uma vez e executado em qualquer dispositivo que tenha a **Java Virtual Machine (JVM)** instalada. De forma geral, entendemos que plataforma (ambiente de execução) é composta por hardware + software básico (sistema operacional). A plataforma Java é puramente baseada em software, e sua estrutura modular (JVM, API, JRE e JDK) permite que aplicações Java sejam portáveis, seguras e eficientes. Essa arquitetura fez do Java uma das linguagens mais utilizadas no mundo, especialmente em sistemas empresariais, aplicações web e desenvolvimento para dispositivos embarcados.
A plataforma Java é definida apenas em software e possui dois componentes:
- Máquina Virtual Java (JVM - Java Virtual Machine);
- Conjunto de bibliotecas que disponibilizam classes comuns.Detalhando mais sobre a estrutura da Plataforma Java, ela é composta por quatro camadas principais, cada uma com um papel fundamental:
Máquina Virtual Java (JVM - Java Virtual Machine):
A **JVM** é o coração da plataforma Java e atua como um **interpretador** para o código Java compilado (bytecode). Ela é responsável por:
- Executar programas Java de forma independente do sistema operacional.
- Gerenciar memória automaticamente via **Garbage Collector (GC)**.
- Otimizar o desempenho através da **compilação Just-In-Time (JIT)**.
- Fornecer segurança ao isolar o código executado.A JVM traduz o **bytecode Java** para instruções específicas da máquina onde está rodando.
Biblioteca de Classes (Java API - Application Programming Interface):
A **API do Java** é um conjunto de bibliotecas padrão que oferece funcionalidades essenciais para o desenvolvimento de aplicações. Ela contém:
- **Pacotes básicos** (`java.lang`, `java.util`, `java.io`) → Manipulação de strings, coleções, entrada/saída, etc.
- **Bibliotecas de rede** (`java.net`) → Comunicação via HTTP, sockets e WebSockets.
- **Bibliotecas de concorrência** (`java.util.concurrent`) → Threads, sincronização, paralelismo.
- **Acesso a bancos de dados** (`java.sql`, `javax.persistence`) → JDBC, JPA.
- **Interface gráfica** (`javax.swing`, `javafx`) → Desenvolvimento de GUIs.Ambiente de Execução (JRE - Java Runtime Environment):
O **JRE** é o ambiente necessário para executar aplicações Java e contém:
- A **JVM**.
- As **bibliotecas de classes da API Java**.
- Ferramentas básicas para rodar aplicações Java.Se um usuário final quiser rodar um programa Java, basta ter o **JRE** instalado. No entanto, para desenvolvimento, o JRE sozinho não é suficiente.
Kit de Desenvolvimento Java (JDK - Java Development Kit):
O **JDK** é um **superconjunto do JRE** e inclui ferramentas para **desenvolver** aplicações Java. Ele contém:
- **Compilador (javac)** → Transforma código-fonte Java em **bytecode**.
- **Depuradores e ferramentas de monitoramento** (`jdb`, `jconsole`, `jvisualvm`).
- **Bibliotecas adicionais** para desenvolvimento avançado.
- **Ferramentas para modularização** (desde o Java 9 com `jlink` e `jmod`).Se um desenvolvedor deseja programar em Java, ele precisa do **JDK**.
Exemplo de Fluxo de Execução na Plataforma Java:
1. Um desenvolvedor escreve um programa em **Java** (`.java`).
2. O código-fonte é compilado pelo **`javac`** e transformado em **bytecode** (`.class`).
3. A **JVM** lê o bytecode e o executa na máquina usando o **JRE**.API Java:
"Diferentemente das linguagens convencionais, que são compiladas para código nativo, a linguagem Java é compilada para "bytecode" (gerando o
.class
ou.jar
), que é executado por uma máquina virtual Java (JVM - Java Virtual Machine)."O modelo inicial era interpretado. Já o atual trocou a etapa do interpretador por uma 2ª compilação (compilador JIT, isto é, just-in-time).
A tecnologia Java é composta por três plataformas:
- **J2SE** ou **Java SE (Java Standard Edition)**: base da plataforma, inclui o ambiente de execução e as bibliotecas comuns;
- **J2EE** ou **Java EE (Java Enterprise Edition)**: versão voltada para o desenvolvimento de aplicações corporativas e aplicações web;
- **J2ME** ou **Java ME (Java Micro Edition)**: versão voltada para o desenvolvimento de aplicações móveis ou embarcadas.A sigla JSE pode referir-se a diferentes tecnologias dependendo do contexto, mas comumente tem dois significados principais:
1. **Java Standard Edition (Java SE)**: Este é um conjunto de especificações e tecnologias fornecidas pela Oracle para o desenvolvimento de aplicativos Java. O Java SE inclui a linguagem de programação Java, a Máquina Virtual Java (JVM) e bibliotecas padrão para criar aplicações de desktop e servidor.
2. **JavaScript Engine**: Este é um motor que executa o código JavaScript, comumente usado em navegadores web para executar scripts em páginas web. Exemplos de motores JavaScript incluem o V8 do Google Chrome, o SpiderMonkey do Mozilla Firefox e o Chakra do Microsoft Edge.
Portanto, a sigla JSE pode se referir tanto ao Java Standard Edition, relacionado à linguagem de programação Java, quanto a um motor JavaScript, relacionado ao JavaScript. O significado exato depende do contexto em que a sigla é utilizada.
## [Java] Ambiente de desenvolvimento
Existem os ambientes JDK e JRE para construir e executar uma aplicação Java.
O **Java Development Kit (JDK)** é uma coleção de programas para, dentre outras tarefas, compilar e executar aplicações Java. Este é o kit necessário para o desenvolvedor, pois contém todo o suporte para a criação de aplicações em Java.
Exemplo:
- Javac (compilador Java);
- Javadoc (utilitário para documentação);
- Java;
- Outros.Kit com todos os programas necessários para executar aplicações Java. Faz parte do JDK, mas pode ser instalado separadamente para execução em máquinas clientes, uma vez que o JDK é voltado para os desenvolvedores. O JRE pode ser instalado separadamente e dá suporte somente a execução de aplicações ou jogos como o Minecraft, por isso é a versão mais indicada para instalação nas máquinas clientes que apenas executarão aplicações, não sendo responsáveis pelo seu desenvolvimento.

O código de um programa Java é compilado apenas uma vez, gerando um código intermediário, o **bytecode**, que pode ser executado quantas vezes forem necessárias em qualquer ambiente que possua uma máquina virtual Java (JVM) disponível.
Inicialmente a tecnologia Java realizava uma interpretação completa do bytecode, mas atualmente o interpretador realiza uma compilação **just-in-time** (compila o bytecode para o ambiente onde ocorrerá a execução), permitindo aumentar o desempenho da aplicação.
Para o desenvolvimento de aplicações em Java é comum o uso de ferramentas IDEs (Integrated Development Environment), que facilitam a codificação e a realização de testes, sendo as mais conhecidas:
- Eclipse;
- NetBeans;
- IntelliJ;
- BlueJ.Você pode usar qualquer tipo de ambiente, se preferir um editor de texto como um bloco de notas ou um VSCode você precisa usar a extensão `.java`.
# ☕ [Java] Ambiente de programação
Existem várias ferramentas para o desenvolvimento de sistemas utilizando a linguagem Java, mas os desenvolvedores têm preferência pelos IDEs **Netbeans** e **Eclipse**. Ambos são gratuitos e podem ser adquiridos pela internet através de download.
É importante que você já tenha instalado o JDK antes de instalar o seu IDE escolhido (Netbeans ou Eclipse). Assista o vídeo:
Existem dois arquivos diferentes: o primeiro, com o source, contém os códigos fonte no Netbeans e não é o ideal para trabalharmos o desenvolvimento. A versão adequada para nós é a versão bin, que contém todos os códigos já compilados e prontos para a execução e desenvolvimento de projetos e aplicações Java.
Você não precisa instalar as duas, pois ambas são concorrentes e desempenham as mesmas funcionalidades. A preferência é a critério do programador.
A linguagem Java possui uma base de construção semelhante à linguagem C e, por isso, boa parte de sua estrutura e sintaxe se assemelha a ela. Desta forma, programadores com conhecimento nesta linguagem tem grande facilidade com a sintaxe da linguagem Java. Outra importante semelhança está nas estruturas de controle de fluxo, que são construídas da mesma forma em ambas as linguagens.
> Cuidado com as diferenças de versões no sistema operacional: se instalar o Java para 64 bits, você deverá usar um IDE (Netbeans ou Eclipse) de 64 bits. O mesmo para a versão de 32 bits: tanto o Java quanto o IDE deverão ser para 32 bits.
## [Java] `Hello, World!`
Veja abaixo o passo a passo para criar sua primeira aplicação em Java, imprimindo na tela o `Hello, World!`, você pode usar qualquer editor de texto e salvar a extensão do arquivo `.java`, mas nesse exemplo, eu estarei utilizando a IDE Netbeans:
![]()
![]()
![]()
![]()
O projeto `Exemplo` foi criado e automaticamente teremos uma **classe inicial** para execução da aplicação.

O ambiente está pronto para digitarmos o código da aplicação: preencha o código conforme o exemplo a seguir.

[](#)
```java
```

Após o código estar pronto e sem erros, podemos executar a aplicação clicando sobre o “arquivo da classe” com o botão direito, e em seguida clicar sobre a opção `Run file`.

A aplicação executará na parte inferior do Netbeans:

Crie um programa em java e execute os seguintes comandos para abri-lo como saída pelo terminal:
[](#)
```java
public class HelloWorld {
public static void main(String[] args)
{
System.out.println("Hello, World!");
}
}
```Configurando a saída do programa pela interface de linha de comando (CLI) do terminal:
```sh
javac app.java
java app
```A linguagem **Java** tem boa parte de suas características herdadas da linguagem **C**. Muitos dos seus operadores, formação de identificadores, comandos de controle de fluxo e várias outras características são compartilhados entre estas duas linguagens.
Todas as instruções da linguagem Java devem terminar por um símbolo de ponto e vírgula “
;
”. Você não usará o ponto e vírgula quando a instrução for uma codificação que irá continuar com um bloco de comandos.Vejamos um exemplo:
```java
System.out.println(“Mensagem do sistema”);
```Os blocos de comandos em Java são delimitados por
{
(abrir) e}
(fechar) chaves, em que a instrução anterior define que todos os comandos do bloco farão parte desta. Isso irá ocorrer em classes, métodos e instruções de controle de fluxo.Exemplo:
```java
if(nota>10.0) {System.out.println(“Nota inválida”);
}
```Como usar a endentação? Quando desenvolvemos um programa em qualquer linguagem, é comum que utilizemos um conjunto de espaços na frente das instruções de forma a facilitar a visualização de blocos. Sempre que iniciamos um bloco, devemos começar na próxima linha com um deslocamento de pelo menos quatro espaços em brando ou uma tabulação (normalmente quatro espaços). Isso permite que identifiquemos rapidamente que certo conjunto de instruções faz parte de um conjunto que será executado em bloco.
Exemplo:
```java
import java.util.Scanner;public class Exemplo {
// identação do bloco da classe Exemplo (a classe sempre possui o nome do arquivo, os dois andam bem alinhados!)
public static void main(String[] args) {
// identação do bloco do método main
// TODO Auto-generated method stub
Scanner sc = new Scanner(System.in);
double media, nota1, nota2;
System.out.println("Digite a nota 1:");
nota1 = Double.parseDouble(sc.nextLine());
System.out.println("Digite a nota 2:");
nota2 = Double.parseDouble(sc.nextLine());
media = (nota1 + nota2) /2.0;
System.out.println("A sua média é:" + media);
sc.close();
} // encerramento da identação do bloco do método main
} // encerramento da identação da classe Exemplo
```Em todas as linguagens de programação, devemos identificar variáveis, programas, funções, métodos, parâmetros etc. O ato de nomear algo em uma linguagem de programação é uma forma de identificação da linguagem. Em Java são permitidos identificadores que comecem com letras (maiúsculas ou minúsculas), ou um símbolo de “
$
” (dólar) ou “_
” (underscore/
underline). Números podem ser usados, mas não para iniciar um identificador.**Java** é uma linguagem de programação sensível à caixa (alta ou baixa ou **case sensitive**). Desta forma, a linguagem faz distinção entre letras maiúsculas e minúsculas. Mas isso não quer dizer que podemos utilizar qualquer nome como um identificador, pois existem algumas palavras reservadas que não podem ser utilizadas para tal.
Exemplos de identificadores válidos em Java:
- `identificador`
- `nomeCompleto`
- `NomeCompleto`
- `nota1`
- `_sys_path`
- `$user`Observe que os exemplos 2 e 3 possuem a mesma grafia, mas, como existe mudança entre caixa alta e baixa, para a linguagem Java são dois diferentes identificadores.
Palavras reservadas da linguagem Java (não podem ser usadas como identificadores), dentre elas podemos destacar:
abstract
arguments
await
boolean
break
byte
case
catch
char
class
const
continue
debugger
default
delete
do
double
else
enum
eval
export
extends
false
final
finally
float
for
function
goto
implements
if
import
in
instanceof
int
interface
let
long
native
new
null
package
private
protected
public
return
short
static
super
switch
synchronized
this
throw
throws
tdrow
transient
-true
try
typeof
var
void
volatile
while
with
yield
> OBS: Todas as palavras reservadas começam por letras minúsculas e são palavras do idioma inglês.
## [Java] Comentários em Java
O uso de comentários em Java é semelhante ao usado na linguagem C, mas apenas dois destes tipos são iguais nas duas linguagens, sendo o terceiro tipo somente disponibilizado na linguagem Java.Vejamos:
//
comentário de uma linhaQuando usamos duas barras em uma linha de código, todo o seu conteúdo, após as duas barras, é desconsiderado pelo compilador, o que quer dizer que podemos escrever qualquer conteúdo que o mesmo não será compilado. É muito usado para deixar informações e avisos do programador no código.
```java
// TODO Auto-generated method stub
```
/**/
comentário de duas ou mais linhas (bloco)Ao usarmos o comentário de bloco, podemos comentar não apenas um trecho de uma linha, mas todo um conjunto de linhas. É utilizado quando temos longos trechos de textos com avisos e informações, ou para a depuração do código. Para a depuração do código, podemos comentar um conjunto de linhas para realizar um conjunto de testes. Neste caso, podemos comentar um conjunto de instruções ou porque estas instruções estão com problemas e queremos verificar as demais. Ou, ao contrário, onde temos um conjunto de instruções já testadas e corretas e queremos apenas testar as demais. Seja como for, o uso do comentário de bloco é muito usual e comum entre os programadores.
/***/
comentário de documentaçãoO comentário de documentação se difere do comentário de bloco por possuir um asterisco a mais no início, mas ambos encerram da mesma forma. Existe uma ferramenta na linguagem Java responsável por extrair de um projeto (com várias classes) todos os comentários de documentação e montar um documento com todo este conteúdo.
Neste caso, usamos este tipo de comentário apenas para descrever avisos e informações das classes, de forma a realizar a documentação do sistema ainda durante sua fase de criação. Isto permite que o desenvolvedor descreva todo a documentação no próprio projeto, facilitando a descrição e a manutenção do sistema. Assim, ao terminar um projeto ou realizar algum tipo de modificação, basta gerar novamente a documentação do sistema que tudo estará atualizado.
# ☕ [Java] Tipos de dados
A linguagem Java possui nove tipos de dados básicos, sendo oito deles primitivos e um tipo especial.Primitivos (armazenam apenas valores)
Tipo lógico (boolean)
O tipo lógico só permite dois estados, verdadeiro (true
) ou falso (false
); em Java ainda é permitido o uso deon
eoff
, ouyes
eno
.Exemplo:
boolean status = true;Tipo caractere (char)
O tipo char permite que seja armazenado na memória apenas um caractere e se difere do texto (String) por ser definido entre ‘e’. Quando usamos aspas simples ou dupla determinamos apenas um caractere.Exemplo:
char letra = ‘A’;Também é possível armazenar caracteres de controle:
Caractere Especial
Representação
’\n’
nova linha.
’\r’
enter.
’\u????’
especifica um caractere Unicode o qual é representado na forma Hexadecimal.
’\t’
tabulação.
’\\’
representa um caractere\
(barra invertida).
’\” ’
representa um caractere“
(aspas)
OBS: A barra invertida na frente indica que é um caractere especial.Tipos inteiros (byte, short, int e long)
São quatro diferentes tipos de inteiros, que se diferenciam pela quantidade de bits que cada um ocupa em memória para armazenar um valor. Isto faz com que, quanto menor a quantidade de bits, maior seja a limitação do valor a ser armazenado. Entretanto, em ocasiões onde a memória é pouca, devemos trabalhar muito bem com estas diferenças para reduzir o espaço de memória necessário. O uso mais comum é doint
, mas, para números muito grandes ou muito pequenos, devemos usar olong
. Já para economizar memória podemos usarbyte
oushort
, de acordo com o valor que será armazenado.
Tipo de dado
Quantidade de bits
Quantidade de Bytes
Escopo (valores que podem ser armazenados)
byte
8
1
-2⁷ . . . . 2⁷ - 1
short
16
4
-2³¹ . . . . 2³¹ - 1
long
64
8
-2⁶³ . . . . 2⁶³ - 1
Tipos reais (float e double)
São dois diferentes tipos de valores reais, sendo um de precisão simples (float
), que ocupa menos espaço de memória, e o de dupla precisão, que ocupa mais memória. Quanto maior o número de bits para armazenar um valor real, maior será a precisão deste número dentro do sistema. O uso dofloat
é comum quando necessitamos economizar espaço de memória. Em Java, todo tipo de dado numérico é convertido paradouble
automaticamente por coerção (força a conversão de tipo). Por isso, é mais indicado, quando não houver falta de espaço de memória, a utilização dedouble
para armazenamento de valores reais.
Tipo de dado
Quantidade de bits
Quantidade de Bytes
float
32
4
double
64
8
Tipo especial
Tipo texto (String)
O tipo texto (String
) não é um tipo primitivo, mas um tipo especial. Na verdade, o tipo String é uma classe e por isso começa com letra maiúscula, ao contrário dos tipos primitivos, que sempre começam por minúsculas. Este tipo de dado armazena um conjunto de caracteres, formando palavras ou frases de tamanhos variados. Como classe, veremos mais tarde que elementos do tipo String possuem métodos que podem realizar ações específicas sobre o seu conteúdo.Exemplo:
String nome = “João da Silva”;# ☕ [Java] Constantes e variáveis
Variáveis e constantes em Java devem **obrigatoriamente possuir um tipo**. Isso ocorre porque **Java é uma linguagem de programação fortemente tipada**.Linguagens de programação fortemente tipadas Obrigam que todas as variáveis e constantes sejam definidas por um tipo de dado.
Linguagens de programação fracamente tipadas permitem que variáveis sejam usadas a qualquer momento, sem a necessidade de terem um tipo predefinido. Isso quer dizer que o tipo de dado pode variar em diferentes partes do programa.
**Variáveis** são declaradas por meio de *um tipo* e *um identificador*, sem que sejam necessárias outras informações. A boa prática em programação Java determina que todas as variáveis comecem por letras minúsculas e, somente se tiver mais de uma palavra, o inicial da segunda palavra em diante deverá começar por letras maiúsculas.
Exemplos:
int c;
double nota1 = 0; // indica que a variável será inicializada com 0 (zero)
String nomeCompleto;A definição de constantes precisam do modificador final, que indica que, uma vez que ocorreu uma atribuição a variável, seu conteúdo não poderá ser mudado. Em Java, constantes podem ser criadas em nomes em minúsculas ou maiúsculas, mas a boa prática de programação determina que sua identificação deve ser toda em maiúsculas.
Exemplos:
final int IDADEMINIMA = 15;
final double VALORDOLAR = 3.96;
final NOMEEMPRESA = “Super Empreendimentos”;# ☕ [Java] Operadores e expressões
Operadores aritméticos
Operador
Descrição
=
Atribuição
+
Soma
-
Subtração
*
Multiplicação
/
Divisão
%
Resto da divisão
> OBS: Java sempre realizará a operação inteira quando os operandos forem inteiros, e a operação real ocorrerá caso um ou mais operando seja real.
Exemplos:
int v = 7 / 2; // o valor atribuído será 3 e não 3.5, porque ambos os operandos são inteirosdouble v = 7.0 / 2; // o valor atribuído será 3.5, porque o primeiro operando é real.
A mesma lógica serve para variáveis:
int a, b=7, c=2;a = b / c; // será armazenado 3 em a.
double a, b=7.0, c=2.0;
a = b / c; // será armazenado 3.5 em a.
Podemos alterar o tipo de um operando em uma expressão utilizando o **cast**, que nada mais é do que informar que o valor armazenado na variável terá o seu valor em função do tipo alterado.
Exemplo:
```java
int b=7, c=2;double a=0;
a = (double) b / c; // o valor de b será convertido para double antes da operação
// e isso fará com que o primeiro operando seja real e desta
// forma a operação será real, armazenado 3.5 em a.
```+= -= *= /= %=Exemplo:
```java
int alturaParede = 2.85; // declaração da variável alturaParedealturaParede += 0.15; // a variável alturaParede terá o valor
// acrescido (somado) em 0.15, sendo
// equivalente a:
// alturaParede = alturaParede + 0.15;
```Desta forma, não precisamos colocar o nome da variável duas vezes.
Em Java temos os operadores de incremento
++
e de decremento--
, que sempre adicionam uma unidade (++
) ou subtraem uma unidade (--
). Eles podem ser ainda divididos em **pré-incremento** e **pós-incremento**, e **pré-decremento** e **pós-decremento**.O **pré-incremento** determina que primeiro seja realizada a operação de incremento e depois é realizada a operação de atribuição.
Exemplo:
```java
int a = 20, b=0;b = ++a; // primeiro a variável a será incrementada de uma unidade, valendo 21,
// depois b receberá o valor de a e assim, também valerá 21.
```O **pós-incremento** determina que antes seja realizada a atribuição para só então ser realizada a operação de incremento.
Exemplo:
int a = 20, b=0;b = a++; // primeiro b receberá o valor de a, que é 20 (antes do incremento),
// depois a será incrementado e assim, o valor de a será 21 e o de b será 20.
O **pré-decremento** determina que primeiro seja realizada a operação de decremento e depois é realizada a operação de atribuição.
Exemplo:
int a = 20, b=0;b = --a; // primeiro a variável a será decrementada de uma unidade, valendo 19,
// depois b receberá o valor de a e assim, também valerá 19.
O **pós-decremento** determina que antes seja realizada a atribuição para só então ser realizada a operação de decremento.
Exemplo:
int a = 20, b=0;b = a--; // primeiro b receberá o valor de a, que é 20 (antes do incremento),
// depois a será decrementado e, assim, o valor de a será 19 e o de b será 20.
Operadores de Relacionais são usados para definir condições.
Operador
Descrição
==
Igualdade/ Comparação
!=
Negação/ Diferente
>
Maior que
<
Menor que
>=
Maior ou igual a
<=
Menor ou igual a
Exemplos:
1) if(a > b) { ... }2) while (a <=100) { ... }
3) for (int c =0; c<50; c++) { ... }
Operadores de em Expressões Lógicas
Operador
Descrição
!
NÃO lógico
&&
E lógico
||
OU lógico
São os determinantes das tabelas-verdade.
Ordem de precedência: **!, &&, ||**
Exemplos:
if(a > b && c < d) { ... }while (a <=100 || b == 10) { ... }
if( !a == 15 && b >= 10) { ... }
if( !a == 15 || c > d && b >= 10) { ... }
Pela ordem de precedência: if( (!a == 15) || (c > d && b >= 10))
Primeiro será executada a negação (!); depois o e lógico (&&) e por último o ou lógico (||).
Operadores de bits
Operador
Descrição
&
E entre bits
^
OU EXCLUSIVO entre bits
|
OU entre bits
Ordem de precedência:
&
^
|
# ☕ [Java] Estruturas de programação
Comandos de controle de fluxo Servem para determinar se as condições são verdadeiras ou controlar uma determinada ordem lógica de eventos para o código.## [Java] Estrutura condicional
Se (if):if (condição) {
// instruções;
}
// A cláusula else é opcional.
if ... else,if (condição) {
// instruções;
}
else {
// instruções;
}
if ... else if ... else,if (condição1) {
instruções;
}
else if (condição2) {
instruções;
}
else if (condição3) {
instruções;
}
else {
instruções;
}
A cláusula if deve ocorrer apenas uma vez; As cláusulas else if podem ocorrer: nenhuma, uma ou várias vezes; A cláusula else só pode ocorrer uma única vez.
switch … case
Estruturas de decisão caracterizadas pela possibilidade de uma variável possuir vários valores diferentes em uma determinada situação.
Uma única estrutura
switch
pode analisar vários diferentes valores para a variável de controle. A variável de controle em Java pode ser do tipo:inteiro
,caractere
, ouString
.A cláusula
case
pode ocorrer de uma a várias vezes, e a cláusuladefault
é opcional.## [Java] Estrutura de Laços de Repetição (Loops)
for
**Estrutura de repetição** (Laços de repetição ou Loop) controlada por uma ou mais variáveis contadoras e caracterizada pela existência de três parâmetros, sendo todos eles opcionais:**1** - Inicialização da(s) variável(is) de controle
**2** - Condição sobre a(s) variável(is) de controle para parada das iterações
**3** - Passo da(s) variável(is) de controle a cada iteração
for((1)inicialização; (2)condição de controle; (3)passo) {// instruções
}
Exemplo:
1) Repetição controlada por uma variável:
for (int c=1; c<=limite; i++) {instruções;
}
2) Repetição controlada por duas variáveis:
for (a=1, b=2; a*b<limite; a++, b+=2) {instruções;
}
3) Repetição sem fim
for ( ; ; ) {instruções;
}
while
Esta estrutura realiza a repetição de um conjunto de instruções enquanto a condição determinada for verdadeira; caso a condição seja falsa no primeiro teste, nenhuma instrução será executada.// realiza o teste da condição no início da estruturawhile (condição) {
instruções;
}
do...while
Esta estrutura de repetição é semelhante à anterior, mas com o diferencial de que as condições devem ser verificadas apenas no final da estrutura, obrigando que as instruções sejam sempre executadas pelo menos uma vez.// Teste de condição no finaldo
{
instruções;
} while (condição);
# ☕ [Java] Entrada e Saída de dados
Em Java temos muitas formas de **entrada de dados** (input), inclusive de forma gráfica. Inicialmente trabalharemos com a *classe*Scanner
, responsável pela entrada de dados em formato texto, com perguntas diretas ao usuário e a inclusão da resposta em variáveis do programa.Para realizarmos esta tarefa, é necessário que seja criado um objeto da classe `Scanner`.
Para isso, devemos **importar** a *classe*
Scanner
antes do início da programação da classe:import java.util.Scanner;Depois é necessário criar o **objeto** para realizar as entradas de dados:
public class EntradaDados {public static void main (String[] args) {
Scanner entrada = new Scanner(System.in);
}
}
Existem vários métodos associados a classe **Scanner** para a entrada de dados, mas para evitarmos problemas futuros podemos usar sempre a entrada de dados de texto
(nextLine())
e converter o texto para o tipo desejado.Exemplo:
1) Para entrada de texto (String):
String nome;Nome = entrada.nextLine(); // não precisa de conversão, apenas da entrada.
2) Para entrada de valor real:
double nota1;nota1=Double.parseDouble(entrada.nextLine());
// a entrada de dados em texto precisa de conversão para double.
3) Para entrada de valor inteiro:
int idade;idade = Integer.parseInt(entrada.nextLine());
// a entrada de dados em texto precisa de conversão para int.
É aconselhável evitar o uso de métodos como:
- entrada.nextDouble();
- entrada.nextFloat();
- entrada.nextInt();Estes métodos, quando usados em conjunto, podem fazer com que a aplicação pule alguma entrada de dados, sendo necessário que seja realizada uma “limpeza de buffer”. Este tipo de problema pode ser contornado ao usar sempre o método “nextLine()” e a conversão de tipos.
A **saída de dados** (output) em modo texto pode ser realizada pela *classe*
System
, e o métodoout.print
(não pula linha),out.println
(pula linha) ou outros métodos:1) Apenas uma mensagem:
System.out.println("Entre com a Nota A1.........: ");2) Mensagem e conteúdo de variáveis:
System.out.println(" Nome: " + nome + " Idade: " + idade + " Nota 1: " + nota1);A seguir temos o exemplo completo de um programa que recebe duas notas e apresenta a média.
public class Exemplo {public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner sc = new Scanner(System.in);
double media, nota1, nota2;
System.out.println("Digite a nota 1:");
nota1 = Double.parseDouble(sc.nextLine());
System.out.println("Digite a nota 2:");
nota2 = Double.parseDouble(sc.nextLine());
media = (nota1 + nota2) /2.0;
System.out.println("A sua média é:" + media);
sc.close();
}
}
## [Java] Conversão de tipos
A conversão de tipos em Java pode ser feita por cast ou com o uso de conversão por classes. Ambos já foram vistos em exemplos anteriores:1) Por cast: Usado para converter valores de um tipo para outro; com cast basta indicar o tipo que você quer que a variável, ou valor, seja convertida, colocando o tipo desejado entre parênteses:
2) Por uso de classes para conversão de textos em valores:
double nota1 = Double.parseDouble(“7.8”);int idade = Integer.parseInt(“34”);
float valor = Float.parseFloat(“2.15”);
long valor2 = Long.parseLong(“3456789”);
Pode-se usar ainda:
Byte.parseByte() / Short.parseShort()
# ♨️ [Java] A Boa prática em programação Java
A boa prática em programação Java leva em conta um conjunto de regras que facilitam o desenvolvimento de aplicações e melhoram bastante o trabalho em conjunto realizado por equipes. Ao seguir estas regras, projetos podem ser desenvolvidos em paralelo por diferentes programadores, sem que seja necessário que cada componente precise esperar que outros terminem suas tarefas. Estas regras foram utilizadas na construção da linguagem permitindo que não seja necessário decorar as sintaxes de instruções Java. Não são obrigatórias, mas permitem a codificação melhor de nossas aplicações. Vamos ver algumas dessas regras na prática:1) Variáveis auxiliares, atributos, métodos e objetos devem ser identificados iniciando por letras minúsculas. Quando houver mais de uma palavra, deve-se começar cada nova palavra com uma letra maiúscula.
Exemplos:
int idade;int maiorIdade;
String nome;
String nomeCompleto;
2) Constantes devem ser identificadas por letras maiúsculas em todo o seu nome; mesmo quando temos mais de uma palavra, todo o identificador deve ficar em maiúsculas.
Exemplos:
final int idade;final int maiorIdade;
final String nome;
final String nomeCompleto;
3) Classes e interfaces (tipo especial de classe) devem iniciar por letras maiúsculas. Quando houver mais de uma palavra, deve-se começar cada nova palavra com uma letra maiúscula.
Exemplos:
public class Carro { ... }public class Carro Hibrido { ... }
public interface Basico { ... }
public interface MetodosBasicos { ... }
# ☕ [Java] Classes e objetos
A **programação orientada a objetos (POO)** tem como principal conceito representar, em um sistema computacional, um **objeto da vida real**.Esta representação deve descrever o objeto quanto às suas características e ações que poderá realizar dentro do sistema.
Não devemos nos preocupar com todas as características presentes no objeto, mas com aquelas que serão necessárias ao sistema (requisitos).
Exemplo: A placa de um automóvel é importante para um sistema de estacionamento, assim como a hora de chegada e saída.
Em alguns casos, o fabricante, modelo e a cor do automóvel poderão ser importantes, mas dificilmente iremos cadastrar o número do chassi do mesmo. Como o número do chassi não é facilmente visto e seu cadastramento dependeria da documentação do automóvel ou de uma análise para a identificação, que seria difícil, uma vez que é um identificador com muitas letras e números, acabaria por gerar filas e insatisfação dos clientes.
Entretanto, para o sistema de cadastramento do DETRAN, por exemplo, o número do chassi é uma das informações mais importantes. Dessa forma, identificarmos a placa é importante como descritor do automóvel para o sistema de estacionamento, já o chassi não.
Por isso, devemos analisar cada objeto separadamente e quais são as características importantes para o sistema em que o objeto será utilizado. Como outro exemplo, podemos notar que a matrícula, nome e CR de um aluno são importantes para o sistema acadêmico, mas o time para o qual o aluno torce ou sua religião não são. Por isso, os descritores time e religião não são importantes para o objeto Aluno em um sistema acadêmico.
As **classes** Java são responsáveis pelo **conjunto de códigos para a criação de objetos e aplicações**. Uma classe Java deve descrever as *características e ações* que o objeto possui ao ser representado em um sistema computacional, levando em consideração as caracteristicas (atributos) e as ações (métodos) juntamente ou não com seus argumentos (parâmetros).
**Atributo** é conceitualmente um descritor do objeto e deve representar uma **característica**, dele. O **conjunto de atributos** do objeto deve representar todas as *características importantes* do objeto para o sistema.
Exemplo:
String matricula; // atributo para armazenamento da matrículaString nome; // atributo para armazenamento do nome
double cr; // atributo para armazenamento do cr
**Método** é uma **ação**, um conjunto de instruções a serem executadas por um objeto para realizar uma determinada tarefa.
O **conjunto de métodos** de um objeto deve descrever **todas as ações (tarefas ou funções)** que o objeto poderá realizar dentro do sistema.
Exemplo:
public int soma(int n1, int n2){int soma;
soma = n1 + n2;
return soma;
}
public void imprimeAumento(double salario, int percentual){
double aumento;
aumento = salario + salario * percentual / 100.0;
System.out.println("O salário com aumento é: " + aumento);
}
A classe modela o **objeto** de acordo com as necessidades do sistema para a sua descrição e suas ações. A partir de uma mesma classe, vários objetos diferentes, mas com características semelhantes, podem ser criados em um mesmo sistema ou em diferentes sistemas.
Se consideramos a classe Aluno, podemos criar a partir desta classe dezenas, centenas ou mesmo milhares de objetos Alunos com características semelhantes, tais como matrícula, nome e CR, mas com propriedades (valores próprios nos atributos de cada objeto) diferentes.
Os *objetos* **só existem durante a execução do sistema**, pois estes só existirão como referência na memória do computador neste momento. Dizemos também que os objetos só existem “em tempo de execução”, uma vez que o sistema ao ser encerrado terá toda a sua memória apagada. Consequentemente, todas as suas variáveis e objetos não existirão mais.
Exemplo: `Aluno.java` (objeto)

# ☕ [Java] Aplicações Java
**Aplicações em Java** são classes especiais que possuem um métodomain()
. O método `main` é responsável por criar os objetos e realizar a combinação de diferentes classes para atender às necessidades de um sistema.Em cada sistema, temos **apenas uma aplicação**, que será **responsável pela lógica de criação e uso das classes**. A comunicação entre os objetos ocorre por meio de **trocas de mensagens**, que são **expressas com o uso de métodos**. Uma aplicação, então, cria objetos a partir de uma ou mais classes e usa os métodos dos objetos para realizar as ações que atenderão às necessidades dos usuários.
Exemplo: `AppAluno.java` (aplicação)

> [!Note]
> 1. Cada **classe pública (public) deve ser criada em um arquivo próprio** e o **nome da classe deve ser o mesmo do arquivo**. Ou seja, a classe Aluno deve ser criada no **arquivo**Aluno.java
e a **classe da aplicação** "AppAluno" deve ser criada no arquivoAppAluno.java
. No projeto, seja no Eclipse ou no Netbeans, deverão ser criadas duas classes, uma para o **Aluno** e outra para a **aplicação**;
>
> 2. Foi criada apenas uma classe **Aluno**, mas a partir dela poderemos criar quantos objetos quisermos;
>
> 3. Na aplicação foram criados três diferentes objetos do tipo **Aluno**. Isso faz com que cada **objeto Aluno** (aluno1, aluno2 e aluno3) seja criado na memória em locais diferentes (endereços) e possuam espaço de alocação de memória diferentes para **cada atributo de cada objeto**;4. Cada *objeto* criado é **independente** do outro e possui valores próprios para os seus atributos (propriedades). Como a ação é realizada pelo objeto, cada método fará a ação sobre os atributos do objeto indicado, evitando que haja qualquer tipo de alteração indevida nos valores de cada um.

5. Operador
new
serve para criar um novo objeto e instancia-lo com()
, portanto, é um método construtor.nome_da_classe nome_do_objeto = new método_construtor();## [Java] Biblioteca de classes e reaproveitamento de código
A classe `Aluno` passou a ser `uma biblioteca`, e esta classe pode ser reutilizada em diversas outras aplicações. Esse conceito é um dos mais importantes na programação orientada a objetos, pois reduz o trabalho. Qualquer classe criada poderá ser reaproveitada inúmeras vezes por diversas aplicações, poupando esforço de desenvolvimento e facilitando a manutenção.Cada classe criada se torna uma parte da sua biblioteca de classes e, conforme você vai criando novas classes, a sua biblioteca tende a aumentar. Dessa forma, quando você for criar novas aplicações, terá à sua disposição uma séria de classes já prontas e disponíveis para reaproveitar, sem precisar de novas.
Se você precisar realizar qualquer melhoria em uma classe da sua biblioteca, você poderá realizar sem problemas, pois qualquer inclusão não afetará o uso desta classe nas aplicações antigas, mantendo a compatibilidade entre todas as aplicações.
Com base no reaproveitamento de código da programação orientada a objetos, podemos realizar alterações de melhoria, atualização ou qualquer manutenção em uma classe. Isso fará com que todas as aplicações sejam atualizadas quando forem recompiladas.
# ☕ [Java] Métodos Getters e Setters
Por questões de **segurança e falta de controle**, **não é comum realizar acessos diretos aos atributos de um objeto**, por isso são criados métodos específicos para receber o valor e realizar a **atribuição (Setters)**, ou para a **recuperação (Getters)** de um valor armazenado nos atributos de um objeto. Este processo pode evitar que valores incorretos sejam atribuídos sem qualquer chance de análise.**Métodos Setters** são métodos especiais que recebem o valor do atributo e, por serem métodos, podem analisar se são válidos, sendo responsáveis pela atribuição. Quando o atributo é protegido (privado), é necessário um método para realizar a atribuição.
Características dos métodos Setters:
- São sempre do tipo
void
, pois métodos Setters não devem retornar nada;- Devem ser públicos para que a aplicação tenha acesso ao método;
- Devem começar pela palavra
set
e o nome do atributo: como tem mais de uma palavra, cada nova palavra no nome deve começar por letra maiúscula;- Possui sempre um parâmetro do mesmo tipo do atributo que receberá o valor, pois ambos (parâmetro e atributo) devem ser do mesmo tipo.
A verificação do valor a ser atribuído não pode ser realizada quando efetuamos uma atribuição direta:
Aluno a = new Aluno();a.cr = -5.0;
O uso de um método Setter neste caso evitará que seja atribuído um valor inválido para o CR, no caso -5.0;
Exemplo: Setters


- Note que o parâmetro c recebe o valor a ser atribuído ao CR (-5.0), mas antes de atribuir é realizada uma verificação do valor para averiguar se o mesmo é válido. No caso, o valor do parâmetro é menor do que zero.
- Como sabemos que um CR não pode ser negativo, a atribuição não será realizada, assim como a tentativa de realizar a atribuição de um CR maior do que 10 (dez) também não permitirá que a atribuição ocorra.
**Apenas atribuições com valores válidos poderão ser realizadas neste caso.**
O **Métodos Getters** são métodos especiais que **retornam o valor armazenado no atributo, evitando acesso direto a ele pela aplicação**. Assim como visto no método Setter, a proteção do atributo (private) fará com que a aplicação não tenha acesso direto a ele, fazendo com que seja necessário um método público para recuperar o valor atribuído ao mesmo.
Características dos métodos Getters:
- São sempre do mesmo tipo do atributo que será retornado, nunca do tipo
void
;- Devem ser públicos para que a aplicação tenha acesso ao método;
- Devem começar pela palavra
get
e o nome do atributo: como tem mais de uma palavra, cada nova palavra no nome deve começar por letra maiúscula;- Não possui parâmetro: esses métodos nunca receberão parâmetros, uma vez que não farão atribuições ou ações com parâmetros, realizando apenas o retorno do valor armazenado no atributo.
Exemplo:

Note que não existe parâmetro, o método apenas deve retornar o valor armazenado e por isso não pode ser
void
, sendo o tipo de retorno do mesmo tipo do atributo que será retornado, e a ação é a de retorno (return).**No futuro, os atributos das nossas classes serão protegidos contra acesso direto (privado)**, impedindo que a aplicação possa acessar diretamente um atributo. Dessa forma, é necessário que usemos os métodos *Setters* e *Getters* para atribuir e recuperar os valores do atributo.
Exemplo: `Aluno.java` (versão com métodos Setters e Getters.)

Exemplo: `AppAluno.java` (nova versão)

> [!Note]
>
> 1. Os valores dos atributos dos alunos 1 e 2 serão atribuídos normalmente, mas os valores do aluno3 não, porque a matrícula e o nome estão vazios e o CR não é válido;
>
> 2. Os valores foram atribuídos utilizando os métodos Setters, que verificaram se os valores eram válidos para só então realizar as atribuições;
>
> 3. Os métodos Getters foram usados na própria classe Aluno para buscar os valores armazenados nos atributos do objeto no método imprimir.Exemplo: A classe `Carro` possui os atributos e métodos a seguir, crie a **classe Carro** e a **aplicação AppCarro**, realize a entrada de dados na aplicação através do teclado, e ao final imprima os dados dos respectivos carros (através do método
imprimir()
).Classe `Carro`:
Atributos
MétodosFabricante: texto
- Setters para todos os atributosModelo: texto
- Getters para todos os atributosCor: texto
- Imprimir() // imprime todos os atributosPlaca: texto
Valor: real
Número de Portas: inteiro
Ano de fabricação: inteiro
Ano do Modelo: inteiro
`Carro.java`

`AppCarro.java`

> [!Note]
> Você pode ver que temos algumas repetições de código para realizar a entrada de dados de cada objeto. Se aumentarmos o número de objetos, aumentaremos consideravelmente o tamanho do código. Para resolver este problema e evitarmos a redundância de códigos, vamos incluir um novo método na classe Carro, um método para a entrada de dados. Desta forma, evitamos a redundância dos códigos de entrada de dados.Classe `Carro`
Atributos
Métodos
Fabricante: texto
- Setters para todos os atributos
Modelo: texto
- Getters para todos os atributos
Cor: texto
Imprimir() // imprime todos os dados do carro
Placa: texto
- EntradaDados () // realiza a entrada de dados do carro
Valor: real
Valor: real
NumeroPortas: inteiro
AnoFabricacao: inteiro
AnoModelo: inteiro
Nova solução do exercício prático, com a inclusão do método `entradaDados` na classe `Carro`:
`Aluno.java`
`AppAluno.java`
> [!Note]
> Você pode perceber agora que existe um método para a entrada de dados na classe `Carro`, e que ele está sendo usado por cada carro para realizar a entrada de dados pelo teclado, evitando que os códigos das entradas de dados fiquem redundantes.
> Além disso, a aplicação ficou muito mais simples. Caso você tenha vários objetos carros, você não terá redundância, portanto sua aplicação ficará mais simples.
> Faça um teste executando a nova aplicação e analise o resultado. Inclua mais dois objetos carros e teste novamente: você verá que a aplicação terá uma pequena mudança, mas a classe Carro ficará inalterada.
> A partir deste momento, todas as classes deverão sempre conter ométodo entradaDados()
.## [Java] Métodos construtores
A programação orientada a objetos permite que possamos controlar a criação de um objeto através dos chamados **métodos construtores**. Tal característica permite que um método especial, o **método construtor**, seja executado no momento em que ocorre a criação do objeto (objeto é instanciado) e um conjunto de ações (instruções) podem ser programadas para serem realizadas neste momento.Entre essas ações, pode-se destacar o recebimento de dados iniciais para serem atribuídos e/ou preparar o objeto para que este esteja apto a atender às necessidades para qual foi criado.
Um *método construtor* pode ainda ser usado para determinar o tamanho de um vetor que será usado pelo objeto, assim como *pré-configurar estruturas de dados de suporte* ao objeto que está sendo criado.
São métodos especiais executados apenas uma vez por cada *objeto* criado, pois somente são executados no momento da **instanciação / criação do objeto**, sendo responsáveis por realizar as ações necessárias para a sua criação (controlar a criação do objeto).
Características dos métodos construtores:
1. São sempre públicos (public, característica de encapsulamento – veremos mais adiante), não podendo ter nenhum tipo de restrição;
2. Não existe definição de tipo de retorno, pois métodos construtores não podem retornar valores com a instrução “return”, são sem tipo;
3. Devem ser identificados sempre com o mesmo nome da classe;
4. São executados exclusivamente durante o processo de criação / instanciação do objeto, não podendo ser usados pelo objeto após a sua criação.
`Pessoa.java`

`AppPessoa.java`

1. Na classe Pessoa, o método construtor:
public Pessoa(String nome, String identidade, int idade)O método é público, não possui tipo de retorno antes no nome identificador do método, seu identificador é igual ao nome da classe, por isso começou por letra maiúscula e só será usado para criar o objeto (instanciar);
2. A partir do momento em que um método construtor é criado, a classe só poderá ser instanciada se usarmos um método construtor existente. Por isso, o objeto Pessoa p1 não pode ser criado e sua criação foi comentada na aplicação, pois este método tenta utilizar um método construtor que não existe na classe;
3. O objeto p2 usa um método construtor existente e por isso pode ser criado;
4. Com o uso do método construtor, os dados recebidos como parâmetros puderam ser utilizados para realizar as atribuições nos atributos do objeto, determinando os valores de suas propriedades no momento da criação do objeto;
5. Os métodos setIdentidade (String id) e setIdade (int id) podem ter o mesmo identificador para o parâmetro porque o parâmetro id é declarado em diferentes métodos e, sendo assim, ele é válido internamente em cada um dos métodos separadamente.
O processo de compilação de uma classe cria um método construtor vazio quando não for encontrado nenhum método construtor. Desta forma, nos exemplos anteriores, as classes Aluno e Carro não tinham métodos construtores, então o compilador criou respectivamente os métodos a seguir para as classes Aluno e Carro:
public Aluno ( ) { }e
public Carro ( ) { }
Quando não temos um construtor em uma classe, um construtor VAZIO é criado no processo de compilação.
# ☕ [Java] Polimorfismo
**Polimorfismo** quer dizer muitas formas. O **polimorfismo de sobrecarga** permite o emprego de operadores e identificadores de várias formas, sendo então necessária uma contextualização para que seja realizada a operação adequada. Este contexto está ligado ao emprego do operador, método etc., de acordo com uma situação.Polimorfismo de sobrecarga de operadores

A mudança de contexto faz com que as operações a serem realizadas sejam diferentes, pois toda linguagem de programação possui diferentes formas de realizar as operações de soma inteira e real. Desta forma, a expressão aritmética a seguir utiliza as duas operações conjuntamente:
double z = ( 2 + 5) / (3.5 + 1.5);Na primeira operação de soma, os operandos são inteiros, então a operação a ser realizada será de uma soma inteira, para somente depois ser realizada a operação de soma real. Desta forma, teremos em um instante a seguinte situação:
double z = ( 7) / (5.0);Assim, a operação de divisão será real e não inteira porque existe um operando real.
Em Java, **todas as operações aritméticas serão realizadas em função dos tipos dos operandos**, e a operação será inteira apenas se ambos os operandos foram inteiros. Caso contrário (um operando inteiro e outro real ou dois operandos reais), a operação será real.
O operador
+
é um dos mais usados, sendo um bom exemplo de **sobrecarga de operadores**, pois pode ser utilizado de várias e diferentes formas em função do contexto:1. Concatenação:
String nome = "João" + " da " + "Silva";
2. Soma inteira:int a = 3 + 4;
3. Soma real:double b = 1.3 + 2.7;
4. Incremento:x++; ou ++x;
5. Concatenação entre textos e valores:System.out.println("Idade" + p2.getIdade());
Agora imagine a seguinte instrução:
System.out.println("Valor =" + (( 3 + 4) + (1.3 + 2.7) + (++x)));A *sobrecarga de operadores* está sendo usada de diferentes formas em uma mesma instrução. Cada contexto será avaliado para que seja executada a operação adequada em cada caso.
Toda expressão é avaliada sintaticamente, assim como cada contexto será avaliado individualmente no momento da execução.
O **Polimorfismo de sobrecarga de métodos** permite que possamos ter mais de um método com o mesmo identificador em uma mesma classe. Isso só é possível em razão da avaliação do contexto no momento da execução. Vamos levar em consideração que eu desejo realizar o cálculo da área de um quadrado e de um retângulo em uma mesma classe.
Para realizar o cálculo da área do quadrado, eu preciso apenas do valor da base do quadrado. Assim, o método área ficaria da seguinte forma:

Já para realizar o cálculo da área do retângulo, eu preciso do valor da base e da altura do quadrado. Assim, o método área ficaria da seguinte forma:

# ☕ [Java] Assinaturas
Esses **dois métodos podem conviver na mesma classe**, uma vez que eles possuem **diferentes assinaturas**. A **assinatura de um método** é determinada pelo *tipo de parâmetros* e pela *ordem em que estes foram declarados*. Desta forma, a assinatura do primeiro método é:```java
area ( int );
```e do segundo:
```java
area ( int , int );
```Diante da diferença de assinaturas, podemos ter dois diferentes contextos para o uso do método de cálculo da área:
1.
System.out.println("Área = " + area( 5 ));No primeiro contexto, é chamado para executar o método área com um único parâmetro e neste caso a avaliação em tempo de execução irá determinar que deve ser usado o cálculo da área do quadrado. Ou seja, aquele que recebe um valor inteiro como parâmetro, e a resposta será:
25
.2.
System.out.println("Área = " + area( 5, 6 ));No segundo contexto, é chamado para executar o método área com um dos parâmetros e, neste caso, a avaliação em tempo de execução irá determinar que deve ser usado o cálculo da área do retângulo. Ou seja, aquele que recebe dois valores inteiros como parâmetro, e a resposta será:
30
.> [!Warning]
> Com o uso da sobrecarga de métodos você poderá criar quantos métodos com o mesmo identificador (nome) quiser em uma mesma classe, desde que eles **não possuam a mesma assinatura de método**.## [Java] Métodos
Se quisesse incluir um método para calcular a **área de uma circunferência**, você não poderia incluir **nesta classe**, pois ele teria a mesma assinatura do método do cálculo da área do quadrado:
O método teria a mesma assinatura do método **área do quadrado** e, no momento da execução, não haveria como saber qual dos dois deveria seria executado, pois ambos teriam o mesmo contexto:
System.out.println("Área Quadrado = " + area( 5 ));System.out.println("Área Circunferência = " + area( 4 ));A linguagem Java não teria como definir qual método executar, já que ambos têm a mesma assinatura e a linguagem não é suficientemente inteligente para tentar buscar isso em algum outro lugar que não o contexto da chamada do método:
area( 5 )e
area( 4 )
Como ambos possuem o mesmo contexto, os métodos com a mesma assinatura não podem compartilhar a mesma classe.
Exemplos de polimorfismo de sobrecarga válidos para uma mesma classe:

As assinaturas são respectivamente:

Todos os métodos acima, apesar de possuírem a mesma quantidade de parâmetros, têm assinaturas diferentes que serão executadas em função de contexto diferentes, respectivamente:

Os valores armazenados em g, h, i e j serão respectivamente: 1, 2, 3 e 4.
## [Java] A sobrecarga de métodos construtores
**Métodos construtores** são métodos e **também podem ser sobrecarregados**.
Uma classe que possui mais de um método construtor é uma classe que oferece diferentes formas de criação para os seus objetos.
Outra forma de uso de *mais de um construtor* é para manter a **compatibilidade de uma classe com suas aplicações antigas**.
Se analisarmos a
classe Carro
, vista como exemplo anteriormente, podemos notar que ela **não possui nenhum método construtor**. Podemos então criar alguns métodos construtores para esta classe e preservar a aplicação antiga, criando e analisando uma classe nova (evoluída) e as duas aplicações, a antiga e a nova:Classe: `Carro` (atualizada com cinco métodos construtores)

Aplicação antiga `AppCarro`
A execução da aplicação não foi afetada pelas mudanças na classe porque foi criado o construtor vazio
public Carro ( ) { }
que garantiu a compatibilidade:Aplicação usando diferentes construtores para criar os objetos: `AppCarro`

> [!Note]
1. O primeiro método construtor criado foi o vazio, para garantir a compatibilidade com a aplicação antiga;
2. Foram incluídos mais quatro métodos construtores seguindo o conceito da sobrecarga de métodos;
3. Na nova aplicação, foram criados cinco diferentes objetos, cada um usando um construtor diferente;
4. Ambas as aplicações funcionaram apesar da alteração;## [Java] Polimorfismo de sobrecarga e a evolução das classes
Com o **polimorfismo de sobrecarga** podemos criar **diferentes implementações para métodos com o mesmo identificador (nome) em uma mesma classe**.Vamos imaginar que uma classe chamada **Login** fosse usada por vários de seus sistemas:
`Login.java`

`AppLogin.java`

Imagine a situação: você tem um *novo cliente*, e seus funcionários fazem o login não apenas com o nome de login e a senha, mas também utilizando um dispositivo eletrônico para geração de senhas (token).
A sua classe não iria funcionar com este novo contexto. Neste caso, a programação orientada a objetos nos ajuda muito, pois faremos uma atualização na classe Login e ela será capaz, não só de atender a esta nova demanda, mas de continuar a atender os antigos clientes.
Classe Atualizada: `Login`
Aplicação: AppLogin com um objeto usando o novo construtor

> [!Note]
>
> 01. A classe Login agora possui dois diferentes construtores e dois diferentes métodos verificaLogin, **ambos sobrecarregados**. A versão anterior foi preservada porque ainda é usada pelos sistemas dos antigos clientes. Entretanto, com a inclusão dos novos métodos, a classe foi atualizada e também passou a atender ao cliente novo;
>
> 02. Foi incluído ainda o método verificarToken, que só é usado pelo cliente novo. Sua inclusão na classe não atrapalha em nada os sistemas dos clientes antigos;
>
> 03. A aplicação agora pode instanciar (criar) objetos das duas formas, com e sem o token, sem que uma atrapalhe a outra.As classes na programação orientada a objetos evoluem conforme precisamos de mais atributos e métodos.
Entretanto, se mantivermos os métodos necessários para os sistemas mais antigos, essa evolução não afetará os outros sistemas e teremos uma melhoria na classe, facilitando a sua evolução e sua manutenção, uma vez que, ao realizar qualquer melhoria em uma classe, basta recompilar as aplicações que estas se tornarão atualizadas.
Exercício: Analisando os métodos abaixo (para uma mesma classe), podemos afirmar que foi aplicado o conceito de polimorfismo de sobrecarga?
double calculo(double p, double a) {return p+a;
}
double calculo(double p, double a) {
return a*p;
}
double calculo(double p, double a, double t) {
return p-a+t;
}
Resolução: Não, apesar dos métodos possuírem o mesmo nome, eles não possuem a mesma assinatura.
Assinaturas:
double calculo(double, double)double calculo(double, double)
double calculo(double, double, double)
# ☕ [Java] Herança
A programação orientada a objetos tem como um dos principais pilares o "reaproveitamento de código". Reaproveitar o código significa menos esforço em seu desenvolvimento e mais facilidade na manutenção do sistema. Ao evitarmos a redundância de código, fica mais fácil gerar alterações, uma vez que não precisaremos modificá-lo em vários locais diferentes.A **herança** é um conceito muito importante que *possibilita identificar duas ou mais classes que possuam semelhanças*. Estas podem ser definidas através de *uma hierarquia*, em que os membros comuns às **duas ou mais classes passam para uma nova classe**, conhecida como **Superclasse** ou **classe “mãe”**.
Já as **classes originais** permanecerão apenas com os membros não comuns, sendo denominadas **Subclasses** ou **classes “filhas”**. Ao aplicar este conceito, podemos trabalhar com uma hierarquia entre as classes, em que as de **maior hierarquia** *aglutinam os membros comuns* e as de **menor hierarquia** possuem *apenas membros distintos entre elas*.
Em algumas linguagens de programação no conceito de POO se trabalha muito com os dois tipos de herança. Entretanto, a linguagem Java não trabalha com Heranças múltiplas, somente com herança simples.
## [Java] Herança Simples
Em Java, temos **apenas** a implementação da *herança simples*. A **herança simples** se caracteriza por **cada classe herdar sempre de apenas uma outra classe por vez**. Devemos observar que, mesmo que tenhamos uma sequência de classes herdando, em que uma herda da outra, ainda assim, temos a herança simples, que pode ser observada nos exemplos das figuras a seguir:



> [!Note]
> Em todos os casos, temos sempre a *herança simples*, pois cada classe sempre herda **apenas** da sua *classe superior*, mesmo se incluirmos novos níveis.## [Java] Herança Múltipla
A **herança múltipla** se caracteriza quando uma mesma classe herda de duas ou mais classes ao mesmo tempo. Java não permite a implementação da herança múltipla; mesmo este sendo um conceito da programação orientada a objetos, algumas linguagens de programação não implementam este conceito. A linguagem C permite a implementação de herança múltipla, mas Java e C#, por exemplo, não permitem esta implementação.A herança múltipla pode ser observada nos exemplos das figuras a seguir:


Vejamos um exemplo:
Sejam as três classes a seguir referentes a uma empresa:
Classe
Atributos
AtributosGerente
Identidade: texto
Nome: texto
Matrícula: texto
Salário: real
PercentualVenda: real
NívelGerente: inteiro
- Setters [para todos os atributos]
- Getters [para todos os atributos]
- Construtores [mínimo 5]
- Imprimir [para exibir os atributos]
- EntradaDados [para todos os atributos]
- Cadastrar [atribui a todos os atributos]
Funcionário
Identidade: texto
Nome: texto
Matrícula: texto
Salário: real
Setor: texto
NomeGerente: texto
- Setters [para todos os atributos]
- Getters [para todos os atributos]
- Construtores [mínimo 5]
- Imprimir [para exibir os atributos]
- EntradaDados [para todos os atributos]
- Cadastrar [atribui a todos os atributos]
Cliente
Identidade: texto
Nome: texto
CódigoCliente: texto
Idade: inteiro
Telefone: texto
- Setters [para todos os atributos]
- Getters [para todos os atributos]
- Construtores [mínimo 5]
- Imprimir [para exibir os atributos]
- EntradaDados [para todos os atributos]
- Cadastrar [atribui a todos os atributos]
Ao analisar as classes, podemos afirmar que teremos que repetir a declaração dos atributos **Identidade** e **Nome** em todas elas, bem como os métodos **Setters** e **Getters** destes atributos em todas as três classes, sem contar que ainda teremos que repetir parte da codificação dos métodos **cadastrar**, **imprimir** e **entradaDados**.
Neste caso, podemos aplicar os conceitos de **herança** e teremos a seguinte estrutura para atender ao problema descrito acima:

- Como os atributos Identidade e Nome são comuns às três classes, eles ficarão na Superclasse de maior hierarquia;
- Como os atributos Matrícula e Salário pertencem somente às classes Gerente e Funcionário, teremos uma classe intermediária chamada PessoaEmpresa, que herdará da classe Pessoa, mas esta também será uma Superclasse para as classes Gerente e Funcionário;
- A classe Cliente herdará da classe Pessoa;
- As classes Gerente e Funcionário herdarão da classe PessoaEmpresa.
Desta forma, não teremos redundância de códigos, escreveremos menos linhas e teremos maior facilidade na manutenção das classes da nossa biblioteca de classes.
Vamos analisar agora as diferenças entre as versões das nossas classes com e **sem a aplicação da herança**:
Classe `Gerente` sem o uso do conceito de herança:

Classe `Funcionário` sem o uso do conceito de herança:

Classe `Cliente` sem o uso do conceito de herança:
Vamos a algumas perguntas:
- Se for necessário incluir um novo atributo com o CPF em todas as classes?
Resposta: Teremos que alterar todas as classes, dificultando a manutenção.
- Se for necessário um novo atributo para armazenar a data de admissão para Gerentes e Funcionários?
Resposta: Será necessário alterar as classes Gerente e Funcionário.
- Se for necessário incluir a data da primeira compra do cliente?
Resposta: Será necessário alterar apenas a classe Cliente.
Vamos guardar estas perguntas e repeti-las após a aplicação da herança.
Aplicando os conceitos de herança: Primeiro de tudo, vamos arquitetar o conceito de superclasses e subclasses:
- **SuperClasse**: Pessoa;
- **SubClasse de Pessoa**: PessoaEmpresa, Cliente;
- **SubClasse de PessoaEmpresa**: Gerente, Funcionario.
Logo, iremos aplicar esse conceito:
Classe `Pessoa` com o uso do conceito de herança (SuperClasse):

Classe `PessoaEmpresa` com o uso do conceito de herança (SubClasse de `Pessoa`):

Classe `Gerente` com o uso do conceito de herança (SubClasse de `PessoaEmpresa`):

Classe `Funcionário` com o uso do conceito de herança (SubClasse de `PessoaEmpresa`):

Classe `Cliente` com o uso do conceito de herança (SubClasse de `Pessoa`):

Vamos responder novamente às três perguntas feitas anteriormente:
- Se for necessário incluir um novo atributo com o CPF em todas as classes?
Resposta: Teremos que alterar apenas a classe Pessoa, uma vez que todas as demais classes irão herdar qualquer atualização nesta classe.
- Se for necessário um novo atributo para armazenar a data de admissão para Gerentes e Funcionários?
Resposta: Será necessário alterar apenas a classe PessoaEmpresa, já que as classes Gerente e Funcionário herdarão desta classe.
- Se for necessário incluir a data da primeira compra do cliente?
Resposta: Será necessário alterar apenas a classe Cliente.
> [!Warning]
> Após analisarmos as duas soluções, chegamos à conclusão de que, ao utilizar a *herança*, não só evitamos a **redundância de códigos (repetição)** como *facilitamos a manutenção*, uma vez que, para realizar qualquer atualização, deveremos sempre alterar **apenas** *uma das classes*.
No final, a **classe Gerente** é composta por todos os membros de **Pessoa**, **PessoaEmpresa** e **Gerente**, uma vez que Gerente estende PessoaEmpresa, que por sua vez estende a classe Pessoa:
Pessoa +
PessoaEmpresa +
Gerente
O mesmo ocorre com a classe Funcionário, composta por todos os membros de Pessoa, PessoaEmpresa e Funcionário, uma vez que Funcionário estende PessoaEmpresa, que por sua vez estende a classe Pessoa:
Pessoa +
PessoaEmpresa +
Funcionário
Já a classe Cliente é composta por todos os membros de Pessoa e Cliente, uma vez que Cliente estende a classe Pessoa:
Pessoa +
Cliente
## [Java] Herança de métodos construtores
Vamos analisar os métodos construtores da classe `Cliente`:

Alguns destes métodos repassam os parâmetros recebidos para a **Superclasse**, através da palavra reservada super
. Esta instrução diz ao compilador que o(s) parâmetro(s) será(ão) repassado(s) a um método construtor com a *mesma assinatura na SuperClasse*.
Assim, o método:



O método:



Por final o método:



Todo método construtor de uma SubClasse deve referenciar um construtor da SuperClasse, isso quer dizer que para o construtor vazio: public Cliente() { }
, será obrigatório que exista um construtor vazio na SuperClasse: public Pessoa() { }
.
## [Java] Sobrescrita de métodos
Métodos de uma *SuperClasse* podem ser **sobrescritos** em suas *subclasses*, implicando que um método descrito na *Superclasse* poderá ser substituído na *Subclasse*. Para isso, é importante observar que estes métodos devem possuir as mesmas assinaturas. Caso contrário, será usado o conceito de *Sobrecarga* e não de *Sobrescrita*.
Vamos analisar o método imprimir da Superclasse Pessoa:

Ele não é suficiente para atender às necessidades da classe Cliente, então poderíamos tê-lo substituído por:

Esta substituição permitiria que o método imprimir fosse substituído na **Subclasse** por um método mais completo que atendesse a sua necessidade.
Note que as assinaturas dos métodos, tanto na **Superclasse Pessoa** como na **Subclasse Cliente**, são idênticas: imprimir( );
desta forma, houve uma *Sobrescrita (Override)* e não uma *Sobrecarga (Overhead)*.
Como uma primeira forma de atender a demanda da classe Cliente, o método imprimir nesta nova versão seria suficiente, mas podemos melhorar nossa solução, observe que parte do código já existe no método imprimir da Superclasse Pessoa, ocorrendo uma redundância de código nas instruções destacadas na cor vermelha:

Note que as instruções na cor vermelha já existem no método imprimir da Superclasse e, assim, podemos reaproveitar os códigos da Superclasse ao chamar o método imprimir da Superclasse na Subclasse:

Ao reaproveitar o método imprimir da Superclasse, temos dois ganhos muito importantes:
1. Não haverá redundância de códigos, as instruções do método imprimir da Superclasse serão reaproveitadas na Subclasse;
2. Caso haja a necessidade de incluir um novo atributo no método imprimir, só precisaremos realizar a alteração em apenas uma classe, pois, se for um atributo específico da classe Cliente, só precisaremos incluir a instrução no método da classe Cliente. Caso contrário, se o atributo for comum às demais classes, a instrução deverá ser incluída apenas na Superclasse Pessoa.
A **herança** é um conceito importantíssimo da programação orientada a Objetos, permitindo que reaproveitemos membros Superclasse, que serão herdados pelas Subclasses, evitando redundância de códigos, além de facilitar a manutenção das nossas classes, uma vez que qualquer necessidade de mudança implicará na alteração de apenas uma classe.
# ☕ [Java] Particionamento
Classe
Atributos
Desktop
MarcaPlacaMae : texto
ModeloPlacaMae : texto
PrecoPlacaMae: real
TipoProcessador : texto
MarcaHD : texto
ModeloHD : texto
PrecoHD : real
TipoHD : texto
CapacidadeHD : inteiro
MarcaPlacaVideo : texto
ModeloPlacaVideo : texto
PrecoPlacaVideo : real
Padrao : texto
MarcaMemoria : texto
ModeloMemoria : texto
PrecoMemoria: real
TipoMemoria : texto
CapacidadeMemoria : inteiro
É a decomposição de classes extensas em classes menores, que podem ser mais bem reaproveitadas em outras classes, além de permitir melhor controle e manutenção.
O **particionamento de classes** nos permite criar objetos menores e mais simples, que poderão ser reunidos em conjunto, capazes de criar novas classes, maiores e mais complexas. O particionamento de classes em Java pode ter diferentes significados dependendo do contexto. Geralmente, ele se refere a técnicas de organização, modularização e otimização de código, incluindo:
1. Divisão de classes em pacotes (Organização modular)
2. Uso de classes aninhadas e internas
3. Particionamento em sistemas distribuídos (Microservices, RMI, etc.)
4. Divisão em múltiplos arquivos e JARs (Modularização e encapsulamento)
Vamos imaginar um computador do tipo `Desktop`, que é um objeto bem complexo, com diferentes partes. Muitas dessas partes são usadas por outros objetos também, tal como `Notebooks` e `Servidores`.
Um `HD` (HardDisk), por exemplo, pode ser usado por cada um deles, assim como a placa de vídeo, a placa-mãe, o vídeo, a memória, além de outros dispositivos. Se formos criar uma classe para representar um Desktop, teremos uma classe com muitos atributos, o que a tornaria grande e complexa, com muitas linhas de código e de difícil manutenção. Isso porque, se fosse necessário realizar alguma mudança, teríamos que trabalhar em uma classe altamente complexa. Outro ponto importante seria a criação das classes Notebook e Servidor, que seriam igualmente complexas, sem contar que, para realizar uma alteração em um único componente que fosse, teríamos que fazê-la em todas as três classes (Desktop, Notebook e Servidor).
Inicialmente, nossa classe desktop ficaria com os seguintes atributos:
Podemos analisar como ficará a classe `Desktop`. Declaração dos atributos da classe `Desktop`:

A classe ficou extensa, complexa, sem contar que precisaremos ainda incluir os métodos de acesso (Setters e Getters), construtores, entradaDados, imprimir, cadastrar, além de outros métodos que possam se fazer necessários, sem contar a grande quantidade de atributos.
Observe que muitos dos atributos são comuns a todas as classes e seria necessário diferenciar os atributos que possuem o mesmo nome, tal como: modeloPlacaMae; modeloHD, modeloPlacaVideo, modeloMemoria etc.
O problema ainda poderia ser maior, se tivéssemos mais slots de memória, com modeloMemoria0, modeloMemoria1, modeloMemoria2, modeloMemoria3, para quatro slots, por exemplo. Essas dificuldades aumentam bastante o tamanho da classe e a sua complexidade; além disso, neste exemplo, começaríamos com 18 atributos, sem levar em consideração a questão dos diferentes slots de memória.
Entretanto, se pensarmos segundo a ótica do particionamento, podemos dividir a classe Desktop, grande e complexa, em classes menores e mais simples. Como sugestão, poderíamos criar as classes que foram apresentadas como exemplo no parágrafo anterior em:
- Placa-mãe (PlacaMae);
- Disco Rígido (HD);
- Placa de vídeo (PlacaVideo);
- Memória (Memoria).
A classe Desktop seria então decomposta (particionada) da seguinte forma:

Vamos determinar poucos atributos para cada uma delas, apenas para entendermos melhor o conceito.
Classe
Atributos
PlacaMae
Marca : texto
Modelo : texto
Preco : real
TipoProcessador : texto
HD
Marca : texto
Modelo : texto
Preco : real
Tipo : texto
Capacidade : inteiro
PlacaVideo
Marca : texto
Modelo : texto
Preco : real
Padrão : texto
Memoria
Marca : texto
Modelo : texto
Preco : real
Tipo : texto
Capacidade : inteiro
Assim, vamos criar as classes separadamente, dividindo a classe Desktop conforme proposto:

> [!Warning]
> 1. A decomposição da classe Desktop foi feita em quatro classes mais simples;
> 2. Os nomes dos atributos puderam ser mantidos na forma original, sem que um interfira no outro;
> 3. Cada classe mais simples se torna mais fácil de codificar;
> 4. As classes criadas serão mais fáceis para se realizar qualquer tipo de manutenção.
Outro ponto muito importante é que todas essas classes criadas através do particionamento poderão ser reaproveitadas para as classes Notebook e Servidor. Dessa forma, a nossa biblioteca de classes poderá criar novos objetos quando reunida em conjunto.
# ☕ [Java] Agregação
A programação orientada a objetos nos ajuda a resolver de forma mais simples problemas com alta complexidade. A **agregação de classes** é um conceito voltado a facilitar a solução de problemas muito complexos. Podemos *dividir* uma classe em classes menores, particionando esta classe em diversas outras classes mais simples, para posteriormente as reunirmos em conjunto, formando uma classe maior e mais complexa.
A reunião de uma ou mais classes para formar novas classes é chamada de agregação. Uma nova classe pode ser formada por um conjunto de diferentes objetos. Seguindo nosso exemplo, poderíamos reaproveitar as classes PlacaMae, HD, PlacaVideo, e Memória para criar novas classes, como Desktop, Notebook e Servidor, como nos exemplos a seguir:

> [!Warning]
> 1. Foram incluídos novos atributos para entendermos que a agregação pode incluir não apenas classes, mas também novos atributos;
>
> 2. Como toda classe é também um tipo, podemos declarar atributos como do tipo `Classe`;
>
> 3. Cada atributo criado a partir de uma classe (não sendo de tipos básicos) é uma agregação à classe principal, sendo assim, temos as quatro agregações (`PlacaMae`, `HD`, `PlacaVideo` e `Memoria`) para cada classe principal;
>
> 4. As classes `Desktop`, `Notebook` e `Servidor` foram criadas a partir de fragmentos menores, mas todos os atributos originais da classe `Desktop` estão presentes.
Uma oportunidade se apresenta com o uso da agregação, que não é possível resolver facilmente com a herança. Não confunda herança com agregação, pois são conceitos diferentes.
Imagine agora a situação dos slots de memória: na herança, só poderíamos herdar uma memória, mas, com a agregação, podemos criar quantas memórias quisermos, como no exemplo a seguir. Para a classe Memoria foram criados apenas os métodos de Acesso (Setters e Getters) para facilitar o entendimento:

1. O Desktop possui quatro slots de memória;
2. Como cada slot é um objeto diferente, eles possuem propriedades diferentes, sem que nenhum tenha relação direta com os demais;
3. Não é comum montar um computador dessa forma, mas foram definidas marcas e capacidades diferentes para indicar que os valores armazenados não sofrem influência dos demais objetos agregados.
Agora que vimos como aplicar os conceitos de agregação e particionamento, podemos aplicar também o conceito de herança e melhorar nossas classes. Vamos aplicar o conceito de herança às nossas classes particionadas para termos uma aplicação mais concisa e mais fácil para realizar manutenções.
# ☕ [Java] Classes Particionadas

Devemos identificar os atributos comuns às classes, sendo que os atributos Marca, Modelo e Preço são comuns a todas as classes. Já os atributos Tipo e Capacidade são comuns apenas às classes HD e Memoria, sendo necessária uma classe intermediária. O atributo TipoProcessador pertence apenas à classe PlacaMae e o atributo Padrão pertence apenas à classe PlacaVideo. As classes HD e Memoria não terão atributos específicos, ficarão apenas nas superclasses.
Classes Particionadas redefinidas após a aplicação da Herança

Classe Particionada `Identificação`:

Classe Particionada `Armazenamento`:

Classe Particionada `PlacaMae`:

Classe Particionada `PlacaVideo`:

Classe Particionada `Memoria`:

Com a evolução das classes particionadas com a aplicação do conceito de herança, foram mantidas as compatibilidades e as classes Desktop, Notebook e Servidor não precisam ser alteradas, assim como a aplicação, que funcionará da mesma forma que no exemplo anterior. As mudanças com a evolução das classes particionadas não afetaram as classes agregadoras, nem a aplicação.
Caso seja necessária alguma alteração específica, basta alterar a classe afetada. Entretanto, se for alguma alteração sobre as classes Memoria e HD, basta alterar a classe Armazenamento; caso seja uma alteração que afete ao mesmo tempo as quatro classes particionadas, basta alterar a superclasse Identificação.
Temos então um melhor controle sobre a aplicação e maior facilidade de manutenção de todo o sistema.
# ☕ [Java] Encapsulamento
O **encapsulamento** permite uma classe encapsular atributos e métodos, ocultando os detalhes de implementação dos objetos. Trabalharemos também os tipos de visibilidade de membros de uma classe: public
, protected
, private
e package
. Desenvolveremos uma aplicação utilizando o conceito de encapsulamento em conjunto com os conceitos de herança e agregação.
No desenvolvimento de aplicações, temos situações nas quais a segurança é muito importante. Em muitas situações os membros de uma classe (atributos e métodos) precisam ter o seu acesso restringido para que não sejam burlados por meio das aplicações. Esse processo de limitação de acesso aos membros de uma classe é chamado de **Programação Orientada a Objetos de Encapsulamento**.
“Encapsulamento trata-se de um mecanismo que possibilita restringir o acesso a variáveis e métodos da classe (ou até à própria classe). Os detalhes de implementação ficam ocultos ao usuário da classe, isto é, o usuário passa a utilizar os serviços da classe sem saber como isso ocorre internamente. Somente uma lista das funcionalidades existentes torna-se disponível ao usuário da classe.” - (FURGERI, 2015)
Encapsulamento é o processo de separação dos membros de uma classe através da restrição ao seu acesso. Pode ocultar os atributos e métodos de uma classe, evitando que dados e detalhes de implementação de métodos sejam vistos (acessados diretamente) pela aplicação ou outras classes. Uma classe encapsula atributos e métodos, ocultando os detalhes de implementação dos objetos. Como um dos princípios do desenvolvimento orientado a objetos, o encapsulamento determina que a implementação de um objeto somente deve ser acessada através de uma interface visível e bem definida. Portanto, esse tipo de conceito é o mais seguro entre todo os conceitos da POO.
Os atributos não devem ser manipulados diretamente, podendo ser alterados ou consultados somente através dos *métodos de acesso (Setters e Getters)* do objeto. Ao restringir o acesso direto aos atributos de uma classe, evitamos que eles sejam manipulados diretamente pela aplicação ou outras classes, não permitindo que possam receber um valor qualquer, principalmente, valores inválidos para o seu contexto. Dessa forma, aumentamos a segurança e a confiança sobre os valores atribuídos.
Por exemplo: Na classe `Exemplo` temos o seguinte atributo

Na aplicação:

Saída:
Idade = -20
1. Como não temos restrição sobre o atributo Idade, a aplicação poderá realizar um acesso direto ao atributo;
2. Na aplicação foi realizado um acesso direto ao atributo, sem o uso de um método de acesso (Setter), e o valor atribuído não é válido porque uma pessoa não pode ter idade negativa;
3. Acesso direto aos atributos de uma classe não permitem que sejam realizadas críticas ao valor antes da atribuição, fazendo com que o valor atribuído diretamente (acesso direto) não tenha qualquer tipo de validação.
*Métodos* podem ter sua visibilidade restrita para evitar que detalhes de implementação ou um possível uso indevido possam ser realizados por outras classes ou aplicações. É muito comum que um método aparentemente simples, tal como calcularImposto(double valor, tipoProduto)
não se restrinja a um simples cálculo. Ao se definir o tipoProduto
, podemos ter diversas e diferentes formas de calcular o imposto sobre esse produto. Podemos então criar um método principal calculaImposto()
e, através dele, chamar diversos outros métodos, cada um responsável por calcular o imposto para um determinado tipo de produto.
Realizar o acesso diretamente aos métodos chamados por calculaImposto()
pode exigir muito conhecimento por parte do desenvolvedor que irá usar a classe, mas nem sempre este desenvolvedor terá os conhecimentos técnicos necessários. Provavelmente, houve o apoio de um especialista em processos fiscais para que o desenvolvedor original da classe pudesse desenvolver os métodos previstos. Para evitar o uso indevido de métodos que possam ser usados de forma equivocada ou com restrição de segurança, alteramos a visibilidade desses métodos.
Por exemplo: A classe `Tributos`

Na aplicação:

Saída:
Digite o preco:1000
Imposto incorreto:
Valor do imposto [atribuição direta] = -20.0
Imposto correto, mas método errado
A faixa estava correta por acaso, porque 00 = 0
Valor do imposto[método errado 00]= 45.0
Imposto e método errados
Valor do imposto [método errado 11]= 131.0
Imposto correto, por acaso, porque 22 foi para a última faixa
Valor a pagar [método errado 22]= 281.0
Imposto e método corretos
Valor a pagar [método correto 00]= 45.0
Imposto e método corretos
Valor a pagar [método correto 00]= 128.0
Imposto e método corretos
Valor a pagar [método correto 00]= 281.0
1. Imaginemos que tipo de produto é um valor entre os seguintes: [00, 01, 02, 10, 11, 12, 20, 21 e 22], em que o primeiro dígito se refere ao tipo do produto e o segundo à faixa do imposto;
2. Primeiro, o método calculaImposto()
irá determinar o método a ser usado para cada tipo de imposto, e a faixa será usada dentro do método específico;
3. O método específico será chamado pelo *método principal*, que retornará o valor correto do imposto;
4. Se um desenvolvedor sem conhecimento correto usar diretamente um dos métodos específicos, o cálculo poderá resultar em um valor errado, pois dificilmente ele irá determinar a faixa correta, pois acabará por passar como parâmetro o tipo do produto e não a faixa;
5. Se os testes forem realizados apenas com os tipos entre 00 e 02, provavelmente o resultado estará correto porque será passada apenas a faixa, mas, para os demais casos, de 10 a 12 ou de 20 a 22, provavelmente o resultado será 0 (zero) ou calculado pela última faixa;
6. Uma aplicação ainda poderá burlar os cálculos da classe, simplesmente determinando um valor ao atributo imposto [tributo.imposto = -20;], o que resultaria em um imposto incorreto, aumentando o preço do produto; como o imposto é negativo e a operação aritmética é de subtração, haverá um sobre preço sobre o valor.
A falta de conhecimento sobre o uso de uma classe pode gerar erros porque, mesmo realizando testes, as faixas com problemas podem não ser identificadas. Para isso, devemos ocultar parte da implementação da classe.
Atributos não devem ser visíveis por nenhum objeto que não seja instância da própria classe ou de uma classe descendente (herança).
Na linguagem Java, temos quatro diferentes tipos de encapsulamento:
Nível de restrição:
Tipo de Encapsulamento (modificador):
Menor restrição:
↕
Maior restrição:
1. public – acesso irrestrito;
2. (vazio ou omissão) – acesso padrão (package);
3. protected;
4. private.
A relação apresentada está em ordem de nível de restrição, indo do *menos restrito* (public
) até o mais *restrito* (private
).
Visibilidade:
- **Public**: Uma classe definida como public pode ser acessada por qualquer classe ou aplicação, sem restrições. Seus membros são igualmente acessíveis (visíveis) por qualquer outra classe ou aplicação. Determina o nível menos restritivo de acesso e visibilidade aos membros (atributos e métodos) de uma classe;
- **Private**: Um membro definido como privado só pode ser acessado por membros da própria classe, ou seja, apenas métodos existentes na própria classe poderão ter acesso (visibilidade) aos atributos e métodos definidos como private. É o nível com maior restrição, pois nem mesmo subclasses dessa classe terão visibilidade sobre esses membros;
- **Protected**: Um membro definido como protegido pode ser acessado apenas por membros da própria classe, das suas subclasses e por outras classes ou aplicações que estejam no mesmo pacote (package);
- **Default** (padrão, omissão): Quando não é usado um modificador de encapsulamento, a visibilidade é dita padrão e os membros têm visibilidade, ou seja, só podem ser acessados por classes e aplicações que estejam no mesmo pacote.
Exemplos de visibilidade de membros:
- **Membros públicos**: é a forma normal para métodos de acesso *(Setters e Getters)*.

- **Membros com visibilidade padrão**: devemos evitar o uso do acesso padrão, para que tenhamos sempre a visibilidade definida.

- **Membros privados**: é a forma normal para os atributos de classe que *não terá subclasses*, mas não é adequada para os métodos de acesso (*Setters e Getters*).

- **Membros protegidos**: é a forma normal para os atributos de classe que terão subclasses, mas também não é adequada para os métodos de acesso *(Setters e Getters)*.

Como vimos, o encapsulamento determina a **visibilidade de classes** ou de **seus membros**. É comum protegermos os atributos de uma classe para que eles não tenham *acesso direto*, e os *valores* a serem atribuídos possam ser analisados por um método antes da atribuição.
# ☕ [Java] Pacotes
**Pacotes**, em Java, são usados para facilitar o armazenamento e controle da biblioteca de classes. Como vimos até o momento, nossa biblioteca de classes vem crescendo e, dessa forma, várias classes foram criadas e estão em diferentes locais. É necessário organizarmos nossas classes e, para isso, podemos usar os pacotes.
Pacotes não passam de uma *estrutura de diretórios* em que colocamos as nossas classes por afinidade. Por afinidade devemos entender que são classes com algum tipo de aderência, similaridade ou que pertencem a um mesmo assunto.
No projeto criado para nossos exemplos dessa unidade, temos a seguinte estrutura:

Note que temos apenas um pacote, o pacote default do projeto, e todas as classes estão juntas. Para criarmos novos pacotes, basta clicar sobre o projeto e escolher **New / Package**, como você pode observar na imagem a seguir.

Como foram criados três grupos de classes para os exemplos, vamos separar nossas classes em três pacotes, sendo eles: **parte1**, **parte2** e **parte3**.

Basta agora arrastar as classes para seus respectivos pacotes.

Fisicamente, os arquivos ficarão dentro dos respectivos diretórios, podendo ainda ser criados subpacotes. A separação em pacotes permite duas ou mais classes com o mesmo nome, bastando que elas estejam em diferentes pacotes.


Quando temos nossas classes separadas em pacotes, sempre que precisarmos usá-las devemos importar a(s) classe(s) de seus respectivos pacotes.
Dessa forma, criamos um pacote apenas para as aplicações e transferimos as aplicações de encapsulamento para esse pacote. Note que as classes agora estão apresentando erros.

Isso ocorre porque as aplicações não estão encontrando as respectivas classes e, para que elas sejam encontradas, devemos importar as classes:
Para a aplicação do primeiro exemplo:
import parte1.Exemplo;
Para a aplicação do segundo exemplo:
import parte2.Tributos;
Outro ponto importante é que os atributos das classes **Exemplo** e **Tributos** estavam com a visibilidade padrão e, para continuar a funcionar, é necessário alterar a visibilidade dos atributos para pública (public), uma vez que essas classes agora estão em diferentes pacotes.
# ☕ [Java] Classes abstratas e interfaces
As **classes abstratas** são usadas como moldes para a criação de outras classes e podem encapsular atributos e comportamentos comuns. Já **interface** é um recurso muito utilizado em Java. Uma classe pode implementar várias interfaces.
## [Java] Modificadores: `static` e `final`
Anteriormente, conhecemos os *modificadores de acesso* ou de *visibilidade* (Encapsulamento). Agora iremos conhecer mais alguns modificadores que podem ser aplicados sobre classes, no caso do final e sobre membros (Atributos e Métodos) de uma classe. Esses modificadores, como o nome já diz, servem para alterar a forma de uso de classes, métodos e/ou atributos.
## [Java] Referência `.this`
O uso do this
em Java é para ajudar na questão das referências (endereçamento) de memória. O this
é um ponteiro (variável que armazena endereço de memória) de forma implícita. Java não possui *endereçamento direto de memória* (endereçamento explícito), apenas o *endereçamento indireto* (implícito) de memória. A referência this
então é uma referência implícita ao endereçamento de memória de um objeto.
A referência this
altera a identificação do objeto pelo seu identificador (nome) e pelo endereço de memória do objeto, e *só pode referenciar membros da classe*, ou seja, somente faz referência a *atributos e métodos*.
Dessa forma, podemos substituir o nome do objeto pela sua referência, e também separar quando tem o mesmo nome variáveis e atributos:

Nota: O this
separa os membros da classe das demais variáveis auxiliares e parâmetros da classe.
## [Java] O modificador: `static`
Este modificador pode ser aplicado sobre atributos e métodos e transforma o atributo ou método para a forma “compartilhada”.
O modificador static aplicado a um atributo. Este modificador ao ser aplicado em um atributo de uma classe modifica este atributo de objeto (ou de instância) para um atributo de classe1.
Exemplo 1 - Aplicação com o compartilhamento de um valor: private static double valorDolar
Classe Cotacao (pacote: biblioteca):

Classe Exemplo1 (pacote: aplicacao):

Execução:
Qual é o Valor do dólar ?3.82
Quantos reais para a conversão ?
1000
Valor do dólar :3.82
Coversão de Real para dólar : US$ 261,78
Valor do dólar :4.15
Coversão de Real para dólar : US$ 240,96
Valor do dólar :4.15
Coversão de Real para dólar : US$ 240,96
> [!Note]
> 1. Primeiramente foi utilizado valor de R$ 3,82 para a cotação do dólar, com a leitura através do teclado para o objeto cot_1, e foi calculada a conversão do valor de R$ 1000,00, resultando em: US$ 261,78;
>
> 2. Depois, foi criado o objeto cot_2, com o valor da cotação do dólar definido através do método construtor em R$ 4,15, e foi mantido o mesmo valor de R$ 1000,00 para a conversão;
>
> 3. Ambos os objetos deram o mesmo resultado porque o valor do dólar para a conversão era o mesmo, de R$ 4,15. Isso ocorreu pois, ao alterar o valor da cotação do dólar através do objeto cot_2, o objeto cot_1 também foi afetado, já que o atributo valorDolar é compartilhado: private static double valorDolar;
Como o atributo valorDolar é compartilhado, o atributo deixa de ser um atributo de objeto (propriedade) com valor próprio a cada objeto, e passa a ser um atributo de classe, ou seja, passa a ser compartilhado por todas as instâncias. Internamente é criado um ponteiro implícito em que todos os atributos compartilhados da classe apontam para o mesmo endereço de memória. Por isso, qualquer objeto que realize uma alteração em um atributo compartilhado afetará todos os demais objetos criados a partir da mesma classe na aplicação.
A referência this
não pode ser usada com *atributos estáticos (static)*, apenas *atributos não compartilhados*, porque a referência dos atributos compartilhados não pertence ao objeto, mas sim à classe.
Exemplo 2 - Aplicação com um contador de objetos criados: private static int contador
Classe Teste (pacote: biblioteca):

Classe Exemplo2 (pacote: aplicacao)

Contador =1Contador =2
Contador =3
Contador =4
Contador =5
Contador =5
> [!Note]
> 1. Foram criados cinco objetos, mas em nenhum caso foi feito um acesso direto para o atributo contador; cada objeto, ao ser criado, incrementava o contador;
>
> 2. Conforme foram sendo criados os objetos, o valor compartilhado do atributo contador ia sendo atualizado para todos os objetos;
>
> 3. Após a criação do 5º objeto, o objeto t5, o atributo do objeto t1, assim como os demais, compartilhavam o mesmo local de memória para buscar o valor do atributo; por isso, todos os objetos encerraram a aplicação retornando o valor 5 para o atributo contador compartilhado (`static`).
# ☕ [Java] Tratamento de exceções
O **tratamento de exceções** é um importante recurso que permite criar programas tolerantes a falhas. Trata-se de um mecanismo que permite resolver ou ao menos lidar com exceções, muitas vezes evitando que a execução do software seja interrompida, e ela está presente na maioria das linguagens de programação. A linguagem Java se baseou mna linguagem C++ para o tratamento de exceções.
Uma **exceção** é uma condição causada por um erro em tempo de execução que interrompe o fluxo normal de software. Não se trata de um desvio normal para um fluxo alternativo, mas sim previsto ao tempo de execução, ou seja, a exceção leva ao estado desejado. E, esse erro pode ter muitas causas como receber valor `0` ou uso indevido de um array.
Quando uma exceção é criada no Java é criado um objeto chamado de `Exception object` que contém informações sobre o erro, seu tipo e o estado do programa quando o erro ocorreu. Após ser criado esse objeto é entregue para o sistema de execução da máquina virtual Java. E todo esse processo é chamado de lançamento de exceção. Uma vez que a exceção é lançada para o método, o sistema de execução na máquina virtual Java procura na pilha de chamadas "`call stack`" por um método que contém o código para tratar essa exceção.
O bloco de código que tem por finalidade tratar essa exceção é chamado de `Exception handler` (tratador de exceções) e quando ele está rodando é verificado se o tipo de objeto é o mesmo do tratador de excessão pode tratar, se for, ele é considerado adequado e a exceção é passada. Quando o tratador de exceção recebe uma exceção para tratar diz que ele captura uma excessão, por isso, o bloco que trata exceções é designado pela instrução `catch` (capturar).
Caso se o tratador não for adequado para tratar aquela exceção, a busca prossegue até que a máquina virtual Java encontre até que o `exception handler` capaz de tratar a exceção. Se nenhum for encontrado, então, a excessão é entregue ao tratador de exceções padrão da máquina virtual Java. Que imprime as informações de exceção e encerra o programa.
Embora, os recursos de tratamento de exceção não seja a única maneira de lidar com erros de software, ela permite algumas vantagens, tais como:
- A separação de erro do código destinada ao tratamento de erros da execução do software, isso melhora a organização e contribui para facilitar a resolução de problemas.
- propagar o erro para cima na pilha de chamadas, entregando o objeto da exceção diretamente ao método que tem interesse na sua ocorrência, tradicionalmente o código do erro teria que ser propagado método a método no código.
> Para melhor absorção do conhecimento, recomenda-se o uso de computador com o Java Development Kit (JDK) e um IDE (Integrated Development Environment) instalados.
A documentação oficial da linguagem Java explica que o termo exceção é uma abreviatura para a frase **evento excepcional** e o define como “um evento, o qual ocorre durante a execução de um programa, que interrompe o fluxo normal das instruções do programa” (ORACLE AMERICA INC., 2021).
A definição de exceção em software apresentada por Java não é específica da linguagem. Sempre que um evento anormal causa a interrupção no fluxo normal das instruções de um software, há uma exceção. Porém, nem todas as linguagens oferecem mecanismos para lidar com tais problemas. Outras oferecem mecanismos menos sofisticados, como a linguagem C++.
A linguagem Java foi concebida com o intuito de permitir o desenvolvimento de programas seguros. Assim, não é de se surpreender que disponibilize um recurso especificamente projetado para permitir o tratamento de exceções de software. Esse será o objeto de nosso estudo, que buscará lançar as bases para que o futuro profissional de programação seja capaz de explorar os recursos da linguagem Java e produzir softwares de qualidade.
Hierarquia de exceções: No Java, todas as exceções são representadas por classes que fazem parte da hierarquia de `Throwable`. A estrutura principal dessa hierarquia é:
```
java.lang.Throwable
├── java.lang.Error (Exceções da JVM, não tratáveis)
└── java.lang.Exception (Exceções tratáveis)
├── Checked Exceptions (Obrigam tratamento)
├── java.lang.RuntimeException (Unchecked Exceptions)
```
A classe `Throwable` (Raiz da Hierarquia) é a **superclasse de todas as exceções e erros**. Ela define métodos comuns, como `getMessage()` e `printStackTrace()`.
Subtipos diretos de `Throwable`:
✅ `Error` → Indica problemas sérios que o programa **não deve capturar**.
✅ `Exception` → Indica problemas que podem ser **capturados e tratados**.
Error (Erros de Sistema) Os **erros** representam falhas graves da JVM ou do sistema, como falta de memória ou problemas na inicialização da classe.
Exemplos de `Error`:
- `StackOverflowError` → Quando há recursão infinita.
- `OutOfMemoryError` → Quando a JVM fica sem memória.
- `NoClassDefFoundError` → Quando uma classe necessária não pode ser carregada.
> [!Caution]
> Evite capturar `Error`, pois são problemas do sistema, não da aplicação!
Exception (Exceções Verificadas e Não Verificadas) a classe `Exception` representa exceções que **podem ser tratadas** pelo programa.
Ela se divide em duas categorias:
1️. `Checked Exceptions` (Verificadas)
2️. `Unchecked Exceptions` (Não Verificadas)
`Checked Exceptions` (Exceções Verificadas) são **checadas pelo compilador** e obrigam o uso de `try-catch` ou `throws`.
Exemplos de `Checked Exceptions`:
- `IOException` → Problemas de entrada/saída.
- `SQLException` → Erros em bancos de dados.
- `FileNotFoundException` → Arquivo não encontrado.
Exemplo de tratamento obrigatório:
```java
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
public class Teste {
public static void main(String[] args) {
try {
FileReader file = new FileReader("arquivo.txt");
} catch (IOException e) {
System.out.println("Erro ao abrir arquivo: " + e.getMessage());
}
}
}
```
Se não tratar, o código nem compila!
`Unchecked Exceptions` (Exceções Não Verificadas) também chamadas de **Runtime Exceptions**, não são forçadas pelo compilador e podem ser evitadas com boas práticas.
Exemplos de `Unchecked Exceptions`:
- `NullPointerException` → Quando uma variável `null` é usada.
- `ArrayIndexOutOfBoundsException` → Acesso a índice inválido em arrays.
- `ArithmeticException` → Erro matemático, como divisão por zero.
Exemplo sem tratamento obrigatório:
```java
public class Teste {
public static void main(String[] args) {
String texto = null;
System.out.println(texto.length()); // NullPointerException
}
}
```
> [!Warning]
> O código compila, mas falha em tempo de execução!
Criando Exceções Personalizadas: Podemos criar nossas próprias exceções estendendo `Exception` (checked) ou `RuntimeException` (unchecked).
Exemplo de `Checked Exception` personalizada:
```java
class MinhaExcecao extends Exception {
public MinhaExcecao(String mensagem) {
super(mensagem);
}
}
public class Teste {
static void validar(int idade) throws MinhaExcecao {
if (idade < 18) {
throw new MinhaExcecao("Idade mínima é 18!");
}
}
public static void main(String[] args) {
try {
validar(16);
} catch (MinhaExcecao e) {
System.out.println("Erro: " + e.getMessage());
}
}
}
```
Obrigatório tratar (`throws` ou `try-catch`).
Exemplo de `Unchecked Exception` personalizada:
```java
class ErroDeNegocioException extends RuntimeException {
public ErroDeNegocioException(String mensagem) {
super(mensagem);
}
}
public class Teste {
static void processar(int valor) {
if (valor < 0) {
throw new ErroDeNegocioException("Valor negativo não permitido!");
}
}
public static void main(String[] args) {
processar(-5); // Lança a exceção sem obrigar try-catch
}
}
```
Não precisa de tratamento obrigatório.
| Tipo | Tratamento Obrigatório? | Exemplo |
|------|-----------------|---------|
| `Error` | 🚫 Não | `OutOfMemoryError` |
| `Checked Exception` | ✅ Sim | `IOException`, `SQLException` |
| `Unchecked Exception` | 🚫 Não | `NullPointerException`, `ArithmeticException` |
# 🤖 [Java] Primefaces
O **PrimeFaces** é uma biblioteca de componentes de interface de usuário (UI) de código aberto que é usada no desenvolvimento de aplicativos web Java Enterprise Edition (Java EE). Ela fornece uma ampla variedade de componentes de interface de usuário ricos e predefinidos que podem ser facilmente incorporados em aplicativos web Java, permitindo aos desenvolvedores criar interfaces de usuário atraentes e funcionais de maneira eficiente.
Alguns dos principais recursos e componentes fornecidos pelo PrimeFaces incluem:
- Componentes de interface de usuário: Botões, tabelas, árvores, menus, gráficos, calendários e muitos outros componentes prontos para uso, que podem ser personalizados e estilizados de acordo com as necessidades do aplicativo.
- Ajax e atualizações dinâmicas: O PrimeFaces facilita a criação de aplicativos web interativos, permitindo a atualização de partes específicas da página sem a necessidade de recarregar a página inteira, graças ao uso do Ajax.
- Temas e estilos: O PrimeFaces oferece suporte a temas personalizáveis que permitem aos desenvolvedores escolher entre várias opções de aparência para seus aplicativos.
- Integração com o Java EE: O PrimeFaces é especialmente projetado para ser usado em conjunto com tecnologias Java EE, como JavaServer Faces (JSF), e funciona bem com servidores de aplicativos Java EE, como o Apache Tomcat, o WildFly (anteriormente conhecido como JBoss) e o Oracle WebLogic.
- Comunidade ativa: O projeto PrimeFaces possui uma comunidade ativa de desenvolvedores e uma documentação abrangente, o que facilita o aprendizado e a resolução de problemas.
O PrimeFaces é uma opção popular para o desenvolvimento de aplicativos web Java que desejam fornecer interfaces de usuário ricas e interativas. Ele é amplamente usado em várias indústrias e fornece uma base sólida para a criação de aplicativos web empresariais.
# 🥛 [Java] Kotlin
**Kotlin** é uma linguagem de programação moderna, concisa e segura que foi projetada para interoperar plenamente com o Java. Desenvolvida pela JetBrains, Kotlin se tornou uma escolha popular para o desenvolvimento de aplicativos Android, servidores, web e desktop. Kotlin é uma linguagem versátil e poderosa que tem ganhado popularidade rapidamente devido à sua concisão, segurança e interoperabilidade com Java. É amplamente adotada para desenvolvimento Android, mas também é adequada para back-end, web e desenvolvimento multiplataforma.
Principais Características da Linguagem Kotlin:
1. **Interoperabilidade com Java**: Kotlin é totalmente compatível com Java, o que significa que você pode chamar código Java a partir de Kotlin e vice-versa. Isso facilita a integração com projetos existentes em Java.
2. **Sintaxe Concisa**: Kotlin foi projetada para ser mais expressiva e concisa do que Java. Muitas construções comuns podem ser escritas de forma mais curta e clara em Kotlin.
3. **Segurança Nula**: Um dos principais problemas em Java é o null pointer exception (NPE). Kotlin aborda isso de forma nativa com seu sistema de tipos que diferencia entre tipos que podem ser nulos e tipos que não podem ser nulos.
4. **Funções de Extensão**: Permitem que você adicione novas funcionalidades a classes existentes sem ter que herdá-las ou usar padrões de design como Decorator.
5. **Suporte para Programação Funcional**: Kotlin suporta muitos paradigmas de programação funcional, incluindo lambdas, funções de alta ordem, e imutabilidade.
6. **Coroutines para Programação Assíncrona**: Coroutines facilitam a escrita de código assíncrono, permitindo a execução de operações de longo prazo, como chamadas de rede ou operações de E/S, de maneira não bloqueante.
7. **Null Safety**: O sistema de tipos do Kotlin tem null safety integrada, reduzindo a probabilidade de erros de referência nula.
8. **Estrutura Moderna de Linguagem**: Kotlin possui várias características modernas de linguagem, como propriedades, delegados, data classes, sealed classes, entre outros, que ajudam a escrever código mais limpo e expressivo.
Exemplo de Código em Kotlin:
```kotlin
// Declaração de uma função de extensão para a classe String
fun String.lastChar(): Char = this[this.length - 1]
fun main() {
// Declaração de uma variável imutável (val)
val name: String = "Kotlin"
// Uso da função de extensão
println(name.lastChar()) // Saída: n
// Exemplo de null safety
var nullableName: String? = null
println(nullableName?.length) // Saída: null
// Exemplo de uma data class
data class User(val name: String, val age: Int)
val user = User("John Doe", 30)
println(user) // Saída: User(name=John Doe, age=30)
// Uso de coroutines
runBlocking {
launch {
delay(1000L)
println("World!")
}
println("Hello,")
}
}
```
Ecossistema Kotlin:
1. **Desenvolvimento Android**: Kotlin é a linguagem preferida para o desenvolvimento de aplicativos Android. A Google anunciou suporte total para Kotlin como uma linguagem de primeira classe para o desenvolvimento Android em 2017.
2. **Kotlin/JS**: Permite escrever código Kotlin que pode ser compilado para JavaScript, possibilitando o desenvolvimento front-end.
3. **Kotlin/Native**: Permite a compilação de código Kotlin para binários que podem rodar sem uma Máquina Virtual Java (JVM), permitindo o desenvolvimento para plataformas onde a JVM não está disponível, como iOS.
4. **Kotlin Multiplatform**: Permite compartilhar código comum entre diferentes plataformas (JVM, JavaScript, iOS, etc.), facilitando o desenvolvimento de aplicativos multiplataforma.
# 🍃 [Java] Spring
[](#) [](#) [](#) [](#) [](#) [](#) [](#)
O **Spring** é um framework open source desenvolvido para a plataforma Java baseado nos padrões de projetos inversão de controle (IoC) e injeção de dependência (DI). Sua estrutura é composta por módulos afins de reduzir a complexidade no desenvolvimento aplicações simples ou corporativa. O Spring Framework fornece infraestrutura abrangente para desenvolver aplicações Java robustas e escaláveis. Ele facilita a inversão de controle (IoC) e a injeção de dependências (DI), além de oferecer suporte para desenvolvimento de aplicativos web, segurança, transações, persistência de dados, entre outros. Spring é conhecido por sua modularidade e capacidade de integrar facilmente com outros frameworks e tecnologias.
Olhando um pouco a história, há muito, mas muito tempo atrás, o Java EE (atualmente chamado de Jakarta EE) era realmente muito complicado e nem era necessário entrar numa discussão, era visto como um framework pesado e burocrático, com muita configuração manual e complexidade desnecessária, então usar o Spring era um caminho mais simples e mais fácil de evoluir. Aí chegou a versão 5 do Java EE e a discussão voltou a ficar um pouco mais quente.
O Spring Framework é um framework para aplicações Java que fornece infraestrutura para desenvolvimento de aplicativos Java corporativos. Ele é modular e abrange diversas funcionalidades, como:
- Inversão de Controle (IoC) e Injeção de Dependência (DI).
- Programação orientada a aspectos (AOP).
- Acesso a banco de dados via JDBC, JPA/Hibernate.
- Criação de APIs REST com Spring MVC e Spring Boot.
- Gerenciamento de transações.
- Integração com serviços na nuvem e muito mais.
O Spring Framework Runtime Environment (ambiente de execução do Spring) não é um termo técnico oficial dentro do ecossistema Spring, ele varia conforme o tipo de aplicação. Se for uma aplicação Spring Boot, o runtime pode ser apenas um JAR executável com um servidor embutido. Se for uma aplicação corporativa, pode envolver servidores de aplicação externos, bancos de dados, serviços na nuvem, entre outros. Mas no fim das contas, tudo sempre roda dentro da JVM, gerenciado pelo **Spring Container**.
O ambiente de execução do Spring pode ser entendido como o conjunto de componentes e infraestrutura necessários para que uma aplicação Spring rode corretamente.
Principais componentes do ambiente de execução do Spring:
1. **JVM (Java Virtual Machine)** O Spring é um framework Java, então a aplicação Spring **roda dentro da JVM**. Você precisa de um JDK/JRE compatível para executar sua aplicação.
2. **Container de Inversão de Controle (IoC Container)** O **Spring Container** gerencia a criação e o ciclo de vida dos beans (objetos controlados pelo Spring). Ele pode ser o `ApplicationContext` ou `BeanFactory`, dependendo do tipo de aplicação.
3. **Servidor de Aplicação/Servlet Container** Se você estiver rodando uma aplicação web com **Spring Boot**, ela pode rodar em servidores como **Tomcat, Jetty ou Undertow** (embutidos ou externos). Em aplicações corporativas maiores, pode ser implantada em servidores como **WildFly, WebLogic, WebSphere**.
4. **Spring Boot Runtime (para aplicações Spring Boot)** No caso do **Spring Boot**, o ambiente de execução inclui bibliotecas de autoconfiguração e um servidor embutido, permitindo executar a aplicação como um **JAR executável** (`java -jar app.jar`).
5. **Banco de Dados e Integrações** Muitas aplicações Spring interagem com bancos de dados via **Spring Data JPA**, **JDBC**, **MongoDB**, **Redis**, etc. O ambiente de execução pode incluir conexões com serviços externos, como APIs REST, mensageria (RabbitMQ, Kafka), caches distribuídos, entre outros.
6. **Spring Runtime e Nuvem** Se sua aplicação roda em **ambientes de nuvem**, como AWS, Azure, GCP ou Kubernetes, o runtime pode incluir:
- **Spring Cloud** (para microservices e integração com serviços na nuvem).
- **Docker/Kubernetes** para implantação escalável.
- **Configuração distribuída** via Spring Config Server.
- **Service Discovery** com Eureka, Consul ou Zookeeper.
O Spring é ideal para aplicações modulares, microserviços e arquiteturas robustas, como Hexagonal Architecture (Ports & Adapters) e Domain-Driven Design (DDD). O motivo é que ele oferece um ecossistema completo, que facilita a implementação de princípios arquiteturais modernos, incluindo modularização, injeção de dependências e comunicação entre serviços.
A **Inversão de Controle** (Inversion of Control ou IoC) é um princípio de design de software e trata-se do redirecionamento do fluxo de execução de um código retirando parcialmente o controle sobre ele e delegando-o para um container. O principal propósito é minimizar o acoplamento do código.
Sem IoC
Com IoC
- **Sem IoC**: Em nosso desenvolvimento éramos responsáveis pela instanciação e gestão dos objetos da aplicação.
- **Com IoC**: Agora um container cuida de todo este trabalho de criação e controle dos objetos da aplicação denominados de `Component`.
> [!Note]
> **Não confunda!** Os Design patterns (Padrões de design) são soluções reutilizáveis para problemas comuns de design de software, enquanto design de software é o processo de definir a arquitetura, componentes e interfaces de um sistema para atender a requisitos específicos. No contexto de IoC, o design de software aplica o princípio de Inversão de Controle para criar arquiteturas desacopladas, e padrões como Dependency Injection e Service Locator implementam esse princípio para gerenciar dependências de forma eficiente.
A **Injeção de dependência** (Dependency Injection ou DI) é um padrão de desenvolvimento com a finalidade de manter baixo o nível de acoplamento entre módulos de um sistema.
DI
DI com Singleton, Prototype e Spring Bean Scopes
- **Beans** é o objeto que é instanciado (criado), montado e gerenciado por um container através do princípio da inversão de controle.
- **Scopes** é o controle da existência de nossos objetos Components da aplicação.
- **Singleton**: O container do Spring IoC define apenas uma instância do objeto.
- **Prototype**: Será criado um novo objeto a cada solicitação ao container.
- **Request**: Um bean será criado para cada requisição HTTP.
- **Session**: Um bean será criado para a sessão de usuário na Web.
- **Global**: Ou Application Scope cria um bean para o ciclo de vida do contexto da aplicação.
O **Autowired** é uma anotação (indicação) onde deverá ocorrer uma injeção automática de dependência.
- `byName`: É buscado um método `set` que corresponde ao nome do Bean.
- `byType`: É considerado o tipo da classe para inclusão do Bean.
- `byConstrutor`: Usamos o construtor para incluir a dependência.
Enquanto que o Spring Framework é baseado no padrão de injeção de dependências, o **Spring Boot** foca na configuração automática. O Spring Boot é um framework Java de código aberto usado para programar aplicativos autônomos baseados em Spring de nível de produção com o mínimo de esforço. O Spring Boot é uma extensão de convenção sobre configuração para a plataforma Spring Java destinada a ajudar a minimizar as preocupações de configuração ao criar aplicações baseadas no Spring. A maior parte do aplicativo pode ser pré-configurada usando a "visão opinativa" da equipe Spring da melhor configuração e uso da plataforma Spring e bibliotecas de terceiros.
Dado que a maior parte das configurações necessárias para o início de um projeto são sempre as mesmas, por que não iniciar um projeto com todas estas configurações já definidas?
Essa é exatamente a proposta do Spring Boot! Ele adota a filosofia de convenção sobre configuração para eliminar a necessidade de definir manualmente todas as configurações que, na maioria dos casos, seguem um padrão repetitivo. Em vez de perder tempo ajustando arquivos XML, definindo beans manualmente e configurando o servidor de aplicação, o Spring Boot simplifica tudo com **configuração automática** (**Spring Boot AutoConfiguration**), **starters** e **embutindo um servidor Tomcat ou Jetty** por padrão.
Com o **Spring Boot Starters**, basta adicionar a dependência certa no `pom.xml` (Maven) ou `build.gradle` (Gradle), e o framework já configura automaticamente tudo o que é necessário. Por exemplo, ao adicionar `spring-boot-starter-web`, o Spring Boot configura um ambiente pronto para APIs REST, incluindo um servidor embutido (Tomcat), suporte ao Spring MVC e conversão de JSON. Se adicionarmos `spring-boot-starter-data-jpa`, ele configura o Hibernate e a conexão com o banco de dados.
Outro grande benefício da inicialização automática do Spring Boot é o **Spring Boot Actuator**, que fornece endpoints para monitoramento e métricas da aplicação sem precisar de configurações complexas. Além disso, o **Spring Boot DevTools** ajuda na produtividade, permitindo recarregamento automático da aplicação durante o desenvolvimento.
Com isso, ao invés de gastar tempo com configurações repetitivas e infraestrutura, os desenvolvedores podem focar diretamente no desenvolvimento das **regras de negócio**. É isso que faz o Spring Boot ser tão eficiente para criar microserviços, APIs REST e aplicações escaláveis rapidamente.