Java: a Linguagem e Padrões de Projeto
Ronaldo Rodrigues Godoi
2
Ronaldo Rodrigues Godoi
Java: a Linguagem e Padrões de Projeto
Londrina - Paraná
2024
3
Dedicado à mulher que povoa minha imaginação,
que eu sempre amei e nunca tive.
4
Agradecimentos:
Em primeiro lugar, e sempre, ao nosso Deus, bom e justo.
A equipe da Faculeste, Faculdade Iguaçu e Faculdade Serra Geral.
Aos colegas da comunidade Java que fazem parte do portal GUJ, site [Link];
a todo pessoal, que nos atende tão bem, do curso “Formação Java Web Full-Stack e
Spring Boot Rest API” da JDev Treinamentos, site [Link]; aos
desenvolvedores e professores do curso Java Web da escola OnByte – Londrina; Ao
Alberto da Dev Eficiente, portal que busca capacitação na produção de código de
qualidade, site [Link] por sua generosidade que
manteve desperto o interesse em produzir código cada vez com maior qualidade.
A estas pessoas e a todos que contribuíram com conhecimentos da linguagem, o
que formou de forma direta ou indireta parte do corpo técnico deste trabalho.
5
Qualquer trabalho científico, qualquer descoberta,
qualquer invenção é um trabalho universal.
Ele está condicionado, em parte pela cooperação de contemporâneos,
em parte pela utilização do trabalho de seus predecessores.
K. MARX
6
GODOI, Ronaldo Rodrigues. Java: a Linguagem e Padrões de Projeto. 2024.
RESUMO
O propósito deste trabalho é estudar as principais características da linguagem de
programação Java, com o intuito de expor os elementos constituintes de um sistema, ou
seja, pontos estratégicos de sistemas na linguagem Java. Serão discutidos elementos
de lógica, classes, métodos e programação orientada ao objeto. Apresentaremos
elementos da linguagem SQL, que pode ser dividida em: DDL, DML, DCL e TCL. Os 23
padrões de projeto evidenciados pela Gang dos Quatro serão exemplificados com
programas na linguagem Java. Neste trabalho haverá utilização: do JDK – Java
Development Kit – da versão 21 da linguagem, em todo processo; de um ambiente de
desenvolvimento integrado (IDE); e de um sistema gerenciador de banco de dados.
Palavras-chave: Java. Orientação ao objeto. SQL. Padrões de projeto.
7
GODOI, Ronaldo Rodrigues. Java: the Language and Design Patterns. 2024.
ABSTRACT
The purpose of this work is to study the main characteristics of the Java
programming language, in order to expose the constituent elements of a
system, that is, strategic points of systems in the Java language. Elements of
logic, classes, methods and object-oriented programming will be discussed. We
will present elements of the SQL language, which can be divided into: DDL,
DML, DCL and TCL. The 23 design patterns evidenced by the Gang of Four
will be exemplified with programs in the Java language. In this work there will
be the use of: the JDK – Java Development Kit – version 21 of the language, in
the entire process; an integrated development environment (IDE); and a
database management system.
Keywords: Java. Object orientation programming. SQL. Design patters.
8
Sumário
1 INTRODUÇÃO 12
2 APRESENTAÇÃO DO JAVA 14
2.1 BREVE HISTÓRIA E PRIMEIRO PROGRAMA ................................................................. 14
2.2 OBTENDO E INSTALANDO O JAVA 21 ........................................................................ 16
2.3 OBTENDO E INSTALANDO NETBEANS 19................................................................... 17
2.4 OBTENDO E INSTALANDO O MYSQL [Link] ........................................................... 18
2.5 CONFIGURAÇÃO DO WINDOWS: AS VARIÁVEIS DE AMBIENTE ....................................... 19
2.6 CARACTERÍSTICAS DA LINGUAGEM ........................................................................... 19
2.6.1 Palavras reservadas 19
2.6.2 Operadores aritméticos, lógicos e relacionais 20
2.6.3 Instrução ou bloco, estruturas de repetição, desvio e controle de erros 22
2.6.4 Arrays e matrizes 34
2.7 ESPECIFICADORES E MODIFICADORES ...................................................................... 36
2.8 ANOTAÇÕES .......................................................................................................... 37
2.9 CRIAÇÃO DE PACOTES, MÓDULOS E IMPORTAÇÃO DE PACOTES .................................. 39
2.10 CRIAÇÃO DE MÉTODOS ......................................................................................... 40
2.11 PROGRAMA DE EXEMPLO DO CAPÍTULO 2................................................................ 40
3 ORIENTAÇÃO A OBJETOS 45
3.1 PROGRAMAÇÃO ORIENTADA AO OBJETO NA LINGUAGEM JAVA .................................... 45
3.1.1 Variáveis, objetos, classes e construtores, métodos e visibilidade 45
3.1.2 Declaração e instanciação: qual a diferença? 49
3.1.3 Operador ponto (.) e operador dois pontos dois pontos (::) 50
3.1.4 Métodos estáticos 50
3.2 HERANÇA, POLIMORFISMO, ENCAPSULAMENTO E ABSTRAÇÃO .................................... 50
3.3 INTERFACES .......................................................................................................... 54
3.6 PROGRAMA EXEMPLIFICANDO O CAPÍTULO 3 ............................................................. 55
4 SWING 56
4.1 INTRODUÇÃO AO AWT E AO SWING ......................................................................... 56
9
4.2 ELEMENTOS GRÁFICOS: PRINCIPAIS COMPONENTES .................................................. 60
4.2.1 Containers e Janelas 61
4.2.2 Botões 63
4.2.3 Componentes de Texto 65
4.2.4 Listas e Combo Box (caixas de combinação) 69
4.2.6 Menus 71
4.2.7 JFileChooser, JOptionPane, JColorChooser, 71
4.2.8 JSlider, JTree, JSpinner 71
4.3 CONTROLE DE EVENTOS ......................................................................................... 71
4.4 PROGRAMA EXEMPLIFICANDO SWING NO CAPÍTULO 4 .............................................. 74
5 EXPRESSÕES LAMBDA, COLEÇÕES, STREAMS E THREADS 75
5.1 EXPRESSÕES LAMBDA ............................................................................................ 75
5.2 COLEÇÕES ............................................................................................................ 79
5.2.1 List (ArrayList, LinkedList, Vector e Stack) 79
5.2.2 Queue (PriorityQueue) 79
5.2.3 Set (HashSet, LinkedHashSet) 79
5.2.4 Map (HashMap, LinkedHashMap e HashTable) 79
5.2.1 Genéricos 79
5.2.2 Operador diamante (<>) 80
5.3 STREAMS .............................................................................................................. 80
5.4 THREADS .............................................................................................................. 80
5.4 PROGRAMA EXEMPLIFICANDO O CAPÍTULO 5 ............................................................. 81
6 BANCO DE DADOS E JDBC 82
6.1 SISTEMAS GERENCIADORES DE BANCO DE DADOS ..................................................... 82
6.2 DRIVER E JDBC .................................................................................................... 82
6.3 EXECUTANDO COMANDOS NO BANCO DE DADOS COM JDBC ..................................... 82
6.4 PROGRAMA DE CADASTRO EXEMPLIFICANDO O CAPÍTULO 6 ........................................ 82
7 O SISTEMA DESENVOLVIDO 83
7.1 CADASTROS .......................................................................................................... 83
10
7.2 MOVIMENTOS......................................................................................................... 83
7.3 CONSULTAS EM TELA .............................................................................................. 83
7.4 RELATÓRIOS IMPRESSOS ........................................................................................ 83
7.5 MANUTENÇÃO DO SISTEMA ..................................................................................... 83
7.6 ARQUIVOS DE CONFIGURAÇÃO E BIBLIOTECAS INCORPORADAS ................................... 83
8 PADRÕES DE PROJETO 84
8.1 PADRÕES DE INTERFACE......................................................................................... 85
8.1.1 Adaptador (ou Adapter) 85
8.1.2 Fachada (ou Facade) 87
8.1.3 Composto (ou Composite) 90
8.1.4 Ponte (ou Bridge) 93
8.2 PADRÕES DE RESPONSABILIDADE (OU RESPONSIBILITY PATTERNS) ............................. 97
8.2.1 Instância Única (ou Singleton) 97
8.2.2 Observador (ou Observer) 99
8.2.3 Mediador (ou Mediator) 101
8.2.4 Procurador (ou Proxy) 103
8.2.5 Cadeia de Responsabilidade (ou Chain of Responsibility) 103
8.2.6 Peso-Mosca 103
8.3 PADRÕES DE CONSTRUÇÃO (OU CONSTRUCTION) ................................................... 103
8.3.1 Construtor 103
8.3.2 Método Fábrica 103
8.3.3 Fábrica Abstrata 103
8.3.4 Protótipo 103
8.3.5 Registro (ou Memória) 103
8.4 PADRÕES DE OPERAÇÃO ...................................................................................... 104
8.4.1 Método Modelo 104
8.4.2 Estado 104
8.4.3 Estratégia 104
8.4.4 Comando 104
8.4.5 Interpretador 104
11
8.5 PADRÕES DE EXTENSÃO ....................................................................................... 104
8.5.1 Decorador 104
8.5.2 Iterador 104
8.5.3 Visitante 104
9 CONCLUSÃO 106
REFERÊNCIAS 108
APÊNDICE A – JAVA FX 110
A.1 O QUE É JAVA FX ................................................................................................ 110
A.2 J AJLKDFJLASDJFLAJSDFLKASDJFLK ASDFKLJ SDFJ ASDLJK ...................................... 110
A.3 J WIR WIOEOIJSDLK L JASDF KJSLADJLK ................................................................. 110
A.4 J WOIE LSKDFLKJ LK XCMVN MN ............................................................................. 110
A.5 S AKLSFJ LASLKDFJLSJD OIWEI .............................................................................. 110
APÊNDICE B – SERVLET 111
B.1 O QUE É SERVLET................................................................................................ 111
B.2 DESENVOLVIMENTO DE PÁGINAS PARA INTERNET .................................................... 111
B.3 ... ....................................................................................................................... 111
INDICE REMISSIVO 113
12
1 INTRODUÇÃO
Adotada por uma considerável comunidade de desenvolvedores, que se aproxima
de 10 milhões de pessoas (CHIN, S.; VOS, J.; WEAVER, J., 2019, p. 4, apud Oracle Java
Certification Paths Overview), a linguagem Java apresenta-se como importante elemento
no setor de tecnologia informática e é objeto de estudo deste trabalho. Aqui será
produzido um sistema de controle de estoque com cadastros de clientes, fornecedores
e uma série de relatórios em pdf, para exemplificar o uso de Java.
O problema é produzir um conteúdo que envolva os fundamentos e práticas
adotadas no desenvolvimento de sistemas de computador baseados na linguagem Java.
Nos perguntamos: como apresentar um corpo de conhecimento que beneficie a evolução
de profissionais em desenvolvimento de sistemas com linguagem Java?
O objetivo é levar o leitor a aprender a programar utilizando a linguagem Java,
dominando a programação orientada ao objeto e as mais diversas particularidades que
o trabalho cotidiano utilizando essa linguagem apresenta. Registrar, citar, exemplificar e
descrever o processo de desenvolvimento de sistemas com Java.
O que justifica o desenvolvimento deste trabalho é a importância que a linguagem
Java possui devido aos seus incontáveis recursos, a grande comunidade de
desenvolvedores e o enorme número de dispositivos instalados no mundo que utilizam
esta linguagem.
A metodologia adotada é a pesquisa bibliográfica e prática de programação
propriamente dita. E o início dos trabalhos foi em primeiro de novembro de 2023, quando
começa o processo de leitura do material bibliográfico e produção deste livro. A previsão
para o término é em outubro de 2026.
Inicialmente será abordada uma contextualização histórica sobre o surgimento da
linguagem Java, como obter e instalar o JDK (Java Development Kit), que é o software
necessário para a compilação e execução de programas escritos nessa linguagem.
Na sequência, será explicado onde e como fazer o download da IDE (Integrated
Development Environment) NetBeans, a tradução é ambiente de desenvolvimento
integrado. Depois da instalação da linguagem e do IDE, abordaremos o processo de
download e instalação do banco de dados MySQL.
13
Uma vez terminado o processo de instalação dos softwares necessários ao trabalho
com Java, serão abordados os principais elementos da lógica de programação,
explicando métodos e classes que ilustram e exemplificam os elementos da lógica. Na
sequência, um estudo sobre swing e seus recursos na produção de elementos gráficos
com controle de eventos.
Em segundo momento, faremos a criação de um banco de dados de um sistema
de controle de estoque. Depois de gerar o banco de dados, apresentaremos os principais
pontos no desenvolveremos de um sistema de controle de estoque.
A parte final do livro abordará os 23 padrões de projeto expostos no livro
desenvolvido pela Gang of Four – Gangue dos Quatro.
14
2 APRESENTAÇÃO DO JAVA
2.1 Breve história e primeiro programa
Java foi lançada em 1995 com este nome, possivelmente por uma destas duas
razões: os criadores da linguagem tomavam um café em uma esquina e o café vinha de
Java (a ilha) ou a equipe tirou férias na ilha com nome Java. Não sabiam que seria um
nome mundialmente conhecido. Originalmente, a linguagem foi desenvolvida para
equipamentos pequenos (torradeira, micro-ondas, controle remoto) mas com o
surgimento da internet, em 1993, novos nichos de mercado apareceram e a linguagem
se destacou (FURGERI, 2018, p. 17; SCHILDT, 2015, p. 3). Outro nome que poderia ser
o nome da linguagem Java era Oak, que significa carvalho (BRANCO, 2022), mas já
havia um produto com este nome, o que fez com que o nome fosse substituído.
A empresa que desenvolveu a linguagem Java foi a Sun Microsystems e o chefe
da equipe de programadores era James Gosling que trabalhou com Patrick Naughton,
Chris Warth, Ed Frank e Mike Sheridan. Em 2008/2009, a Sun foi adquirida pela Oracle
Corporation (WIKIEPÉDIA, acessado em 01/11/2023; SCHILDT, 2015, p. 3; JANDL
JUNIOR, 2021, p. 16).
Quando ocorre inovação e desenvolvimento de uma linguagem de computador,
duas são as razões fundamentais: para se adaptar a ambientes e usos em mudança e
para implementar refinamentos e melhorias na arte da programação (SCHILDT, H;
COWARD, D.; 2024, p. 3, tradução nossa).
Veja a definição do que é Java:
[...] Java é uma linguagem de propósito geral, concorrente, baseada em
classe, orientada ao objeto. Ela é concebida para ser simples o suficiente para
que muitos programadores possam atingir fluência na linguagem. A linguagem
de programação Java está relacionada ao C e C++ mas é organizada bastante
diferentemente, com aspectos de C e C++ omitidos e algumas ideias de outras
linguagens incluídas. Pretende ser uma linguagem de produção, não uma
linguagem de pesquisa [...] (GOSLING et al., 2020, acessado 06/11/2023,
tradução nossa).
A versão Java 21 foi lançada em 29 de setembro de 2023 (WIKIEPÉDIA, acessado
em 01/11/2023). Trabalharemos com essa versão, considerando que se utilizarmos
15
versões anteriores, não serão suportados recursos das versões mais recentes, enquanto
a versão 21 suporta todos os recursos das versões anteriores.
Digite o texto do programa 2.1 em um editor de texto (notepad do windows) grave
com o nome [Link].
1 public class OlaMundo {
2 public static void main(String[] args) {
3 [Link](“Olá, mundo!”);
4 }
5 }
Programa 2.1 – Primeiro programa em Java do livro, fonte: WIKIEPÉDIA, acessado 01/11/2023.
Este pequeno programa é uma saudação que destaca sua entrada no mundo do
Java e deve ser gravado com o nome de [Link]. Todo arquivo de programa em
Java tem que ter extensão java. Quando você estiver digitando seu programa tem que
prestar atenção se as letras são maiúsculas ou minúsculas, pois o Java faz diferenciação
entre elas, ou seja, não podemos digitar em maiúscula o que é para ser digitado em
minúscula e vice-versa. Convencionamos que a primeira palavra do nome de uma classe
deve ser maiúscula e a primeira letra do nome de um método deve ser minúscula. As
palavras compostas, como OlaMundo, devem ter a letra da segunda palavra do nome,
sempre em maiúscula, sejam nomes de classes, métodos ou mesmo se for palavra
criada pelo usuário, como nome de variável, devem obedecer esta regra. É muito comum
expressões compostas por várias palavras que devem sempre ter algum significado
relacionado com a sua função. Mas interfaces e classes, tem a primeira letra que as
nomeia maiúscula, como OlaMundo. Por convenção, classe tem na primeira palavra do
nome substantivos e interfaces adjetivos ou substantivos descritivos.
O método main da segunda linha do programa é onde começa a execução
propriamente dita, nele estão os procedimentos, como a aplicação do método println da
classe System na linha 3 do programa 1.1. Veja que a expressão impressa na tela é
“Olá, mundo!” e está digitada dentro de aspas duplas, que estão dentro de parênteses.
O método main é muito importante e, geralmente, só existe um método main dentro de
cada sistema. É este método que dá direção aos procedimentos, levando-nos as mais
diversas partes do sistema. Por maior que seja o sistema, só existe um método main
nele, esta palavra quer dizer principal. Porém, sistemas muito complexos e grandes
16
podem ser divididos e, neste caso, tem mais de um método main – podem ser divididos
em diversos projetos no seu IDE.
Java é uma linguagem interpretada. Para executar um programa Java precisamos
digitar a seguinte sequência de comandos numa janela de prompt de comandos:
1 javac [Link] <ENTER>
2 java OlaMundo <ENTER>
Para abrir a janela de comandos no Windows 10, você deve digitar: <botão
Windows> CMD <botão ENTER>. Apesar do Java poder ser executado em qualquer
plataforma, utilizaremos o Windows como referência para todos os efeitos. Se você
estiver utilizando uma plataforma diferente, no tocante a nossa sintaxe e lógica, não há
alterações nos programas java.
Para executar esses comandos é preciso que o Java esteja instalado no
computador em que o programa foi compilado e executado. A primeira linha compila o
programa [Link] e a segunda linha executa este mesmo programa.
Após elaborar e digitar um programa, este deve ser compilado para gerar o
bytecode, que é um arquivo com a extensão class, esse arquivo pode ser executado em
qualquer plataforma (Windows, OS2, Linux, etc.) que possua uma máquina virtual Java.
Dessa forma podemos compreender que programas feitos em linguagem Java são
compatíveis com qualquer computador ou sistema operacional. Depois de compilado em
Java o programa pode ser executado em qualquer plataforma.
2.2 Obtendo e instalando o Java 21
Para instalar o Java em sua máquina é preciso fazer o download gratuito no
endereço <[Link]/tech-network/java/javase/download/index>, mas você tem
que criar uma conta no site da Oracle com seu email em mãos. Depois de criar esta
conta você faz uma busca no Google para procurar “download java jdk 21 windows 64
bits” caso seu sistema operacional seja Windows de 64 bits, caso contrário faça a busca
para seu sistema operacional, lembre-se: queremos a versão 21 do Java. Dentre as
inúmeras respostas a está pesquisa no Google, você deve encontrar a correspondente
ao download que você deseja fazer, que provavelmente é uma das primeiras opções
apresentadas e, então, você deve clicar nesta opção para fazer o download. Leia as
17
instruções no site e encontre o link de download. Quando solicitado o login e a senha da
conta da Oracle você deve digitá-las e seguir os procedimentos de download.
Uma vez feito o download, clique duas vezes sobre o nome do arquivo de instalação
que você baixou e o processo de instalação começará. Siga as instruções do programa
de instalação até que o processo tenha terminado.
2.3 Obtendo e instalando NetBeans 19
Estamos baixando e instalando o NetBeans 19, que é um ambiente de
desenvolvimento integrado, mas pode ser outra versão ou, mesmo, outro IDE. Estou
apenas utilizando o NetBeans, versão 19, como referência porque é este IDE que tenho
instalado em meu computador.
Para obter o arquivo executável de instalação do NetBeans 19, faça a busca no
Google por “[Link]”, caso seu Windows seja de 64
bits, e clique em cima da opção que permite download, na resposta à pesquisa, e procure
nesta página o link de download do arquivo. Clique sobre este link e espere o download
ser completado. Agora, dê um clique duplo sobre o nome do arquivo e leia as instruções
de instalação que apareceram na tela. Normalmente você apenas terá que clicar no
botão next para instalar o NetBeans 19. Repare que não foi necessário criar uma conta
para fazer o download.
Abra o ambiente de desenvolvimento integrado (IDE NetBeans) que você instalou,
e crie um projeto chamado ProgramasJava21, abrindo o menu File>New Project e
escolhendo na caixa Categories a opção Java with Ant; dentro desta opção, na caixa
Projects, escolha a opção Java Application; aperte, então, o botão Next. Iremos dar um
nome ao nosso projeto, este nome será ProgramasJava21 e será digitado na caixa de
texto Project Name. Apertaremos o botão Finish para criarmos o projeto. Esse projeto
conterá todos os nossos programas da primeira parte deste livro (depois criaremos
outros projetos).
Deve ter sido criado um pacote, vamos alterar seu nome clicando com o botão
direito no nome do pacote programasjava21, janela Projects do IDE, para aparecer um
18
menu; neste menu, escolha a opção Refactor>Rename; na caixa de texto New Name
digite o nome Programas; pressione o botão Refactor. Desta forma o IDE refatorou o
nome do pacote do nosso programa 1.1, que deve constar através da linha “package
Programas;” antes de qualquer codificação da classe em questão, o que pode ser
verificado na janela com o nome da classe. O IDE alterou também o nome do pacote na
janela Projects.
Criando o projeto, o IDE deve ter criado também uma nova classe com o método
main. Na janela Projects do IDE, você deve clicar com o botão direito do mouse sobre o
nome do programa ([Link]) e clicar na opção Refactor>Rename, na
caixa de texto New Name deve digitar o nome do programa 1.1, é o nome da classe que
será OlaMundo. Pressione o botão Refactor e veja que o nome da classe foi modificado,
podemos agora digitar o primeiro programa do capítulo 2 e executá-lo com o menu
Run>Run File.
Utilizaremos esse projeto para gravar diversos de nossos programas e estes
programas terão, cada um deles, um método main. Isto é uma situação atípica,
normalmente um projeto estruturado de um sistema tem apenas um método main, em
nosso caso diversos programas serão criados dentro deste projeto e cada um deles terá
um método main. É uma situação didática.
2.4 Obtendo e instalando o MySQL [Link]
Procure no Google pelo MySQL da versão 8, preferencialmente [Link],
compatível com o seu sistema, seja ele 32 bits ou 64 bits, caso seja 64 bits você pode
fazer o download no site de endereço que segue:
<[Link]
Você vai notar que existem várias versões, mas qualquer versão que seja a partir
da 8 e menor que a 9 serve para que possamos trabalhar. Siga as instruções do site e
faça o download.
Uma vez obtido o arquivo de instalação, clique duas vezes sobre o nome do arquivo
na pasta em que ele se encontra para iniciar a instalação. O processo deve ser
19
acompanhado por você que vai seguir as orientações do programa de instalação até
finalizar o processo. Note que na instalação do MySQL você tem que cadastrar o usuário
administrador e uma senha para acesso. Anote esta senha pois você vai utilizá-la para
acessar o banco de dados que vamos criar.
2.5 Configuração do Windows: as variáveis de ambiente
Eventualmente pode haver algumas configurações que você deva alterar no seu
sistema operacional ou ambiente operacional, para que o Java funcione corretamente. A
variável de ambiente PATH é uma delas (JANDL JUNIOR, 2021, p. 21 e 22). Esta
variável deve conter em sua composição o caminho que leva até a pasta lib, dentro da
pasta do Java, que deve estar dentro da pasta Arquivos de Programas.
Mas somente se preocupe com estas configurações se você não conseguir
executar seu programa OlaMundo. Note que se você utilizar a IDE recomendada
possivelmente não terá que alterar suas variáveis de ambiente. E se você instalar pelo
instalador oficial da Oracle, ele normalmente ajusta as variáveis PATH e JAVA_HOME.
2.6 Características da linguagem
Em um programa, quando encontrar duas barras da seguinte forma “//”, o restante
da linha é considerado comentário e não será interpretado como instrução. Esses
comentários curtos são úteis para explicar o que a linha está fazendo. Da mesma forma,
podemos fazer um comentário mais longo, iniciando com barra junto de asterisco “/*” e
finalizando esse trecho de comentário com asterisco junto da barra “*/”. Tudo que estiver
entre os marcadores não será compilado como código executável, da mesma forma que
o comentário que estiver à direita das duas barras.
2.6.1 Palavras reservadas
Tabela 2.1 – Palavras reservadas na linguagem Java.
abstract assert boolean break byte case
20
catch char class continue default do
double else enum extends false final
finally float for if implements import
instanceof Int interface long native new
null package private protected public return
short static strictfp super switch synchronized
this throw throws transient true try
var void volatile while yield
Fonte: adaptado (JANDL JUNIOR, 2021; FURGERE, 2018; SCHILDT, 2015).
Acima temos uma tabela com as palavras reservadas do Java. Não podemos
utilizar estas palavras para criar métodos, classes ou variáveis pois estas palavras tem
função específica dentro da linguagem e só podem ser usadas para estas funções. As
palavras true, false e null não são consideradas palavras reservadas, mas não podem
ser usadas para ser identificadores. Identificadores são os nomes dados a interfaces,
classes, métodos ou variáveis.
2.6.2 Operadores aritméticos, lógicos e relacionais
Os operadores relacionais, lógicos e aritméticos tornam a linguagem operacional
para realizar cálculos e estabelecer a lógica que permite a construção de rotinas que
fazem dos computadores e programas uma ferramenta de trituração de dados útil.
No caso dos operadores compostos por dois símbolos, não podemos separar por
espaço estes símbolos. Ou seja, se o operador for composto por mais de um símbolo,
como ++ (incremento), devem ser digitados sem espaço entre os caracteres que
compõem o operador.
Tabela 2.2 – Operadores aritméticos e matemáticos
Operador O que faz Exemplo
* Multiplicação a*b
/ Divisão a/b
+ Soma a+b
- Subtração a-b
% Módulo (resto da divisão) a%b
++ Incremento (soma um) a++
-- Decremento (subtrai um) a--
Fonte: adaptado (JANDL JUNIOR, 2021, p. 48; FURGERE, 2018; SCHILDT, 2015).
21
As operações lógicas retornam verdadeiro ou falso, no caso true ou false
respectivamente. Podemos encadear as expressões lógicas da mesma forma que as
expressões aritméticas, determinando a prioridade de acordo com parênteses e juntando
estas expressões com operadores lógicos:
Tabela 2.3 – Operadores lógicos
Operador Nome ou o que faz Exemplo
&& E (AND) a && b
|| Ou (OR) a || b
! Nega a expressão (NOT) !a
^ Xor (ou exclusivo) a^b
Fonte: adaptado (JANDL JUNIOR, 2021, p. 52; FURGERE, 2018; SCHILDT, 2015).
Os operadores relacionais, da mesma forma que os operadores lógicos, retornam
valores booleanos verdadeiro (true) ou falso (false).
Tabela 2.4 – Operadores relacionais
Operador O que faz Exemplo
== Igual a == b
!= Diferente a != b
> Maior a>b
< Menor a<b
>= Maior ou igual a >= b
<= Menor ou igual a <= b
Fonte: adaptado (JANDL JUNIOR, 2021, p. 51; FURGERE, 2018; SCHILDT, 2015).
O Java não atribui verdadeiro ou falso a nenhum número, mas para fins didáticos
vamos considerar as entradas a e b sendo 0 para falso e 1 para verdadeiro. Tendo isto
em mente, apenas para este momento, observemos as tabelas verdade abaixo, para os
operadores lógicos não, e e ou. Note que se a entrada de não for 1 que é verdadeiro a
negação dele devolve a saída falso que é 0 e vice-versa. Da mesma forma se entrarmos
dois valores falsos ou um valor falso e outro verdadeiro na porta lógica e obtemos falso
e se entrarmos dois valores verdadeiros na porta e obtemos verdadeiro. Já na porta ou,
basta que um dos valores seja verdadeiro e sua resposta será verdadeiro, caso contrário
será falso.
22
Tabela 2.5 – Sáida das portas lógicas não, e e ou.
Não (NOT) E (AND) Ou (OR) XOR
Input Output Input Output Input Output Input Output
A A B A B A B
0 1 0 0 0 0 0 0 0 0 0
1 0 0 1 0 0 1 1 0 1 1
1 0 0 1 0 1 1 0 1
1 1 1 1 1 1 1 1 0
Fonte: adaptado de (GODOI, 2004, p.81)
Na tabela verdade 2.5, temos um operador que aceita uma entrada A apenas, e os
demais operadores aceitam duas entradas A e B. Note que o operador NOT tem apenas
uma entrada A, se a entrada é zero (falso) ele retorna 1 (verdadeiro), e vice-versa. O
operador AND retorna verdadeiro se ambas as entradas forem verdadeiras, caso
contrário retorna falso. O operador OR retorna verdadeiro se pelo menos uma entrada
for verdadeira e retorna falso se ambas as entradas são falsas. O operador XOR retorna
verdadeiro apenas se uma única entrada é verdadeira e a outra é falsa, caso contrário
retorna falso.
2.6.3 Instrução ou bloco, estruturas de repetição, desvio e controle de erros
Um programa desenvolvido em qualquer linguagem é uma sequência de
instruções, que podem repetir um bloco ou instrução e que podem desviar de um bloco
ou instrução e, para tanto, o Java possui as estruturas que seguem:
for...break...continue; while...break...continue; do...break...continue...while; if...else
if...else; switch...break...default; e try...catch...finally (JANDL JUNIOR, 2021, p. 58-
83).
Cada instrução é separada da seguinte por um ponto e vírgula (“;”). É uma boa
prática colocar cada instrução em uma linha separada das demais, embora o Java aceite
várias instruções em uma única linha, desde que separadas por ponto e vírgula (“;”).
23
[Link] Instrução ou bloco de instruções
Podemos colocar várias instruções em uma única linha, separando-as por ponto
e vírgula “;”, mas isto não é uma boa prática. Normalmente o que se faz é quebrar a linha
depois do ponto e vírgula para melhor compreensão do código.
Além de uma instrução isolada, temos os blocos de instruções, esses elementos
são compostos por uma ou diversas instruções que estão separadas do restante do
código por chaves, ou seja, um bloco é delimitado pela abertura e fechamento de chaves
{...bloco...}. Pode haver o encadeamento de blocos – um bloco dentro do outro. É
importante notar que cada bloco tem seu escopo próprio (JANDL JUNIOR, 2021, p. 39,
56 e 57).
O escopo de uma variável, ou de um objeto Java, é o local onde este objeto é válido.
Quando você declara uma variável ou instancia um objeto dentro de um bloco, este
objeto é valido ou reconhecido dentro deste bloco. Se houver um bloco dentro de outro,
os objetos declarados dentro do bloco pai são visíveis no bloco filho, mas os objetos
declarados dentro do bloco filho só serão visíveis em blocos netos e assim por diante. É
preciso ter um cuidado especial com o escopo de um objeto ou variável em qualquer
linguagem.
[Link] Estruturas de repetição
Quando é necessário repetir a instrução n vezes, utilizamos estruturas de repetição.
Essas estruturas podem apresentar número variável de execução ou número fixo.
Quando conhecemos o número de execuções que uma instrução ou bloco de instruções
deve ser executado utilizamos a estrutura for, quando haverá uma condicional durante
o início ou fim de um bloco utilizamos respectivamente as estruturas while ou do...while
(JANDL JUNIOR, 2021, p. 58-63).
Examine os três exemplos de trechos de código abaixo:
... ... ...
for(int i = 0; i < 10; i++) int i = 0; int i = 0;
{ while(i < 10) { do {
[Link](i); [Link](i); [Link](i);
} i++; i++;
... } } while(i < 10);
... ...
Sintaxe:
for( <inicialização>;<condição>;<incremento>)[<instrução;> ou <{...bloco...}>]
while(<condição>) [<instrução;> ou <{...bloco...}>]
do [<instrução;> ou <{...bloco...}>] while(<condição>);
A <inicialização> é: int i = 0
A <condição> é: i < 10
O <incremento> é: i++
E o <{...bloco...}> é:
(Bloco do for) (Bloco do while/do...while)
{ {
[Link](i); [Link](i);
} i++;
}
Temos três exemplos com trechos de código que fazem quase a mesma coisa. O
for inicia a variável i como inteira, com o valor zero e imprime dez vezes o seu valor de
zero até nove, cada número em uma linha. No for, quando i atinge o valor 10 a execução
sai do laço sem imprimir. No while, a variável i do teste condicional tem que ser
declarada antes de iniciar o bloco, assim como no do...while, que também tem que ter
a variável i (ou qualquer variável que seja testada na condição) declarada antes de iniciar
o laço; então, o while imprime o valor de i, que agora é zero. Depois disso, o while volta
a questionar se i < 10, executando nova impressão até que o valor seja dez, a partir daí
sai do laço (ou loop) sem imprimir o dez. No exemplo da estrutura do...while, é
executada a impressão do zero antes de testar a condição, depois incrementa e testa a
condição. Quando i assume o valor dez, durante o do...while, na linha de incremento, a
execução chega até o teste condicional antes de imprimir e sai do laço sem imprimir o
dez. A diferença essencial entre o while e do...while está no fato de que o while testa a
25
condição antes de executar o bloco de instruções, enquanto que o do...while executa o
bloco pela primeira vez e só então testa a condição (JANDL JUNIOR, 2021, p. 58-63).
Note que o primeiro bloco tem apenas uma instrução e, portanto, não precisaria ser
um bloco, podendo estar com a linha de impressão diretamente na linha da instrução for.
Os dois blocos de instruções do while e do do...while são constituídos por duas
instruções, estando, por isso, obrigados a compor um bloco. Um bloco de instruções
pode conter quantas instruções forem necessárias.
Na instrução for, quando é encontrado uma instrução break, o bloco do for é
interrompido e a execução passa para a próxima linha abaixo da chave que fecha esse
bloco. Ao encontrar a instrução continue, a execução do bloco do for volta para a
condição deste for, executando o incremento ou o decremento, e testando a condição
que determina o número de iterações. Tanto o break quando o continue podem ser
utilizados por todas as estruturas de repetição, ou seja, funcionam no for, while e
do...while.
O que é iteração? Iteração é a execução do laço uma vez, ou seja, é a execução
do bloco de instruções que faz parte do for, while ou do...while uma vez. Quando a
condição não é mais satisfeita, o número de iterações está completo e a execução do
programa segue para fora do laço (em seguida ao laço).
Um erro relativamente comum é o loop eterno. Nesse erro, a execução do programa
nunca sai do loop ou laço e isto é um erro que trava o computador. Isto ocorre quando a
condição que determina a execução do bloco nunca é falsa.
[Link] Estruturas de desvio
[Link].1 if...else if...else
O if...else if...else trata-se de uma estrutura de desvio de fluxo de execução do
programa. O if envolve um bloco ou instrução que é executado se uma expressão lógica
for verdadeira. O else if é opcional e encadeia uma nova expressão lógica que será
testada caso a expressão lógica do if for falsa (JANDL JUNIOR, 2021, p. 63-65), else
também é opcional e pode envolver um bloco ou instrução que será executado caso a
26
expressão lógica do if seja falsa. Pode haver uma cadeia de diversos else if com novas
expressões lógicas, com tantos else if quantos forem necessários, veja a sintaxe:
if(<condição>) [<instrução;> ou <{...bloco...}>]
else if(<condição>) [<instrução;> ou <{...bloco...}>]
else [<instrução;> ou <{...bloco...}>]
Onde: <condição> é um teste lógico que retorna um valor verdadeiro ou falso e pode
ser uma expressão lógica com diversos termos associados por operadores com
prioridade definida por parênteses;
<instrução;> ou <{...bloco...}> é código executado se a condição for
verdadeira pode ser uma única instrução ou um bloco de instruções.
Note na demonstração de sintaxe acima, que o else deve vir por último na estrutura
if...else if...else. O else não tem condição a ser testada, enquanto o if e o else if tem
uma condição que é obrigatória. O programa abaixo ilustra o uso destas estruturas de
desvio.
1 import [Link];
2 public class Maior {
3 public static void main(String[] args) {
4 // Instância um objeto da classe Random com o construtor padrão
5 Random gerador = new Random();
6 // Gera três números aleatório entre 0 e 1
7 int numero1 = [Link](100);
8 int numero2 = [Link](100);
9 int numero3 = [Link](100);
10 // instancia a variável maior e o declara como zero e double
11 int maior = -1;
12 if(numero1 > numero2 && numero1 > numero3) {
13 maior = numero1;
14 } elseif(numero2 > numero3 && numero2 > numero1) {
15 maior = numero2;
16 } elseif(numero3 > numero1 && numero3 > numero2) {
17 maior = numero3;
18 } else {
19 [Link](“Nenhum deles é maior isolado!”);
20 }
21 if(maior != -1) [Link](“O maior é ” + maior);
22 }
23 }
Programa 2.2 – Ilustração do uso do if (Construído com o auxílio do Copilot da Microsoft). Revisado e
testado pelo autor.
Esse programa acima exemplifica o uso do if para descobrir se existe um número
que seja maior dentre três números gerados pseudo-randomicamente pelo computador.
27
Caso exista um número que é maior, o programa exibe esse número, caso não exista
um número que seja isoladamente maior, o programa mostra a mensagem “Nenhum
deles é maior isolado!”.
[Link].2 switch...case...break...default
A instrução switch (também é uma estrutura de desvio de fluxo de execução) pode
ser usada com números, letras isoladas ou string. O switch compara caso a caso para
ver em qual situação a variável fornecida a ele se encontra. Quando a variável encontra
o case em que a variável se enquadra é executado o bloco de comandos associado ao
case. É necessário colocar a instrução break como última linha do bloco de cada case,
caso contrário todos os cases abaixo do case executado, serão testados também.
Então, você tem a opção de permitir que estes case sejam testados ou utilizar o break.
A palavra default determina uma porção do código que somente será executada caso
nenhum dos case anteriores forem executados (JANDL JUNIOR, 2021, p. 65-73).
Podemos usar o switch de três formas diferentes (1) com uma variável numérica;
(2) com uma variável String; e (3) com o retorno para uma variável, que deve ter o
mesmo tipo do dado retornado. Também podemos considerar o switch com bloco de
instruções no case ou com instruções sem as chaves de abertura e fechamento
envolvendo cada case. Veja os exemplos abaixo:
Primeira sintaxe: (com numeral)
int numero = 2;
switch (numero) {
case 1: {
[Link]("Número 1");
// Você pode adicionar mais código aqui
break;
}
case 2: {
[Link]("Número 2");
// Você pode adicionar mais código aqui
break;
}
case 3: {
[Link]("Número 3");
// Você pode adicionar mais código aqui
break;
}
default: {
[Link]("Número ignorado");
// Você pode adicionar mais código aqui
break;
28
}
}
Neste quadro acima, conforme o valor da variável numero teremos a execução de
um bloco de instruções, são testados os números 1, 2, 3 e a exceção quando nenhum
dos cases foi executado, que é a cláusula default.
Segunda sintaxe: (com String)
String dia = "segunda-feira";
switch (dia) {
case "segunda-feira": {
[Link]("Início semana");
// Adicione mais código aqui
break;
}
case "quarta-feira": {
[Link]("Meio da semana");
// Adicione mais código aqui
break;
}
case "sexta-feira": {
[Link]("Fim da semana");
// Adicione mais código aqui
break;
}
default: {
[Link]("Dia ignorado");
// Adicione mais código aqui
break;
}
}
O mesmo padrão se repete, porém, com a String dia, que vale “segunda-feira”.
São testados os valores “segunda-feira”, “quarta-feira” e “quinta-feira”; além do bloco que
acompanha a cláusula default, que deve ser executado se nenhum dos cases contiver
o valor da variável dia.
Terceira sintaxe: (retornando valor)
int numero = 2;
String resultado = switch (numero) {
case 1 -> {
[Link]("Dentro case 1");
yield "Número 1";
}
case 2 -> {
[Link]("Dentro case 2");
yield "Número 2";
}
case 3 -> {
[Link]("Dentro case 3");
yield "Número 3";
}
default -> {
[Link]("dentro default");
yield "Número desconhecido";
}
};
[Link](resultado);
Desta vez, no exemplo acima, a variável numero alimenta o switch que retorna
para variável resultado, que é do tipo String, diferentes conteúdos dependendo de
numero. A cláusula yield retorna um tipo (String) igual ao da variável resultado (caso
contrário ocorre erro). São testados os valores 1, 2 e 3, a cláusula default só é executada
se nenhum case tem o valor inteiro 2 sendo testado. Veja os dois exemplos de sintaxe
abaixo:
Primeira sintaxe:
switch(<ordinal>) {
case <valor 1>:
<diretiva;>
[break;]
... (quantos case forem necessários)...
case <valor n>:
<diretiva;>
[break;]
default
<diretiva;>
};
Segunda sintaxe:
double <variável> = switch(<ordinal>) {
case <valor 1>: -> <constante 1;>
... quantos case forem necessários...
case <valor n>: -> <constante n;>
default -> <constante>
};
Note que:
<ordinal> é a variável que contém o valor que será comparado no case;
<valor1> e <valor n> são comparados ao valor do ordinal, se forem iguais o case
executa suas diretivas;
<variável> é o elemento que receberá um dos valores das diversas constantes;
<constante 1>, <constante n> e constante são valores que o switch retornará para
variável.
30
O primeiro exemplo pode avaliar número, caracter ou string. O segundo exemplo
se refere ao uso de switch enquanto uma expressão que retornará o valor de uma
constante ou elemento dinâmico para dentro da <variável>. O retorno deve ter o mesmo
tipo que a <variável>.
[Link] Estrutura de controle de erros
Os erros tratados por try...catch...finally derivam da classe Exception ou de
classes que estendem essa classe. Esses erros são de duas categorias: exceções
verificadas e exceções não verificadas. Mas há também erros que são graves e não
estendem a classe Exception, eles estendem a classe Error, como OutOfMemoryError
e StackOverflow, entre outros. A classe Error estende a classe Throwable.
Os erros que estendem Exception podem ser exceções verificadas (IOException,
SQLException, etc.) ou não verificadas (NullPointerException, ArithmeticException,
IndexOutOfBoundsException, etc.). As exceções verificadas estendem diretamente a
classe Exception, que estende a classe Throwable. Todos os erros gerenciados pela
JVM (Máquina Virtual Java) estendem Throwable. Os erros não verificados estendem a
classe RuntimeException (OPENAI, 2024e), mas essa classe estende diretamente
Exception.
Exceções verificados devem ser tratados por try...catch...finally e normalmente
derivam do ambiente, como falhas de I/O ou banco de dados. Exceções não verificadas
devem ser prevenidas pelo programador porque são erros de lógica ou condições
inesperadas.
Além das exceções que o Java previu, podem haver exceções criadas por
programadores. Essas exceções tem seu nome definido pelo programador (normalmente
com a palavra exception como parte desse nome) e sempre estendem a classe
Exception ou uma de suas descendentes. As exceções criadas por programadores
podem ser verificadas ou não verificadas e representam condições especiais no fluxo do
programa, ou seja, são exceções especificas da aplicação.
31
[Link].1 try...catch...finally
Os erros de execução possíveis são os mais diversos, são as exceptions. Quando
quero quebrar o código no local onde um determinado erro ocorre e levar a execução do
programa para determinado ponto (fazer uma forma de desvio) utilizo a estrutura
try...catch...finally. Sua sintaxe é a que segue:
try {
<instrução;>
} catch (Exception exp) { // exp se torna uma variável que recebe o erro.
<instrução;>
} finally {
<instrução;>
}
Onde: <instrução;> pode haver uma instrução neste local ou diversas, pois o bloco está
aberto e as instruções podem ser colocadas nele.
Exception exp é a exceção (erro) geral que ficará disponível na variável exp.
A cláusula finally será sempre executada, havendo ou não, interrupção. Essa
cláusula é opcional e só pode haver uma única cláusula finally depois de todas as
cláusulas catch.
Se você quiser especificar cada erro importante para seu programa, deve conhecer
o nome de sua classe e substituir a palavra Exception por esse nome. As cláusulas
catch podem ocorrer em número igual ao número de erros que podem acontecer no
código determinado pelo bloco try. Temos os blocos try, catch e finally, cada bloco
destes pode ter o número que for necessário de instruções e por ser um bloco é
delimitado por chaves “{}” (JANDL JUNIOR, 2021, p. 73-80).
Além desta sintaxe para o try temos o try-with-resources, onde você acrescenta um
objeto que é um recurso que precisa do método close() ao terminar seu uso, porém,
utilizando o try-with-resources não há necessidade de fechar o recurso com o método
close(). O exemplo da sua sintaxe está abaixo:
try (<recurso 1>; <recurso 2>; ...; <recurso n>) {
<instrução;>
} catch (Exception exp){
<instrução;>
32
} finally {
<instrução;>
}
Onde: <recurso 1>, <recurso 2> e <recurso n> são objetos ou instanciações de
objetos que precisam ser fechados com o método close() mas não há esta necessidade
utilizando o try-with-resources, pois esses recursos serão utilizados e fechados
automaticamente. O que reforça o caráter opcional da clausula finally.
Podem haver quantos recursos forem necessários.
O elemento <instrução;> é código Java que será executado quando o
processamento atingir a linha em que a instrução se encontra, seja no bloco try, catch
ou finally.
O programa 2.3, abaixo, exemplifica como usar o try catch finally. Dentro de seu
IDE, no pacote Programas, crie a classe TesteExcecoes e digite o programa, salve e
execute com o Run File do menu que abre quando você clica o botão direito na janela
de código (programa que está digitado).
1 package Programas;
2 /**
3 *
4 * @author Chat GPT e Ronaldo R. Godoi
5 * Programa que demonstra o uso de exceções personalizadas
6 * e IOException em Java.
7 *
8 */
9
10 import [Link];
11 import [Link];
12 import [Link];
13
14 // Definição da exceção personalizada
15 // Esta exceção será lançada quando um valor inválido for detectado no programa.
16 class MinhaExcecao extends Exception {
17 public MinhaExcecao(String mensagem) {
18 super(mensagem);
19 }
20 }
21
22 // Classe principal do programa
23 public class TesteExcecoes {
24 public static void main(String[] args) {
25 BufferedReader leitor = null;
26
27 try {
28 // Tentativa de abrir um arquivo inexistente
29 // Isso gerará uma IOException e será tratado no catch
30 leitor = new BufferedReader(new FileReader("arquivo_inexistente.txt"));
31 [Link]("Arquivo lido com sucesso!");
32
33 // Simulação de um erro de lógica e lançamento da exceção personalizada
34 // Caso o valor seja negativo, a exceção MinhaExcecao será disparada
35 int valor = -10;
36 if (valor < 0) {
37 throw new MinhaExcecao("Valor negativo não permitido: " + valor);
33
38 }
39
40 } catch (IOException e) {
41 // Captura e tratamento da IOException
42 [Link]("Erro de entrada/saída: " + [Link]());
43
44 } catch (MinhaExcecao e) {
45 // Captura e tratamento da exceção personalizada
46 [Link]("Erro específico detectado: " + [Link]());
47
48 } finally {
49 // O bloco finally é sempre executado
50 // Ele é usado para garantir que recursos sejam fechados corretamente
51 [Link]("Bloco finally executado. Fechando recursos...");
52 try {
53 if (leitor != null) {
54 [Link]();
55 }
56 } catch (IOException e) {
57 [Link]("Erro ao fechar o arquivo: " + [Link]());
58 }
59 }
60 }
61 }
Programa 2.3 – Exemplo de uso de try...catch...finally (OPENAI, 2024e). Revisado e testado pelo autor.
Ao executar o programa 2.3 sem incluir o “arquivo_inexistente.txt”, ocorre o erro
abaixo.
Erro de entrada/saída: arquivo_inexistente.txt (O sistema não pode encontrar o
arquivo especificado)
Bloco finally executado. Fechando recursos...
Saída do programa 2.3 – Com exceção IOException capturada pelo catch.
Note, na saída acima, que o programa exibe um erro ao tentar achar o arquivo, pois
o arquivo não existe, crie um arquivo (pode ser vazio) com o nome
“arquivo_inexistente.txt” dentro do diretório raiz do projeto ProgramasJava21. Depois de
criar o arquivo, o erro que aciona IOException não ocorrerá novamente. Execute o
programa novamente e o novo erro é MinhaExcecao. Perceba, nas duas saídas, que o
bloco finally é executado de qualquer forma.
Arquivo lido com sucesso!
Erro específico detectado: Valor negativo não permitido: -10
Bloco finally executado. Fechando recursos...
Saída do programa 2.3 – Com exceção de usuário capturada pelo catch.
[Link].2 assert
Fazer uma asserção (instrução assert) é determinar uma condição de erro e
interrupção do programa e o lançamento de uma exceção do tipo AssertionError. A
34
instrução assert por padrão fica desativada e é ativada por passagem de parâmetro na
linha de execução do programa (java -ea nome_programa ou java -enableassertions
nome_programa), se estiver desativada ao passar pelo assert nada ocorre, mas se
estiver ativada ocorre o teste da condição com interrupção e lançamento de exceção se
a condição não for satisfeita. Para desativar as asserções, a linha de execução do
programa deve conter os parâmetros -disableasserions da seguinte forma: java -
disableassertions nome_programa (JANDL JUNIOR, 2021, p. 81-83). Veja a sintaxe da
instrução assert, abaixo:
assert <condição> [: <expressão>];
Onde: <condição> é uma expressão booleana que pode assumir verdadeiro ou falso;
<expressão> é uma string entre aspas duplas que diz o problema ocorrido.
1 package Programas;
2
3 import [Link];
4
5 public class MaiorAssert {
6
7 public static void main(String[] args) {
8 // Instância um objeto da classe Random com o construtor padrão
9 Random gerador = new Random();
10 // Gera dois números aleatório entre 0 e 100
11 int numero1 = [Link](100);
12 int numero2 = [Link](100);
13
14 // Uso da instrução assert
15 assert numero1 != numero2 : "Números são iguais " + numero1 + " = " + numero2;
16
17 // instancia a variável maior e o declara como zero e int
18 int maior = 0;
19 if (numero1 > numero2) {
20 maior = numero1;
21 } else {
22 maior = numero2;
23 }
24 [Link]("O maior é " + maior);
25 }
26
27 }
Programa 2.4 – Ilustração do uso do assert.
É muito importante saber que o uso comum do assert é durante a depuração do
programa ou sistema, sendo que o controle de erros durante o uso do projeto é feito
através do try...catch...finally.
2.6.4 Arrays e matrizes
35
Arranjos e matrizes são conhecidos genericamente como arrays “são estruturas de
dados que armazenam um número fixo de elementos de um mesmo tipo” (JANDL
JUNIOR, 2021, p. 83). Seus elementos são como variáveis simples do tipo predefinido,
permitindo armazenar e recuperar valores desse tipo. Cada elemento é acessível por
meio de um índice começando pelo elemento zero, depois um, e assim sucessivamente.
Sintaxe de declaração de arrays:
<tipo> identificador[];
<tipo>[] identificador;
Exemplos:
String meses[];
double[] salarios;
Para alocar os arrays/arranjos, deve-se usar a palavra new da seguinte forma:
String meses[] = new string[12]; // variável meses com 12 elementos
double[] salarios = new double[12]; // variável salario com 12 elementos
Ao se referir a determinado elemento do array, devemos colocar o número ou a
variável que contém o número do elemento entre colchetes da seguinte forma:
...mes[1]... mes[3]... mes[cisao]... mes[inicio]...
Esta forma acima de se referir a um elemento serve para qualquer array.
As seguintes operações podem ser feitas com arrays: atribuição, cópia e
ordenação. As seguintes métodos são utilizadas para fazer operações com arrays: fill,
copyOf, copyOfRange e sort. Para saber quantos elementos tem um array utiliza-se a
palavra length. Veja exemplos no trecho de código abaixo:
// a linha abaixo atribui quatro valores criando o array salario
double salario[] = {2000.66, 1550.77, 2300.88, 1450.99};
double salario2[] = new double[12];
// a linha abaixo atribui 7320.55 para os 12 elementos
[Link](salario2, 7320.55);
// a linha abaixo copia todos elementos do array salario para salario3
double salario3[] = [Link](salario, [Link]);
// a linha abaixo copia iniciando no elemento 2 e terminando no elemento 3
double salario4[] = [Link](salario, 2, 3);
// a linha abaixo ordena em ordem crescente o array salario
[Link](salario);
36
Para trabalhar com matrizes basta criarmos arrays bidimensionais, tridimensionais
ou n-dimensionais da seguinte forma:
// cria matriz com números inteiros com 3 linhas e 3 colunas
int matriz[][] = new int[3][3];
// cria matriz tridimensional chamada cubo
double cubo[][][] = new double[3][3][3];
2.7 Especificadores e modificadores
O processo de aprendizagem de uma linguagem e em particular da linguagem
Java, leva tempo e dedicação. Com esses dizeres não esperamos desanimar os leitores
mas dizer que eles certamente chegaram ao ponto que desejam se trabalharem as ideias
contidas neste texto. Como em todas as áreas da produção humana, um dia após o outro
perseguindo um caminho longo nos leva ao destino.
Não há como trabalhar com a linguagem Java sem compreender as duas tabelas
seguintes. Essas duas tabelas explicam quando e porque utilizar os especificadores
private, protected e public que determinam a visibilidade dos membros de uma classe.
Os modificadores abstract, final e static que modificam a forma como uma classe e seus
membros são acessados e os processos de herança que também são fundamentais
(JANDL JUNIOR, 2021, p. 92).
Tabela 2.5 – Aplicação dos especificadores e modificadores Java.
Aplicação
Modificador Campo Variável Construtor Método Classe Classe
local interna
abstract não não não simA sim sim
final sim BG sim BG não simB simC simD
native não não não simE não não
private sim não sim sim não sim
protected sim não sim sim não sim
public sim não não sim sim sim
static sim Não não sim não sim
strictfp não Não não sim sim sim
synchronized não Não não sim não não
transiente sim Não não não não
volatile simF Não não não não
(A) Métodos declarados abstratos não podem ter um corpo.
(B) Métodos finais não podem ser sobrepostos em subclasses.
(C) Classes finais não podem ser estendidas por meio da herança.
37
(D) Classes finais internas não podem ser estendidas por meio da herança.
(E) Indica métodos implementados em outras linguagens.
(F) Campos que podem ser assincronamente modificados por threads.
(G) Campos e variáveis finais não podem ter seus valores alterados após sua inicialização.
Fonte: (JANDL JUNIOR, 2021, p. 92 e 93).
A tabela acima e suas observações de (A) a (G) foram copiadas na integra por não
caber alteração em tabela tão útil. A tabela abaixo sofreu o mesmo processo de cópia,
pelo mesmo motivo.
Tabela 2.6 – Descrição dos modificadores especiais da linguagem Java.
Modificador Descrição Aplicação
abstract Indica que a classe não é concreta (não pode ser instanciada), pois Herança
contém membros não implementados, mas cuja interface já está
definida.
final Determina que a classe ou o membro particular de uma classe não pode Herança
ser modificado por meio da herança.
native Indica ao compilador que o método é implementado em outra linguagem JNI
de programação (C, C++, assembly) de maneira especifica da
plataforma.
static Define quando um membro de uma classe passa a pertencer a classe Geral
em si, não precisando ser acessado por variáveis de instância.
strictfp Indica ao compilador que o código gerado deve aderir estritamente à Geral
especificação para cálculos numéricos, garantindo resultados idênticos
mesmo em plataformas diferentes.
synchronized Indica que o método só pode ser acessado por uma thread de cada vez, Threads
exigindo o uso de um monitor por parte da JVM.
Transiente Indica que o campo referido não deve ser serializado. Serialização
volatile Indica campos que podem ser modificados por várias threads Threads
simultâneas, habilitando a manutenção de cópias de trabalho em cada
thread e evitando otimizações inadequadas.
Fonte: (JANDL JUNIOR, 2021, p. 93).
2.8 Anotações
Podemos incluir anotações, ou annotation, dentro do código do programa, essas
anotações são diretivas para o compilador. Introduzidas no Java 5, as anotações
reduzem código e não são necessariamente parte da linguagem Java. Para iniciar uma
anotação é preciso introduzir o caracter arroba “@”. Anotações são metadados (JANDL
JUNIOR, 2021, p. 94).
1 import [Link];
2 import [Link];
3
38
4 public class Principal {
5 @SuppressWarnings("unchecked")
6 public static void main(String[] args) {
7 List<String> minhaLista = new ArrayList();
8 [Link]("Hello");
9 [Link](minhaLista);
10 }
11 }
Programa (2.3) – Ilustração do uso da anotação @suppressWarnings (Construído com o auxílio do Copilot
da Microsoft).
1 class BibliotecaAntiga{
2 @Deprecated
3 void metodoAntigo() {
4 [Link]("Este método é antigo");
5 }
6 }
7
8 public class Principal {
9 public static void main(String[] args) {
10 BibliotecaAntiga biblioteca = new BibliotecaAntiga();
11 [Link]();
12 }
13 }
Programa (2.4) – Ilustração do uso da anotação @Deprecated (Construído com o auxílio do Copilot da
Microsoft).
Abaixo temos as anotações do pacote [Link]:
a) @Override: Essa anotação indica que um método está sobrescrevendo um
método da classe pai. O compilador verifica se o método realmente está
substituindo outro método na hierarquia de classes.
b) @Deprecated: Usada para marcar um método, classe ou campo como
obsoleto. Isso significa que o elemento não é recomendado para uso, pois pode
ser removido em versões futuras.
c) @SuppressWarnings: Essa anotação suprime os avisos do compilador para
um determinado elemento. Por exemplo, você pode
usar @SuppressWarnings("unchecked") para evitar avisos relacionados a tipos
genéricos.
d) @Documented: Indica que as anotações com um tipo devem ser
documentadas pelo Javadoc e ferramentas similares por padrão.
e) @Inherited: Indica que um tipo de anotação é automaticamente herdado pelas
subclasses.
f) @Native: Indica que um campo que define um valor constante pode ser
referenciado a partir de código nativo.
39
g) @Repeatable: Usada para indicar que o tipo de anotação cuja declaração ela
anota é repetível.
h) @Retention: Indica por quanto tempo as anotações com o tipo anotado devem
ser retidas. Os valores possíveis são SOURCE, CLASS e RUNTIME.
i) @Target: Indica os contextos em que um tipo de anotação é aplicável. Por
exemplo, pode ser aplicado a classes, métodos, campos, parâmetros, etc.
(MICROSOFT COPILOT, 2024a)
2.9 Criação de pacotes, módulos e importação de pacotes
Um aplicativo pode apresentar diferentes complexidades, é conveniente formular
nosso código em grupos de programas com funções diferentes que ficam em pacotes
diferentes. Para determinar que um programa está em um pacote digitamos a palavra
package seguida do nome do pacote na primeira linha do programa (JANDL JUNIOR,
2021, p. 132) com a sintaxe que segue abaixo:
package <nome do pacote>;
Para utilizar classes e métodos de outros pacotes é preciso utilizar a declaração
import com o nome do pacote ponto nome da classe que se deseja importar ou import
nome do pacote ponto asterisco que importa todas as classes deste pacote. Veja a
sintaxe abaixo:
import <nome do pacote>.<nome da classe>; // importa uma classe
import <nome do pacote>.*; // importa todas as classes do pacote
Destas duas formas vocês está importando uma classe ou todas as classes do
pacote. Note no programa 2.4 que importamos a classe Scanner do Java na linha 3 para
poder fazer a entrada de dados. E damos o nome de programas para o pacote que
contém a classe Primos, veja a linha 1 do programa 2.4. O Java é composto de muitas
classes e elas são importadas do mesmo modo que as classes criadas para o sistema
por você.
Além da importação normal, temos a importação estática que é feita adicionando a
palavra static após import. Com a importação estática não há necessidade de se referir
a classe para usar uma constante do Java. Veja a sintaxe:
40
import static [Link].*; // importa todos componentes da classe Math
A importação estática só é recomendada quando fazemos uso frequente das
constantes da classe pois a simplificação obtida é pequena.
A partir do Java 9 foi introduzida a criação de módulos. Um módulo é um pacote de
pacotes que são distribuídos como arquivos JAR. Ao nomear um pacote ou módulo, não
é permitido que se use as palavras java e javax, pois elas estão reservadas para
componentes da linguagem. Todo módulo contém um descritor que é um arquivo
denominado [Link] “que relaciona suas dependências” (JANDL JUNIOR,
2021, p. 139).
2.10 Criação de métodos
Veja a criação dos métodos ateNumero(), numeroIsolado(), intervaloDeNumeros()
e verifica(int). Destes, o único método que retorna um conteúdo para linha de sua
chamada é o método verifica(int). O método verifica(int) também é o único que recebe
um parâmetro. Na linha 142 está a declaração do método estático com retorno booleano
(true ou false) para uso privado que se chama verifica. Depois da declaração do método
vem o corpo do método, repare que temos que declarar qual o tipo de retorno que o
método faz, seja um tipo primitivo (char, int, double, etc), seja retorno de um objeto,
classe do objeto retornado. Quando não há retorno é declarado void. Veja os exemplos
de sintaxe nas linhas 142, 67, 93 e 112 do programa 2.4. Os métodos são declarados
estáticos para poderem ser usados sem que haja instanciação da classe Primos.
2.11 Programa de exemplo do capítulo 2
Vamos conceber um programa que verifica se um número é primo em três situações
diferentes: a) começando do um até o número dado, b) entre um intervalo de números
dados e c) um número digitado isolado. O número deve ser menor que 1.000.000, as
opções serão a, b, c e d para escolher como o programa vai trabalhar.
41
Utilizaremos o NetBeans para desenvolver este programa que terá o nome
NumeroPrimo. Entre no NetBeans e escolha File>New Project, o NetBeans irá abrir a
janela New Project clique em Java with Ant na caixa Categories e na caixa Projects clique
em Java Application, depois clique no botão next. O IDE abrirá a janela New Java
Application, no campo Project Name digite a palavra Programas e clique no botão Finish.
Pronto, agora é só digitar o programa abaixo e mandar executar.
01 package programas;
02
03 import [Link];
04
05 /**
06 * @author Ronaldo Rodrigues Godoi
07 */
08 public class Primos {
09
10 private static Scanner entrada = new Scanner([Link]);
11
12 /**
13 * @param args the command line arguments
14 *
15 * Este programa verifica se um número é primo em três situações: a)
16 * começando do um até o número dado b) entre um intervalo de números dados
17 * c) um número digitado isolado
18 *
19 */
20 public static void main(String[] args) {
21
22 char opcao = 'A';
23
24 while (opcao != 'D') {
25
26 do {
27
28 [Link]("\n\n\n Este programa verifica se números são primos");
29 [Link]("\n\n\nOpções: \n\n");
30 [Link]("a - Dê um até o número dado.");
31 [Link]("b - Entre um intervalo de números dados.");
32 [Link]("c - Verifica um numero dado isolado.");
33 [Link]("d - Sair do programa.");
34 [Link]("\n\n\nDigite sua opção: ");
35 opcao = [Link]().charAt(0);
36 opcao = [Link](opcao);
37
38 } while (opcao != 'A' && opcao != 'B' && opcao != 'C' && opcao != 'D');
39
40 switch (opcao) {
41 case 'A':
42 ateNumero();
43 break;
44 case 'B':
45 intervaloDeNumeros();
46 break;
47 case 'C':
48 numeroIsolado();
49 break;
50 case 'D':
51 [Link]();
52 break;
53 default:
54 [Link]("Opção inválida.");
55 break;
56 }
57
42
58 }
59
60 }
61
62 /*
63 * Método ateNumero() que verifica os número primos de 1 até
64 * determinado número inteiro dado, cuja entrada é feita
65 * nesta rotina.
66 */
67 private static void ateNumero() {
68
69 int maximo = 0;
70 int minimo = 1;
71
72 while (maximo < 1 || maximo > 1000000 || maximo <= minimo) {
73
74 [Link]("\n\nDigite o número maior: ");
75 maximo = [Link]();
76 if(maximo < 1 || maximo > 1000000 || maximo <= minimo)
77 [Link]("Faixa de dados inválida!");
78
79 }
80
81 for(int i = minimo; i <= maximo; i++) {
82
83 if(verifica(i)) [Link](i + " é primo!");
84 else [Link](i + " não é primo!");
85
86 }
87 }
88
89 /*
90 * Método numeroIsolado() que permite a entrada de um número inteiro
91 * e verifica se este número é primo.
92 */
93 private static void numeroIsolado() {
94
95 int numero = 0;
96
97 while (numero < 1 || numero > 1000000) {
98 [Link]("\n\nDigite o número a verificar: ");
99 numero = [Link]();
100 }
101
102 if (verifica(numero)) [Link]("Número é primo.");
103 else [Link]("Número foi dividido, ou é o número um, e não é primo.");
104
105 }
106
107 /*
108 * O método intervaloDeNumeros() permite a digitação de um
109 * número mínimo e um número máximo, ambos inteiros e faz
110 * a verificação se os números nesse intervalo são primos.
111 */
112 private static void intervaloDeNumeros() {
113
114 int maximo = 0;
115 int minimo = 0;
116
117 while (maximo < 1 || maximo > 1000000 || maximo <= minimo) {
118
119 [Link]("\n\nDigite o número menor: ");
120 minimo = [Link]();
121 [Link]("\n\nDigite o número maior: ");
122 maximo = [Link]();
123
124 if(maximo < 1 || maximo > 1000000 || maximo <= minimo)
125 [Link]("Faixa de dados inválida!");
126
127 }
128 for(int i = minimo; i <= maximo; i++) {
43
129
130 if(verifica(i)) [Link](i + " é primo!");
131 else [Link](i + " não é primo!");
132
133 }
134
135 }
136
137 /*
138 * A método verifica(int numero) é responsável por receber um número
139 * inteiro e verificar se este número é primo, retornando verdadeiro se o
140 * número for primo e falso se o número não for primo.
141 */
142 private static boolean verifica(int numero) {
143
144 int metade = numero / 2;
145
146 if(numero <= 1) return false;
147
148 for (int i = 2; i <= metade; i++) {
149
150 if (numero % i == 0) return false;
151
152 }
153
154 return true;
155
156 }
157 }
Programa 2.5 – Verificação de números primos com menu. Programa construído pelo autor com o auxílio
das inteligências artificiais Copilot da Microsoft e ChatGPT da OpenAI.
Considerando que não se pode aprender Java sem um computador e praticamente
todos os computadores estão ligados a internet, há um vasto conteúdo sobre Java
disponível. E as inteligências artificiais Copilot da Microsoft e ChatGPT da OpenAI são
excelentes ferramentas que funcionam como instrutores e produtores de código. Neste
contexto torna-se mais fácil entender e aprender Java. Você pode notar que algumas
palavras do programa não foram explicadas neste trabalho, mas formule perguntas as
IA (inteligências artificiais) mencionadas no texto e você obterá uma explicação
detalhada sobre estas estruturas e palavras (classes e métodos).
Ao digitar o programa no IDE, você percebe que vão aparecendo cores diferentes
e sublinhados coloridos, são sinalizações do IDE que te mostram onde estão os
diferentes elementos do programa como variáveis, métodos, palavras reservadas,
classes e também os erros de sintaxe.
Depois de digitar o programa você deve gravar, alias, deve gravar toda vez que
fizer uma alteração. Para gravar basta clicar no ícone do disquete. Modifique o nome do
programa clicando com o botão direito sobre o nome, entrando no menu refactor e
escolhendo a opção Rename. Digite na caixa de texto New Name, o nome Primos e
44
depois clique no botão Refactor. O IDE modificará o nome do arquivo e dentro do
programa modificará o nome da classe pública.
Para executar o programa 2.4, usando a IDE NetBeans, clique no menu Run na
primeira opção Run Project ou aperte a tecla F6. Neste programa, para terminar a
execução basta responder ao menu com a letra d.
45
3 ORIENTAÇÃO A OBJETOS
3.1 Programação orientada ao objeto na linguagem Java
3.1.1 Variáveis, objetos, classes e construtores, métodos e visibilidade
[Link] Variáveis
Variável é um identificador (palavra que designa um conteúdo em todo seu escopo)
que pode conter valores de tipo primitivos (int, double, char, etc.) ou referências a objetos
(String, ArrayList, classes de usuário, etc.). Observe as declarações abaixo, nas quais
temos variáveis de tipos primitivos, não objetos:
int idade = 30; // declara a variável idade do tipo inteiro.
double salario = 2500.50; // declara a variável salario do tipo double.
char letra = ‘A’; // declara a variável letra do tipo char.
boolean estaChovendo = true;//declara a variável estaChovendo do tipo boolean.
byte valorPequeno = 127; // declara a variável valorPequeno do tipo byte.
short temperatura = -10; // declara a variável temperatura do tipo short.
long populacaoMundial = 7800000000L; //
float altura = 1.75f; // declara a variável altura do tipo float.
Cada tipo primitivo tem sua função, e os tipos primitivos oferecem as seguintes
vantagens: desempenho em comparação com objetos, pois são armazenadas na
memória stack que permite acesso mais rápido; menor consumo de memória, ocupam
menos memória pois armazenam valores simples e não objetos complexos; evita
overhead de referência pois não são objetos; estão sempre inicializadas, e apresentam
sempre um valor padrão se não forem inicializadas explicitamente, assim evitam
NullPointerException; operações matemáticas, são ideais para operações lógicas e
matemáticas; e simplicidade, armazenam e manipulam valores de maneira simples e
direta sem as complexidades adicionais das classes wrapper (MICROSOFT COPILOT,
2024d).
46
[Link] Objetos
Objeto é gerado a partir de uma classe (código java). Quando criamos objetos com
o operador new, estamos criando uma instância de uma classe com todas as suas
variáveis, métodos e objetos. As instâncias dos objetos residem na memória heap
(MICROSOFT COPILOT, 2024d). Observe as declarações com instanciação de alguns
tipos principais de objetos de classes pertencentes a linguagem Java, abaixo:
String nome = new String(“João”); // cria objeto tipo String de conteúdo João.
ArrayList<String> = new ArrayList<>(); // cria um array de lista de String.
HashMap<String, Integer> mapa = new HashMap<>(); // ver capítulo 5.
HashSet<Integer> conjunto = new HashSet<>(); // ver capítulo 5.
Scanner scanner = new Scanner([Link]); // cria objeto para entrada de dados.
Random aleatorio = new Random(); // cria objeto para gerar números aleatórios.
File arquivo = new File(“c:\pasta\subpasta\[Link]”); //ver capítulo 5.
Thread thread = new Thread(); // cria uma nova thread de execução.
BigDecimal valor = new BigDecimal(“123.45”); //cria objeto da classe BigDecimal.
Date data = new Date(); // inicia um objeto que deverá conter um valor de data.
“Em resumo, objetos são a espinha dorsal da POO e permitem que programadores
criem programas que são estruturados, reutilizáveis, e mais fáceis de manter”
(MICROSOFT COPILOT, 2024d).
[Link] Classes e construtores
Uma classe é um modelo a partir do qual objetos são criados, na classe estão
definidos os estados e comportamentos que os elementos da classe apresentam. Para
definir uma classe usa-se a palavra class e ela pode apresentar: variáveis de instâncias
que são os atributos; métodos que são elementos ativos e podem transformar os
atributos; construtores que são blocos de inicialização das classes, entre outros
(MICROSOFT COPILOT, 2024d).
Um construtor é o bloco de código que é chamado quando uma classe é
instanciada, o propósito do construtor é inicializar o novo objeto. Uma classe pode ter
47
múltiplos construtores para iniciar seus elementos de maneiras diferentes, com valores
diferentes de acordo com a necessidade. Um construtor tem uma assinatura que inclui o
nome do construtor (que é o nome da classe) e a lista de parâmetros, dois construtores
na mesma classe não podem ter a mesma assinatura, ou seja, devem diferir em tipo ou
número de parâmetros. Observe a classe abaixo com diferentes construtores:
// Definição da classe Carro
public class Carro {
// Variáveis de instância
private String marca;
private String modelo;
private int ano;
// Construtor sem parâmetros
public Carro() {
// Inicializa com valores padrão
[Link] = "Desconhecida";
[Link] = "Desconhecido";
[Link] = 0;
}
// Construtor com um parâmetro (marca)
public Carro(String marca) {
// Inicializa com a marca fornecida e valores padrão para os outros campos
[Link] = marca;
[Link] = "Desconhecido";
[Link] = 0;
}
// Construtor com todos os parâmetros (marca, modelo, ano)
public Carro(String marca, String modelo, int ano) {
// Inicializa com os valores fornecidos para todos os campos
[Link] = marca;
[Link] = modelo;
[Link] = ano;
}
// Métodos getters para acessar os valores das variáveis de instância
public String getMarca() {
return marca;
}
public String getModelo() {
return modelo;
}
public int getAno() {
return ano;
}
// Método para exibir informações do carro
public void exibirInformacoes() {
[Link]("Marca: " + marca + ", Modelo: " + modelo + ", Ano: " + ano);
}
// Método principal para testar os construtores
public static void main(String[] args) {
// Utilizando o construtor sem parâmetros
Carro carro1 = new Carro();
[Link](); // Exibe: Marca: Desconhecida, Modelo: Desconhecido, Ano: 0
// Utilizando o construtor com um parâmetro
Carro carro2 = new Carro("Chevrolet");
[Link](); // Exibe: Marca: Chevrolet, Modelo: Desconhecido, Ano: 0
48
// Utilizando o construtor com todos os parâmetros
Carro carro3 = new Carro("Fiat", "Uno", 2020);
[Link](); // Exibe: Marca: Fiat, Modelo: Uno, Ano: 2020
}
}
Programa 3.1 – Gerado pela IA para demonstrar funcionamento de classes e construtores (MICROSOFT
COPILOT, 2024d).
Quando uma classe tem vários construtores, dizemos que os construtores estão
sobrecarregados, conceito conhecido como sobrecarga de construtores. Há três
construtores para a classe Carro, acima: o primeiro é Carro() que não recebe parâmetros,
cuja assinatura é simplesmente Carro(); o segundo é Carro(String marca) que recebe
um parâmetro, cuja assinatura é Carro(String) – vai receber um valor tipo String; e o
terceiro construtor Carro(String marca, String modelo, int ano) que recebe dois
parâmetros do tipo String e um parâmetro do tipo int, sua assinatura é Carro(String,
String, int). Um construtor especifico tem que ter uma assinatura única composta pelo
nome da classe e parâmetros que recebe: Carro(), Carro(String), Carro(String, String,
int). Lembre-se String é uma cadeia de caracteres e int é um número inteiro. Observe no
código do programa 3.1 o comportamento do método exibirInformacoes().
Considere os três pontos como código omitido.
Ao codificar uma classe Java, devemos escrever o nome da classe com inicial
maiúscula, é um padrão adotado na programação, é uma convenção. Quando você vir
uma palavra em Java que começa com letra maiúscula pode dizer que se trata de uma
classe Java, se a convenção tiver sido seguida. Classes Java são código escrito na
linguagem Java que podem ser programas completos ou partes de programas utilizadas
por outras classes.
Se uma classe não tiver construtor codificado e declarado explicitamente, o Java
“fornecerá automaticamente um construtor padrão sem argumentos” que é o construtor
default e pode ser chamado da mesma forma que o construtor declarado e codificado
explicitamente.
[Link] Métodos
49
Não existe método sem classe. O comportamento dos métodos podem ser
classificados como: a) métodos de instância que requerem uma objeto da classe a qual
pertencem para serem executados, veja os exemplos de instanciação de objetos no item
[Link], foram criados objetos de dez classes que podem chamar métodos das classes
as quais eles pertencem; e b) métodos estáticos são aqueles que não precisam de
objetos mas da referência a classe para serem executados.
[Link] Visibilidade
3.1.2 Declaração e instanciação: qual a diferença?
Podemos declarar um objeto, isto é informar ao compilador que determinado objeto
existe, mas não é uma instanciação. A declaração deve ser feita sempre antes na
instanciação. Na declaração é especificado o tipo e o nome da variável (ou objeto),
exemplo:
String nome;
Para instanciar utiliza-se a palavra new, veja o exemplo:
nome = new String(“Ronaldo”);
Veja três exemplos de declaração e posterior instanciação:
// Declarações dos objetos
Carro meuCarro;
Livro meuLivro;
Smartphone meuSmartphone;
// Instanciações dos objetos
meuCarro = new Carro("Fusca", 1980);
meuLivro = new Livro("Dom Casmurro", "Machado de Assis");
meuSmartphone = new Smartphone("Xiaomi", "Redmi Note 9");ne = new
Smartphone("Xiaomi", "Redmi Note 9");
50
Dessa forma temos primeiro as declarações e em outro momento as instanciações
dos mesmos objetos. Esta separação é vantajosa sob o ponto de vista de oferecer:
flexibilidade no controle de fluxo, pois permite que instanciemos o objeto caso
determinada condição seja satisfeita utilizando o if, por exemplo; gerenciamento de
recursos, pois criamos o objeto apenas quando necessitamos, uma vez que ele consome
recursos do sistema; desacoplamento de código, pois separar a declaração da
instanciação pode tornar o código mais modular sem alterar as referências ao objeto;
melhor desempenho em casos específicos, pois adiar a instanciação retarda o consumo
de recursos; e, finalmente, controle de escopo, pois podemos declarar o objeto em um
escopo mais amplo e instanciá-lo num mais restrito permite controlar a acessibilidade
(MICROSOFT COPILOT, 2024d).
3.1.3 Operador ponto (.) e operador dois pontos dois pontos (::)
3.1.4 Métodos estáticos
3.2 Herança, polimorfismo, encapsulamento e abstração
Herança é um efeito em que estendemos uma classe de forma que criamos uma
classe filha com os mesmos elementos que a classe pai, isso se processa através da
palavra extends. Quando declaramos que uma classe estende outra, essa nova classe
herda métodos, construtores, variáveis da classe pai.
Veja a sintaxe de extends no exemplo abaixo:
// Super classe pai
public class Vertebrados {...}
51
// Classe filha
public class Mamiferos extends Vertebrados {
public String getEspecie() { return “Pan troglodytes”; }
}
// Classe neta
public class Primatas extends Mamiferos {...}
Uma classe filha pode sobrescrever um método, alterando seu conteúdo para os
objetos derivados dessa classe, utilizando a anotação @Override (MICROSOFT
COPILOT, 2024c) na linha acima da declaração onde consta o nome do método da
classe pai que será sobrescrito pelo novo código. O método da classe pai, ou super
classe, fica alterado. O processo de sobrescrever um método é um exemplo de
polimorfismo, muitas formas (JANDL JUNIOR, 2021, p. 147-148). Veja, abaixo, a
sobrescrição do método getEspecie que retornava Pan troglodytes, e agora, para os
mamíferos, retornará Homo sapiens:
public class Primatas extends mamiferos {
@Override
public String getEspecie() { return “Homo sapiens”; }
}
O novo método tem a mesma assinatura que o anterior. Esse mecanismo é uma
forma de implementação do polimorfismo.
O encapsulamento é obtido criando-se modelos de objetos para representar o
mundo mas determinando que seus atributos sejam private, de forma que para ter
acesso (consultar ou alterar) esses atributos, necessitaremos de métodos
implementados pelo programador, chamados getters e setters, que podem ser public.
Examine o programa abaixo e verifique a estrutura dos métodos getNome e setNome
que são respectivamente getters e setters. Neste caso, para ter acesso ao nome do
animal, e consulta-lo, devo invocar o getNome() e para alterar o nome do animal devo
invocar o método setNome(“...novo nome”).
1 package Programas;
2
3 // Definindo uma classe com encapsulamento
4 public class Animal {
5 // Atributos privados
6 private String nome;
7 private int idade;
8
9 // Construtor
52
10 public Animal(String nome, int idade) {
11 [Link] = nome;
12 [Link] = idade;
13 }
14
15 // Método getter para o atributo 'nome'
16 public String getNome() {
17 return nome;
18 }
19
20 // Método setter para o atributo 'nome'
21 public void setNome(String nome) {
22 [Link] = nome;
23 }
24
25 // Método getter para o atributo 'idade'
26 public int getIdade() {
27 return idade;
28 }
29
20 // Método setter para o atributo 'idade'
31 public void setIdade(int idade) {
32 [Link] = idade;
33 }
34
35 public static void main(String[] args) {
36 // Criando um objeto da classe Animal
37 Animal meuAnimal = new Animal("Leão", 5);
38
39 // Exibindo os valores iniciais dos atributos
40 [Link]("Nome: " + [Link]());
41 [Link]("Idade: " + [Link]());
42
43 // Modificando os valores dos atributos
44 [Link]("Tigre");
45 [Link](3);
46
47 // Exibindo os novos valores dos atributos
48 [Link]("Nome atualizado: " + [Link]());
49 [Link]("Idade atualizada: " + [Link]());
50 }
51 }
Programa 3.2 – Ilustração de abstração através de programa desenvolvido pela inteligência artificial
ChatGPT, testado e adaptado pelo autor (OPENAI, 2024c).
Crie uma nova classe para digitar o programa acima, clicando com o botão direito
do mouse no pacote Programas e escolhendo a opção New > Java Class, digite o nome
da classe Animal na caixa de texto Class Name e aperto o botão Finish. Será gerado um
novo arquivo que deve ser modificado de acordo com o código do programa 3.1. Execute
o programa. Você vai obter o seguinte resultado:
run:
Nome: Leão
Idade: 5
Nome atualizado: Tigre
Idade atualizada: 3
BUILD SUCCESSFUL (total time: 0 seconds)
Esse resultado aparecerá na janela Output – ProgramasJava21.
53
A abstração se dá através da declaração de métodos sem o corpo de código, dentro
de classes abstratas. Esses métodos também são declarados abstratos. Quando uma
classe filha estende a classe abstrata, deve implementar o código do método declarado
na classe pai, obrigatoriamente. Na classe exemplo designada Principal, programa 3.2,
temos a classe abstrata Animal que declara o método abstrato barulho, observe que este
método é implementado de maneira diferente nas classes filhas Cachorro e Gato. Os
benefícios da abstração são: simplificação, pois focasse no essencial e a abstração
simplifica o código facilitando a compreensão e manutenção; reusabilidade, onde o
código com a classe abstrata serve de base para múltiplas subclasses; e flexibilidade,
com as classes e interfaces abstratas que permanecem as mesmas modificando-se
partes sem afetar outras (OPENAI, 2024d).
1 package Programas;
2
3 // Definindo uma classe abstrata
4 abstract class Animal {
5
6 // Método abstrato (sem implementação)
7 public abstract void barulho();
8
9 // Método concreto (com implementação)
10 public void comer() {
11 [Link]("Este animal está comendo.");
12 }
13 }
14
15 // Subclasse que herda da classe abstrata Animal
16 class Cachorro extends Animal {
17
18 // Implementação do método abstrato
19 @Override
20 public void barulho() {
21 [Link]("Au, au, au!");
22 }
23 }
24
25 // Subclasse que herda da classe abstrata Animal
26 class Gato extends Animal {
27
28 // Implementação do método abstrato
29 @Override
30 public void barulho() {
31 [Link]("Miau, miau, miau!");
32 }
33 }
34
35 public class Principal {
36 public static void main(String[] args) {
37 // Criando instâncias das subclasses
38 Animal meuCachorro = new Cachorro();
39 Animal meuGato = new Gato();
40
41 // Chamando os métodos
42 [Link](); // Output: Au, au, au!
43 [Link](); // Output: Este animal está comendo.
44
45 [Link](); // Output: Miau, miau, miau!
54
46 [Link](); // Output: Este animal está comendo.
47 }
48 }
Programa 3.3 – Programa para demonstrar abstração em métodos, desenvolvido completamente pela
inteligência artificial e adaptado pelo autor (OPENAI, 2024d).
Abra o IDE, crie uma nova classe com o nome Principal, digite, salve e execute o
programa 3.2. Neste programa utiliza-se o operador ponto em várias linhas, criam-se
várias classes em um único arquivo chamado [Link]. Apresenta-se a criação do
método comer que é concreto e do método barulho que é abstrato. Lembre-se: um
arquivo de código Java só pode conter uma classe public.
3.3 Interfaces
Interfaces são espécies de contratos, compromissos com o projeto que se está
desenvolvendo. Estabelecendo uma interface, sua característica é ser abstrata embora
não seja possível qualificá-la no código Java com a palavra abstract e seus métodos
possam ser static (estáticos), default (padrão) ou abstract (abstratos) com suas
respectivas declarações no corpo do código contendo implementação (default ou static)
ou apenas a sua assinatura. Os métodos de uma interface podem ter seu corpo de código
e, neste caso, ser considerados padrão (default), declarando-se a palavra default ao
nomeá-lo. Os métodos abstratos são declarados por sua assinatura e o seu corpo é
construído na classe que implementa a interface. Não há necessidade de fazer a
declaração abstract na interface. Todos métodos abstratos devem ser implementados
quando uma classe implementar uma interface (OPENAI, 2024e).
A interface determina métodos que podem assumir tratamentos semelhantes para
os objetos submetidos a eles, porém, facilitando o polimorfismo, se estabelece um
tratamento definido a cada classe que implementa a interface. O calcule da área do
objeto da classe Triangulo é um e o cálculo da área de um objeto da classe Quadrado é
outro, mas são semelhantes pois são cálculos de área. Assim, uma interface que tenha
o método calculaArea() deve ser implementado de forma diferente mas em ambas as
classes (Quadrado, Triangulo). Criar uma interface Polígonos com o método abstrato
55
calculaArea() nos permite observar um processamento semelhante mas peculiar a cada
tipo de polígono.
1 package Programas;
2
3 interface Poligono {
4 double calcularArea();
5 }
6
7 class Quadrado implements Poligono {
8 private double lado;
9
10 public Quadrado(double lado) {
11 [Link] = lado;
12 }
13
14 @Override
15 public double calcularArea() {
16 return lado * lado;
17 }
18 }
19
20 class Triangulo implements Poligono {
21 private double base;
22 private double altura;
23
24 public Triangulo(double base, double altura) {
25 [Link] = base;
26 [Link] = altura;
27 }
28
29 @Override
30 public double calcularArea() {
31 return (base * altura) / 2;
32 }
33 }
34
35 public class Geometria {
36 public static void main(String[] args) {
37 Poligono quadrado = new Quadrado(4);
38 Poligono triangulo = new Triangulo(3, 5);
39
40 [Link]("Área do Quadrado: " + [Link]());
41 [Link]("Área do Triangulo: " + [Link]());
42 }
43 }
Programa 3.4 – Implementado pelo Chat GPT, revisado pelo autor.
3.6 Programa exemplificando o capítulo 3
56
4 SWING
4.1 Introdução ao AWT e ao Swing
Os sistemas operacionais que atualmente são ambientes gráficos como Windows
11, Mac OS e outros, baseados em X-Windows System, tem seus elementos nas
interfaces gráficas de usuários (GUI – Graphical User Interface) gerados por Java no
Swing. “[...] Um componente GUI é um objeto visual com o qual o usuário pode interagir
por meio do teclado ou mouse e realizar operações como entrada de valores” (JANDL
JUNIOR, 2021, p. 219) e outras compatíveis com os elementos gráficos do Windows e
outros sistemas operacionais. Botões, caixas de entrada de texto, caixas de lista de
seleção e opção, barras de rolagem, menus são exemplos de elementos GUI gerados
em Java com o Swing (JANDL JUNIOR, 2021, p. 219).
Os pacotes [Link] e [Link] que fazem parte da biblioteca de componentes
GUI, utilizam com predominância o modelo componente-compartimento. O
compartimento é chamado de container onde “se localiza e cuja interface dá acesso aos
serviços gerais providos pelo container” (JANDL JUNIOR, 2021, p. 219).
Abstract Windows Tolkit que toma a sigla AWT foi a primeira biblioteca de
componentes Java e é composta de um conjunto básico de componentes com um
modelo robusto de eventos, gerenciadores de layout que permitem a criação de
aplicações GUI podendo ser utilizadas por grande parte dos sistemas (JANDL JUNIOR,
2021, p. 220).
A API (Application Programming Interface) usa elementos nativos da plataforma
que são implementados peer-to-peer (ponto a ponto) onde as classes AWT são uma
casca para componentes nativos dos sistemas operacionais (JANDL JUNIOR, 2021, p.
220).
Isso facilita o acesso a componentes de diferentes plataformas, e as diferenças
entre os sistemas são ocultadas (JANDL JUNIOR, 2021, p. 220).
No entanto, o mecanismo complexo e pesado de troca de mensagens, exigido entre o sistema
operacional e a JVM, reduz a performance dos componentes AWT (por isso são chamados de
componentes pesados ou heavy weight components) (JANDL JUNIOR, 2021, p. 220).
57
A API Component que é efetivada através da classe abstrata Component, base de
todos componentes GUI do pacote AWT, oferece infraestrutura para obter objetos num
ambiente gráfico (dotados de interação com usuário) e por ser classe abstrata não pode
ser instanciada diretamente. Essa API, Component, permite manipular “tamanho, cor,
fonte, cursor, estado de funcionamento, posição e visibilidade do componente” (JANDL
JUNIOR, 2021, p. 220).
O JFC – Java Foundation Classes é um framework para construção de aplicações
gráficas que inclui o Swing – família de componentes de operação homogênea nas
diferentes plataformas – e o Drag and Drop – que oferece mecanismo de arrastar e soltar
entre aplicações Java e nativas – entre outros (JANDL JUNIOR, 2021, p. 221).
Componentes Swing são 100% Java, não utilizando código nativo, sendo
componentes leves (light weight components). Há necessidade de cuidados especiais
por parte dos programadores no uso de threads, com relação ao Swing, pois se encontra
um comportamento em que múltiplos fluxos de execução não atuam consistentemente
com atributos (JANDL JUNIOR, 2021, p. 221-222).
Para construir uma aplicação GUI é preciso determinar quais componentes serão
utilizados, qual a disposição destes elementos, quais tarefas serão realizadas. Isto pode
ser feito através dos seguinte passos: (a) desenho da janela com a disposição dos
componentes, especificação das ações de usuários e reações correspondentes; (b)
criação de uma subclasse JFrame para cada janela; (c) declaração dos componentes
ativos como elementos privados; (d) implementação do construtor para organizar os
componentes e registrar os eventos; (e) programação do event listeners implementando
os métodos que processarão os eventos selecionados; e por fim, (f) inclusão do método
main(args[]) na janela principal da aplicação (JANDL JUNIOR, 2021, p. 222).
Aplicações GUI frequentemente utilizam componentes do tipo container como
JFrame, JPanel ou JScrollPane para conter os componentes que serão dispostos neslas
de duas formas: (a) através da definição direta de tamanho e posição dos componentes
adicionados ao container; (b) delegando a dimensão e posição dos componentes ao
gerenciador de layout do container (JANDL JUNIOR, 2021, p. 228).
Quando optamos por dimensionar e posicionar os componentes manualmente,
usamos coordenadas x e y, e definimos largura e altura para os componentes, tendo
58
como referência o canto superior esquerdo da janela, e devemos lembrar que alterações
no tamanho da janela exigem esforço adicional do programador para reposicionar e
redimensionar os componentes da interface GUI (JANDL JUNIOR, 2021, p. 229).
A segunda opção, com relação a posicionamento e dimensionamento de
componentes, são os gerenciadores de layout (layout managers), classes capazes de
determinar como os componentes de um container serão arranjados, administrando a
posição e dimensão, ou ambas, de cada componente; sendo que são oferecidos pelo
AWT, os seguintes layouts: FlowLayout, GridLayout, BorderLayout, CardLayout e
GridBagLayout, além destes, pelo Swing são oferecidos: BoxLayout, GroupLayout,
OverlayLayout e SpringLayout; cada layout tem uma disposição especifica com
características diferentes e, muitas vezes, um destes layouts se enquadra melhor na
necessidade do programador (JANDL JUNIOR, 2021, p. 229).
Para não usar os layouts oferecidos pelo AWT e Swing e definir manualmente a
posição e tamanho dos componentes, precisamos definir no identificador do container o
layout como desativado e para isso é necessário usar o método
setLayout(LayoutManager) da classe Container com o argumento null, da forma que
consta no trecho de código abaixo:
1 ...
2 public class ExemploNull extends JFrame {
3 public ExemploNull() {
4 setSize(200,150);
5 Container cont = getContentPane();
6 [Link](null);
7 ...
8 }
9 public static void main(String arg[]) {
10 new ExemploNull().setVisible(true);
11 }
12 }
Nas linhas 1 e 7, temos três pontos (reticencias) que indicam existência de um
trecho de código omitido nesta posição; na linha 2, começa a construção do código
pertencente a classe ExemploNull que extende JFrame; na linha 3, é criado o construtor
da classe ExemploNull; na linha 4, é definido o tamanho da janela; na linha 5, é
59
instanciado um objeto chamado cont da classe Container; na linha 6, é desligado o layout
(conforme orientado em parágrafo acima, antes do trecho de código); a linha 9 declara
um método principal main para começar a execução do programa; a linha 10 declara o
objeto da classe ExemploNull que extende JFrame e o torna visível com o método
setVisible(true); e as linhas 11 e 12 fecham respectivamente o método main e a classe
ExemploNull.
Quando os layouts AWT e Swing estão desligados, é necessário definir a posição
e o tamanho dos componentes através dos métodos setLocation(x, y), onde x e y são as
posições dadas em número inteiro e setSize(largura, altura) que determina o tamanho,
ou pode-se utilizar o método setBounds(x, y, largura, altura) que define posição no eixo
xy da tela e tamanho através da largura e altura dos componentes (JANDL JUNIOR,
2021, p. 229).
Da mesma forma que o método setLayout desativa os layouts, conforme linha 6 do
código acima, esse método pode ser usado para escolher o layout adotado quando
fazemos uso do recurso de determinação automática de posição e tamanho dos
componentes AWT e Swing. Isto é feito conforme as linhas de código demonstram
abaixo:
1 ...
2 public class ExemploLayoutLigado extends JFrame {
3 public ExemploLayoutLigado() {
4 ...
5 setSize(200, 150);
6 ...
7 Container cont = getContentPane();
8 [Link](new FlowLayout());
9 ...
10 }
11 public static void main (String args[]){
12 new ExemploLayoutLigado().setVisible(true);
13 }
14 }
As linhas 1, 4, 6 e 9 têm reticências para indicar trecho de código omitido. Na linha
2, abrimos uma classe chamada ExemploLayoutLigado que estende JFrame; na linha 3,
60
iniciamos o código do construtor da classe; na linha 5, definimos o tamanho da janela;
na linha 7, instanciamos o container, usando o identificador cont; a linha 8 define que o
layout adotado para os elementos que compõem a GUI será FlowLayout; e a linha 11
declara o método main; a linha 12 faz uma instanciação de um objeto anônimo, ou seja,
não fica associado a um identificador e não será referenciado pelo programador
posteriormente, o mesmo ocorre na linha 8. Esses objetos são, na linha 12, a classe
ExemploLayoutLigado e, na linha 8, um objeto da classe FlowLayout, cada um com
funções diferentes.
Os componentes devem ser adicionados ao container empregando o método
add(componente) da seguinte forma (considere o identificador cont da linha 5 do trecho
de código acima) :
[Link](componente);
onde: componente é o nome do identificador que foi instanciado com a classe do
objeto que será adicionado ao container, e portanto, a janela GUI.
Todos os demais layouts disponíveis são utilizados da mesma forma que o
FlowLayout, ou seja, são determinados pelo método setLayout(layout), porém cada
layout apresenta um construtor próprio de acordo com suas características (assinatura,
por exemplo). Alguns layouts podem ter containers como o JPanel contendo diversos
componentes dentro de si, permitindo assim uma variedade grande de composições de
telas, o que resulta em flexibilidade e maior utilidade dos layouts AWT e Swing.
4.2 Elementos gráficos: principais componentes
Os componentes Swing tem seus nomes iniciados pela letra J, entre os
componentes estão os que seguem: JButton, JCheckButton, JRadioButton,
JToggleButton, JLabel, JPasswordField, JTextArea, JTextField, JTextPane,
JCheckBoxMenuItem, JMenu, JMenuBar, JMenuItem, JPopupMenu,
JRadioButtonMenuItem, JSeparator, JProgressBar, JScrollBar, JSlider, JComboBox,
JList, JDeskTopPane, JFrame, JInternalFrame, JWindow, JDialog, JColorChooser,
61
JFileChooser, JOptionPane, JLayeredPane, JPanel, JScrollPane, JSplitPane,
JTabbedPane, JViewPort, JTable, JToolBar, JTree (JANDL JUNIOR, 2021, p. 247-248).
Cada um desses componentes, cujo nome começa com a letra J, é uma classe.
Não existe componente sem sua respectiva classe, instanciada. As classe pertencentes
ao Swing são os elementos que permitem ao programador inserir cada componente GUI
em seus sistemas. Podemos dizer que a classe gera o componente quando é instanciada
e adicionada em um container.
A classe JComponent inclui o tratamento da entrada via teclado; o suporte para dicas e
acessibilidade; os elementos para desenho do componente na tela, incluindo suas bordas; além de
suportar propriedades específicas para os componentes derivados. Sua API é compartilhada por
suas subclasses (JANDL JUNIOR, 2021, p. 247).
Devemos combinar componentes para obter os resultados desejados, por exemplo,
uma lista (JList) ou área de texto (JTextArea) precisam ser combinadas a um
componente JScrollPane para poder exibir comportamento de rolagem; isto confere
“flexibilidade no uso desses componentes” (JANDL JUNIOR, 2021, p. 248).
4.2.1 Containers e Janelas
Há containers considerados principais que são janelas principais da aplicação e
normalmente não são adicionados a outros containers; intermediários que podem ser
adicionados a container principal ou outro container intermediário; e especializados que
tem função específica. São containers principais: JFrame (janela principal das aplicações
que possui todos elementos das janelas Windows), JDialog (diálogos modais ou não
modais), JWindow (janela sem borda e sem controles minimizar, maximizar e fechar).
São containers intermediários: JPanel (mais usado, pode conter componentes embutidos
nele, inclusive outros containers), JScrollPane (fornece uma barra de rolagem para o
container que o contem), JSplitPane (divide dois componentes e permite que o usuário
redimensione a divisão), JTabbedPane (permite a criação de interface com abas),
JToolBar (pode conter botões e outros componentes, usado normalmente para fornecer
atalhos), JLayeredPane (permite sobrepor componente em diferentes camadas),
JDesktopPane (cria interface de múltiplos documentos – MDI – podendo ter janelas
internas). São containers especializados: JInternalFrame (janela que pode ser usada
62
dentro de um JDesktopPane, criando interface de múltiplos documentos), JRootPane
(serve de base para maioria do containers principais, não é usado diretamente), JLayer
(decorativo, para adicionar efeitos visuais em componentes existentes) (OPENAI,
2024a).
Veja o exemplo abaixo, onde são criados um JFrame com JPanel contendo um
JButton:
1 import [Link];
2 import [Link];
3 import [Link];
4
5 public class Example {
6 public static void main(String[] args) {
7 // Cria um JFrame
8 JFrame frame = new JFrame("Exemplo de JFrame");
9 [Link](JFrame.EXIT_ON_CLOSE);
10 [Link](400, 300);
11
12 // Cria um JPanel
13 JPanel panel = new JPanel();
14
15 // Cria um JButton
16 JButton button = new JButton("Clique-me");
17
18 // Adiciona o JButton ao JPanel
19 [Link](button);
20
21 // Adiciona o JPanel ao JFrame
22 [Link](panel);
23
24 // Torna o JFrame visível
25 [Link](true);
26 }
27 }
Programa 4.1 – Produzido pela inteligência artificial Chat GPT, como exemplo do uso de containers
(OPENAI, 2024a).
“Os painéis, como todos componentes Swing, podem ter suas bordas definidas pelo
método setBorder(Border)” (JANDL JUNIOR, 2021, p. 266). O java busca fornecer os
mais variados métodos para configurar os elementos dos objetos Swing de modo geral,
inclusive as características visuais.
Um container muito interessante é o JTabbedPane que é usado para gerar abas
sobrepostas. As abas podem ter um rótulo de texto, imagem, uma dica tool tip (de
funcionalidade) e um container, normalmente JPanel, além de uma barra de rolagem
JScrollPane (JANDL JUNIOR, 2021, p. 271).
Conhecimentos adicionais sobre os diversos construtores das classes, classes,
assinatura dos métodos, métodosdevem ser procurados nas referências deste trabalho
e outras fontes, uma vez que este trabalho não contempla todos os elementos de Java
63
mas classes e métodos mais utilizados. Mesmo para compreender os exemplos de
programas Java, deve-se procurar novas fontes, atitude esperada em um estudante –
fazer perguntas diretas ao Chat GPT ou Microsoft Copilot são um caminho semelhante
a conversar com um professor.
4.2.2 Botões
Cada classe Java, sendo do Swing ou não, pode apresentar métodos variados
adicionais aos de suas super classe que interagem e auxiliam o funcionamento dos
objetos que derivam dela. “A classe AbstractButton oferece a infraestrurua dos
componentes Swing que se comportam como totões, em adição aos métodos disponíveis
de JComponent. [...] As classes que implementam os botões Swing são JButton,
JToggleButton, JCheckBox e JRadioButton” (JANDL JUNIOR, 2021, p. 256). Através dos
botões, apertando-os ou acionando-os, determina-se que ocorra uma ação como
executar um método ou rotina (linhas de código) associada aos respectivos botões.
Pode-se definir texto, imagem de um botão JButton. Esse componente produz,
entre outros, eventos ActionEvent que são manipulados através da interface
ActionListener, método actionPerformed (JANDL JUNIOR, 2021, p. 257). A classe
JButton da mesma forma que muitas outras classes, possui vários construtores com
assinaturas diferentes.
A classe de botões JToggleButton permite que o botão fique ligado ou desligado e
pode ser reunido em grupo de botões, também produz ActionEvent de implementação
pela interface ActionListener (JANDL JUNIOR, 2021, p. 258).
O JCheckBox exibe um conjunto de opções em que nenhuma ou várias opções
estão selecionadas. O JRadioButton apresenta um conjunto de opções que apenas uma
delas pode ser selecionada, necessitando fazer parte de um grupo de objetos que
pertence a um objeto da classe ButtonGroup. Esse agrupamento permite a desseleção
de um botão quando outro é acionado. Botões JRadioButton podem ser adicionados e
removidos, respectivamente pelos métodos add e remove.
64
Observe o programa exemplo abaixo, com botões JButton e JRadioButton, e
elementos JCheckBox, digite o programa no IDE e execute para entender os
componentes e os listeners de eventos.
1 package programas;
2
3 import [Link].*;
4 import [Link].*;
5
6 public class SwingButtonsExample3 {
7 public static void main(String[] args) {
8 JFrame frame = new JFrame("Botões do Swing");
9 [Link](JFrame.EXIT_ON_CLOSE);
10 [Link](new GridLayout(1, 2)); // Configura os dois JPanel lado a lado
11
12 // JPanel para JRadioButton
13 JPanel radioPanel = new JPanel();
14 [Link](new GridLayout(3, 1));
15
16 JRadioButton radioButton1 = new JRadioButton("Opção um");
17 JRadioButton radioButton2 = new JRadioButton("Opção dois");
18 JRadioButton radioButton3 = new JRadioButton("Opção três");
19
20 ButtonGroup group = new ButtonGroup();
21 [Link](radioButton1);
22 [Link](radioButton2);
23 [Link](radioButton3);
24
25 [Link](radioButton1);
26 [Link](radioButton2);
27 [Link](radioButton3);
28
29 // JPanel para JCheckBox
30 JPanel checkBoxPanel = new JPanel();
31 [Link](new GridLayout(3, 1));
32
33 JCheckBox checkBox1 = new JCheckBox("Caixa de seleção um");
34 JCheckBox checkBox2 = new JCheckBox("Caixa de seleção dois");
35 JCheckBox checkBox3 = new JCheckBox("Caixa de seleção três");
36
37 [Link](checkBox1);
38 [Link](checkBox2);
39 [Link](checkBox3);
40
41 // JButton para limpar seleções
42 JButton clearButton = new JButton("Limpar Seleções");
43
44 // Define o tamanho preferido do botão
45 [Link](new Dimension(110, 30));
46
47 // Adiciona os JPanel e o JButton ao JFrame
48 [Link](radioPanel);
49 [Link](checkBoxPanel);
50
51 // JPanel para o JButton usando FlowLayout
52 JPanel buttonPanel = new JPanel(new FlowLayout([Link]));
53 [Link](clearButton);
54 [Link](buttonPanel);
55
56 [Link](400, 200);
57 [Link](true);
58
59 // Adiciona o ActionListener com expressão lambda
60 [Link](e -> {
61 [Link](); // Limpa seleção dos JRadioButton
62 [Link](false); // Desmarca JCheckBox
63 [Link](false);
64 [Link](false);
65
65 });
66 }
67 }
Programa 4.2 – Desenvolvido pela inteligência artificial Chat GPT, e testado pelo autor em IDE NetBeans
(OPENAI, 2024b).
Note que o evento de pressionar o botão e limpar as seleções dos botões radio e
caixas check box, foi desenvolvido utilizando uma expressão lambda. Com essa
funcionalidade (expressão lambda) da linguagem obtemos um código mais simples sem
a necessidade de implementar o método actionPerformed, veja abaixo os exemplos de
implementação com e sem a função lambda:
a) Sem lambda:
[Link](new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
[Link]();
[Link](false);
[Link](false);
[Link](false);
}
});
b) Com Lambda:
[Link](e -> {
[Link]();
[Link](false);
[Link](false);
[Link](false);
});
O tópico expressões lambda será desenvolvido mais profundamente no próximo
capítulo. É um importante recurso introduzido na versão 8 do Java, juntamente com
outras características importantes.
4.2.3 Componentes de Texto
São componentes para entrada e edicação de textos: JTextField, JTextArea,
JPasswordField e JFormattedTextField.
A caixa de texto JTextField permite a edição de uma linha de texto, produz um
ActionEvent tratado pela interface ActionListener, permite alinhamento (à direita, à
66
esquerda e centralizado) de acordo com o tamanho do texto, e também permite a
formatação com escolha da fonte e cor. O JTextField é super classe de JPasswordField,
que é destinada a entrada de senhas, e de JFormattedTextField, que faz entrada de
campos com formatação definida. JTextArea se destina a edição de múltiplas linhas de
texto, não permite que a formatação do texto contido em seus componentes tenha
características diferentes para os seus diversos fragmentos (fonte, alinhamento, cor) e
para que seus componentes tenham barra de rolagem é necessário acrescentar a eles
um objeto da classe JScrollPane (JANDL JUNIOR, 2021, p. 260-265).
O programa abaixo ilustra a utilização dos quatro tipos de componente de texto.
1 package programas;
2
3 import [Link].*;
4 import [Link].*;
5 import [Link].*;
6 import [Link].*;
7 import [Link].*;
8
9 public class EditorTexto {
10 public static void main(String[] args) {
11 JFrame frame = new JFrame("Editor de Texto");
12 [Link](500, 500);
13 [Link](null);
14 [Link](JFrame.EXIT_ON_CLOSE);
15
16 JLabel labelNomeArquivo = new JLabel("Nome do arquivo:");
17 [Link](10, 10, 120, 25);
18 [Link](labelNomeArquivo);
19
20 JTextField campoNomeArquivo = new JTextField();
21 [Link](140, 10, 200, 25);
22 [Link](campoNomeArquivo);
23
24 JButton botaoGravarCarregar = new JButton("Gravar/Carregar");
25 [Link](350, 10, 130, 25);
26 [Link](botaoGravarCarregar);
27
28 JLabel labelSenha = new JLabel("Senha:");
29 [Link](10, 45, 50, 25);
30 [Link](labelSenha);
31
32 JPasswordField campoSenha = new JPasswordField();
33 [Link](140, 45, 200, 25);
67
34 [Link](campoSenha);
35
36 JLabel labelNomeAutor = new JLabel("Nome do autor:");
37 [Link](10, 80, 100, 25);
38 [Link](labelNomeAutor);
39 JTextField campoNomeAutor = new JTextField();
40 [Link](140, 80, 200, 25);
41 [Link](campoNomeAutor);
42
43 JLabel labelCPF = new JLabel("CPF do autor:");
44 [Link](10, 115, 100, 25);
45 [Link](labelCPF);
46
47 JFormattedTextField campoCPF = null;
48 try {
49 MaskFormatter formatter = new MaskFormatter("###.###.###-##");
50 campoCPF = new JFormattedTextField(formatter);
51 [Link](140, 115, 200, 25);
52 [Link](campoCPF);
53 } catch (Exception e) {
54 [Link]();
55 }
56
57 JLabel labelTextoEdicao = new JLabel("Texto em edição:");
58 [Link](10, 150, 120, 25);
59 [Link](labelTextoEdicao);
60
61 JTextArea campoTextoEdicao = new JTextArea();
62 [Link](10, 180, 470, 250);
63 [Link](true);
64 [Link](true);
65 JScrollPane scrollPane = new JScrollPane(campoTextoEdicao);
66 [Link](10, 180, 470, 250);
67 [Link](scrollPane);
68
69 JRadioButton radioQuebraLinha = new JRadioButton("Quebra linha por palavras", true);
70 [Link](10, 440, 200, 25);
71 [Link](radioQuebraLinha);
72
73 JRadioButton radioNaoQuebraLinha = new JRadioButton("Não quebra linha");
74 [Link](220, 440, 150, 25);
75 [Link](radioNaoQuebraLinha);
76
77 ButtonGroup grupoQuebraLinha = new ButtonGroup();
78 [Link](radioQuebraLinha);
79 [Link](radioNaoQuebraLinha);
68
70
71 [Link](e -> {
72 [Link](true);
73 [Link](true);
74 });
75
76 [Link](e -> {
77 [Link](false);
78 [Link](false);
79 });
80
81 JFormattedTextField finalCampoCPF = campoCPF;
82 [Link](e -> {
83 String senha = new String([Link]());
84 if (!"Java21".equals(senha)) {
85 [Link](frame,
86 "Senha incorreta!",
87 "Erro",
88 JOptionPane.ERROR_MESSAGE);
89 return;
90 }
91
92 String nomeArquivo = [Link]();
93 if ([Link]()) {
94 [Link](frame,
95 "Nome do arquivo não pode ser vazio.",
96 "Erro",
97 JOptionPane.ERROR_MESSAGE);
98 return;
99 }
100
101 File arquivo = new File(nomeArquivo);
102 if ([Link]()) {
103 try (BufferedReader br = new BufferedReader(new FileReader(arquivo))) {
104 [Link]([Link]());
105 [Link]([Link]());
106 [Link]([Link]());
107 } catch (IOException ex) {
108 [Link]();
109 }
110 } else {
111 try (BufferedWriter bw = new BufferedWriter(new FileWriter(arquivo))) {
112 [Link]([Link]() + "\n");
113 [Link]([Link]() + "\n");
114 [Link]([Link]());
115 } catch (IOException ex) {
116 [Link]();
69
117 }
118 }
119 });
120
121 [Link](true);
122 }
123 }
Programa 4.3 – Pequeno editor de texto desenvolvido integralmente pela inteligência artificial Chat GPT,
mediante especificações e correções do autor (OPENAI, 2024d).
4.2.4 Listas e Combo Box (caixas de combinação)
A classe JList gera um objeto para manipular e exibir uma lista que permite a
seleção de seus itens em ambiente gráfico. Esta seleção feita pelo usuário pode ser de
apenas um item, um grupo de vários itens contínuos ou grupos de itens com intervalos
entre eles (JANDL JUNIOR, 2021, p. 273-278). Para definir um modo de seleção, entre
os três permitidos e citados, utiliza-se o método setSelectionMode com as respectivas
opções:
a) ListSelectionModel.SINGLE_SELECTION;
b) ListSelectionModel.SINGLE_INTERVAL_SELECTION;
c) ListSelectionModel.MULTIPLE_INTERVAL_SELECTION.
Esse método deve ser aplicado ao objeto JList. Suponhamos a existência do objeto
lista do tipo JList, aplicaremos o método setSelectionMode com a seguinte sintaxe:
[Link](ListSelectionModel.SINGLE_SELECTION);
A instrução acima determina que será permitida a seleção de um único item na lista.
E, da mesma forma, ListSelectionModel.SINGLE_INTERVAL_SELECTION permite a
seleção de um intervalo de linhas da lista de forma contínua; e
ListSelectionModel.MULTIPLE_INTERVAL_SELECTION, determina que poderão ser
selecionados intervalos diversos separados por linhas não selecionadas.
Este componente, JList, permite que se associe um objeto DefaultListModel para
compor a lista adicionando itens com o método addElement e removendo itens com o
método remove, aplicados ao objeto modelo gerado pela classe DefaultListModel. O
objeto modelo deve ser adicionado ao JList, veja as sintaxes abaixo:
DefaultListModel<String> modelo = new DefaultListModel<>();
70
JList<String> lista = new JList<>(modelo);
No objeto modelo podemos adicionar itens da lista utilizando-se da sintaxe, abaixo:
[Link](“HB 20”);
[Link](“Ford Ka”);
[Link](“Fox”);
E remover itens, por exemplo, o selecionado, com a sintaxe abaixo:
int j = [Link]();
[Link](j);
Também podem ser utilizados ícones compostos por figuras com o tipo de arquivo
GIF, por exemplo, para serem linhas de itens de uma lista; tornando as listas elementos
gráficos. Isso é obtido utilizando-se objetos JLabel compostos pelo item da lista que têm
uma imagem que servirá de ícone para ilustrar a lista.
Não se pretende esgotar todas as possibilidades de uso das classes e incluir todos
os seus métodos, e sim, abordar algumas formas de codificação Java e suas filosofias
de programação; o que é mais importante. O desenvolvedor Java deve explorar a
linguagem por conta própria e buscar conhecimento, seja nas referências deste trabalho,
seja em material da internet, para apropriar-se de mais métodos e formas de uso que lhe
ajudem a tornar-se um desenvolvedor mais completo.
Os objetos DefaultListModel apresentam recursos adicionais através dos métodos:
addElement, removeElement, insertElementAt, removeElementAt, addAll, removeAll,
indexOf, lastIndexOf, setElementAt. Além de apresentar a classe ListDataListeners “para
reagir a mudanças na lista” (MICROSOFT COPILOT, 2024d).
Apesar dos recursos que adicionar um objeto modelo gerado a partir da classe
DefaultListModel a um componente JList oferece, podemos criar uma lista adicionando
diretamente na instanciação itens selecionáveis na forma de um array de Strings
contendo as linhas da lista. Isso é feito com a seguinte sintaxe:
String[] opcoes = {"Chevrolet", "Fiat", "Volkswagen", "Ford", "Renault"};
JList<String> listaDeOpcoes = new JList<>(opcoes);
JComboBox
71
4.2.6 Menus
JMenuBar, Jmenu, JMenuItem, JCheckBoxMenuItem, JRadioButtonMenuItem,
JPopupMenu
4.2.7 JFileChooser, JOptionPane, JColorChooser,
4.2.8 JSlider, JTree, JSpinner
4.3 Controle de eventos
O que se objetiva com o uso do computador é produzir um resultado útil que tenha
valor significativo nas atividades humanas dos mais variados setores: produção
industrial, processamento de dados, e quaisquer outras atividades que facilitem a vida
do homem.
Para tanto, podemos admitir que o funcionamento dos processos computacionais
se baseiam nos elementos: entrada, processamento e saída de dados (SILVA, E. J.;
SIDNEI, B. C.; COLARULHO, M. A., 1988, p. 2). Representado, da melhor forma, na
figura abaixo:
Figura 4.1 – Esquema do funcionamento geral dos sistemas computacionais.
Entrada Processamento Saída
Fonte: (SILVA, E. J.; SIDNEI, B. C.; COLARULHO, M. A., 1988, p. 2).
72
Para operar neste contexto, o AWT e o Swing oferecem componentes, eventos e
listeners (JANDL JUNIOR, 2021, p. 237-238). O que entendi, do funcionamento dessa
interface gráfica homem-máquina, é que os eventos são ocorrências de entrada com
dispositivos como: mouse, teclado, leitor de código de barras, sensores dos mais
diversos tipos para os mais diversos contextos que são tratados através de listeners de
eventos. Considerando isto, uma vez que a entrada é efetuada e o listener escuta essa
informação, é executado um processamento correspondente ao código programado para
produzir o efeito desejado associado ao evento. Mas, a simples passagem do tempo
dispara eventos então, posso concluir que entrada de dados são eventos mas eventos
não são sempre entradas de dados. Há uma referência sobre eventos que não são
disparados por mouse, teclado, sensores diversos em Java: The Complete Reference
(SCHILDT, H.; COWARD, D.; 2024).
Os componentes AWT e Swing são elementos encontrados nos ambientes gráficos
(sistemas operacionais) disponíveis para composição dos diversos sistemas, aplicativos
e programas. Sua fundamentação é a entrada e saída de dados, ambas com o auxílio
do monitor, tela dos computadores.
Os principais eventos produzidos pela maioria dos componentes usados nas
aplicações Swing são os que seguem: (a) evento que indica a ocorrência de alguma
ação, como acionamento de botões (JButton), itens de menu (JMenuItem), tecla enter
em caixa de texto (JTextField), estes são eventos ActionEvent que são implementados
pela interface ActionListener; (b) notificação das classe interessadas em modificações
na posição do cursor produzidas em componentes de edição de texto (JTextField e
JTextArea), estes eventos são CaretEvent que são implementados pela interface
CaretListener; (c) posicionamento de controle deslizante (JSlider) ou troca de aba
(JTabbedPane), estes eventos são ChangeEvent que são implementados pela interface
ChangeListener; (d) posicionamento, redimensionamento ou mudança na visibilidade de
componente, estes eventos são ComponentEvent que são implementados pela interface
ComponentListener; (e) modificação em documento, como conteúdo de componentes de
texto (JTextField e JTextArea), estes eventos são DocumentEvent que são
implementados pela interface DocumentListener; (f) perda ou ganho de foco são eventos
FocusEvent que são implementados pela interface FocusListener; (g) seleção ou
73
desseleção de caixas de opção ou listas e combos (JCheckBox, JRadioButton,
JComboButton e JList), estes eventos são ItemEvent que são implementados pela
interface ItemListener; (h) acionamento de teclas gerado por caixas de texto (JTextField)
ou botões (JButton) são KeyEvent que são implementados pela interface KeyListener; (i)
mudança de seleção em lista (JList) ou tabela (JTable), estes eventos são
ListSelectionEvent que são implementados pela interface ListSelectionListener; (j)
acionamento do mouse (pressionar, movimentar, arrastar) sobre determinado
componente, estes eventos são MouseEvent que são implementados pelas interfaces
MouseListener, MouseMotionListener e MouseWheelListener; (k) operações que podem
ser desfeitas são eventos UndoableEditEvent que são implementadas pela interface
UndoableEditListener; (l) alteração do estado de uma janela (JFrame e JDialog) é o
evento WindowEvent que é implementado pela interface WindowListener (JANDL
JUNIOR, 2021, p. 241-242).
Considerando que programas Java devem poder ser executados em diferentes
plataformas, entre os eventos citados no parágrafo anterior há os eventos que são de
alto nível e podem ter sua programação feita de forma mais simples pois tem igual
tratamento em todas as plataformas (sistemas operacionais), estes são ditos eventos
semânticos; e há os eventos que são de baixo nível, este tipo de evento, “as vezes,
requer considerar detalhes específicos da plataforma, sendo portanto de implementação
mais complexa” (JANDL JUNIOR, 2021, p. 242).
O tratamento de eventos aciona as rotinas que efetuam os processamentos devidos
as ocorrências de seus eventos. Para tanto é necessária a codificação do método
add<evento>Listener que mostraremos na implementação abaixo, neste caso, com uso
de uma expressão lambda:
[Link]((e) -> { metodoExecutadoNoEvento(); }
A interface ActionListener é funcional e contém apenas um método, então a classe anônima usada
para definir listeners desse tipo pode ser substituída por uma expressão lambda, tornando sua
construção mais simples (JANDL JUNIOR, 2021, p. 226).
Observe que os métodos devem ser implementados com acesso public, recebem
sempre um único argumento e “não possuem valor de retorno, i. e., são sempre void”
(JANDL JUNIOR, 2021, p. 242).
74
Outra forma de implementação para este listener, exige a implementação do
método actionPerformed, da forma como colocado abaixo:
[Link](new actionListener({
public void actionPerformed(ActionEvent e) {
metodoExecutadoNoEvento();
}
});
Note que a primeira forma de implementação é mais concisa e podemos dizer que
utiliza conceitos de programação Java mais modernos, fáceis de implementar, concisos.
Uma das considerações que deve ser levada em conta é que ActionListener é uma
interface funcional, ou seja, só possui um método e portanto na declaração através da
expressão lambda, o sistema conclui que se trata do único método desta interface,
tornando desnecessário cria-lo (MICROSOFT COPILOT, 2024b).
4.4 Programa exemplificando SWING no capítulo 4
75
5 EXPRESSÕES LAMBDA, COLEÇÕES, STREAMS E THREADS
5.1 Expressões lambda
[..] Do mesmo modo que os genéricos reformularam Java vários anos atraz, expressões lambda
continuam reformulando Java hoje. […]. Expressões lambda impactarão virtualmente todos
programadores Java” (SCHILDT, H.; COWARD, D., 2024, p. 395, tradução nossa).
Expressões lambda são, essencialmente, métodos anônimos. Expressão lambda é
um método anônimo usado para implementar um método definido por uma interface
funcional. Assim, uma expressão lambda resulta em uma forma de classe anônima,
comumente referida como closures. Closures são características que permitem a uma
expressão lambda usar uma variável definida em seu escopo mas, as closures,
apresentam restrições (SCHILDT, H.; COWARD, D., 2024, p. 395, tradução nossa).
A interface funcional – interface que apresenta apenas um método abstrato – é
importante para compreender expressões lambda. Esse único método abstrato das
interfaces funcionais, normalmente, especifica o propósito da interface. Esse,
relativamente novo recurso (lambda) introduzido na versão 8 do Java, é particularmente
interessante no desenvolvimento de programação com Stream API e ambientes
multicore e seu processamento paralelo (SCHILDT, H.; COWARD, D., 2024, p. 395,
tradução nossa).
Interface funcional é uma SAM – Single Abstract Method – método abstrato único.
Com a introdução das expressões lambda, surge um novo operador, chamado
operador seta (->). Em cada lado do operador há uma parte da expressão lambda, do
lado esquerdo há os parâmetros de entrada necessários e do lado direito está o corpo
da expressão lambda. Este especifica as ações da expressão lambda. O operador seta
fica entre os parâmetros e o corpo da expressão. Este operador é composto por um sinal
de menos encostado em um sinal de maior (->). Veja abaixo o formato de uma expressão
lambda (SCHILDT, H.; COWARD, D., 2024, p. 395, tradução nossa):
parâmetros de entrada -> corpo da expressão lambda
76
Pode não haver parâmetros de entrada e, neste caso, o lado esquerdo assume a
forma de abre e fecha parenteses (). No caso de haver um único parâmetro os
parenteses podem ser omitidos. Exemplos de função lambda:
Runnable tarefa = () -> [Link]("Executando uma tarefa");
Consumer<String> impressora = mensagem -> [Link](mensagem);
Function<String, Integer> comprimentoString = s -> [Link]();
Supplier<Double> valorAleatorio = () -> [Link]();
Predicate<Integer> ehPar = numero -> numero % 2 == 0;
BiFunction<Integer, Integer, Integer> soma = (a, b) -> a + b;
UnaryOperator<Integer> quadrado = x -> x * x;
BinaryOperator<Integer> multiplicacao = (a, b) -> a * b;
Comparator<String> comparadorString = (s1, s2) -> [Link](s2);
IntConsumer imprimeInt = i -> [Link](i);
Analise os seguintes nomes: Runnable, Consumer, Function, Supplier, Predicate,
BiFunction, UnaryOperator, BinaryOperator, Comparator, IntConsumer. Eles começam
com uma letra maiúscula, são interfaces funcionais e tem apenas um método abstrato
que pode ser implementado da forma como os exemplos ilustram, por expressões
lambda. “A maneira mais comum de usar expressões lambda é implementando
diretamente interfaces funcionais” (OPENAI, 2024e). Além do método abstrato,
interfaces funcionais podem ter métodos default (implementados dentro da interface com
implementação padrão), métodos estáticos (chamados sem necessidade de
instanciação), métodos privados (só podem ser chamados dentro da mesma interface)
(OPENAI, 2024e).
Você pode perceber a presença do operador seta em todos os blocos de código
que compõe este item 5.1, até este ponto. Esse operador seta pode ser lido ou
interpretado como “se torna”, “vai para”; uma vez que podemos atribuir a uma variável o
retorno da expressão lambda. O corpo da expressão lambda pode ser uma instrução ou
um bloco de instruções, vejamos algums exemplos de implementação de instruções
lambda mais simples, primeiro sem atribuição de valor e segundo com atribuição do valor
retornado para uma variável, juntamente com sua declaração:
Tabela 5.1 – Formulação e resultado da execução de expressões lambda sem atribuição e com.
Expressão lambda sem variável Expressão lambda com atribuição de seu retorno a
uma variável
77
Resultado Expressão Trabalho da Resultado Expressão
expressão
lambda
Executa () -> [Link] Imprimir uma Expressão lambda não retorna
.println("Olá, mundo!"); mensagem valor
Erro () -> 42; Retornar um Executa Integer valorConstante = () ->
valor contante 42;
Erro numero -> numero % 2 == Verificar se um Executa boolean numeroPar = numero ->
0; número é par numero%2 == 0;
Erro numero -> numero * 2; Dobrar um Executa int numeroDobrado = numero ->
número numero*2;
Erro texto->[Link](); Verificar se uma Executa boolean textoVazio = texto ->
String é vazia [Link]();
Erro x -> x * x; Calcular o Executa int quadradoNumero = x -> x * x;
quadrado de um
número
Erro () -> [Link](); Gerar um Executa double numeroAleatorio = () ->
número aleatório [Link]();
Erro (s1, s2) -> s1 + s2; Concatenar Executa String concatenacao=(s1, s2) ->
duas Strings s1 + s2;
Erro s -> Character Verificar se uma Executa boolean comecaComMaiuscula=s ->
.isUpperCase(s String começa Character
.charAt(0)); com uma letra .isUpperCase([Link](0));
maiúscula
Erro (a, b) -> a - b; Subtrair dois Executa int resultadoSubtracao=(a, b)->
números a - b;
Fonte: Organização e análise do autor, expressões OPENAI, 2024e.
Abaixo temos o resultado da análise, caso a caso, das dez expressões lambda que
o proprio ChatGPT forneceu.
Conclusão: Em todos os casos analisados, a implementação sem o uso de
expressões lambda parece ser mais simples, direta e fácil de entender. Expressões
lambda são mais úteis quando se trata de passar comportamentos como
argumentos para métodos ou quando se trabalha com coleções e operações de
stream. No entanto, para tarefas simples e diretas como as analisadas aqui, usar
expressões lambda não traz benefícios claros e pode até mesmo tornar o código
menos legível (OPENAI, 2024e).
Concordo totalmente com a análise dessa poderosa ferramenta de inteligência
artificial. As expressões lambda devem ser pensadas antes de serem implementadas.
Mas o que gostariamos de expor com esses dez exemplos de expressões lambda
é suas duas formulações abaixo:
() -> instrução ou bloco
( parametros ) -> instrução ou bloco
E que expressões lambda podem ser implementadas com e sem uso de interfaces
funcionais.
Tratando-se de todas as operações com objetos e variáveis, deve-se respeitar o
tipo, ou seja, quando se usa interfaces funcionais devem ser compatíveis os tipos de
parâmetros fornecidos a expressão lambda e os retornos, quando houver.
78
Até o momento, utilizamos expressões lambda que fazem uso de interfaces
funcinais pré-definidas ou aquelas que não precisam de interfaces funcionais. Observe
o programa abaixo e veja a definição de uma interface funcional pelo usuário:
1 package Programas;
2
3 import [Link];
4
5 /**
6 *
7 * @author Ronaldo Rodrigues Godoi
8 */
9
10 interface IMC {
11 double calcula(double m, double a);
12 }
13
14 public class IndiceMassaCorporea {
15 public static void main(String[] args) {
16 Scanner scanner = new Scanner([Link]);
17 IMC imcIF = (m, a) -> m / (a * a);
18
19 // Solicitar a entrada da massa
20 [Link]("Digite a massa (kg): ");
21 double massa = [Link]();
22
23 // Solicitar a entrada da altura
24 [Link]("Digite a altura (m): ");
25 double altura = [Link]();
26
27 // Calcular o IMC
28 double imc = [Link](massa, altura);
29
30 // Exibir o resultado
31 [Link]("Seu IMC é: %.2f%n", imc);
32
33 [Link]();
34 }
35 }
Programa 5.1 – Definição de uma interface de usuário e implementação de seu método por uma expressão
lambda.
Abra o NetBeans e crie a classe acima, execute. Ela tem duas entradas de dados
(linhas 21 e 25) para as variáveis massa e altura; tem uma interface functional definida
por usuário chamada IMC (linhas 10 à 12) que tem um método abstrato chamado calcula
(linha 11). Esse método abstrato é implementado pela expressão lambda dentro de um
objeto de identificador imcIF com dois parâmetros de entrada (linha 17). Dentro do
método main é chamado o método calcula acessando o objeto imcIF (linha 28).
Neste programa 5.1, podemos compreender que interfaces funcionais são
codificadas pelo programador ou já existem na linguagem Java, assim nos servimos de
sua existência para aplicar seus métodos às expressões lambda contidas nos objetos
instanciados através delas. Desta forma, podemos ter a implementação de qualquer tipo
de parâmetro de entrada para as expressões lambda.
79
A implementação de streams para processamento de objetos das coleções é ponto
importante na caracterização das expressões lambda como um elemento que transforma
a forma de programação, lhe contribuindo com grande poder. Uma vez que coleções tem
algoritmos de mais alta eficiência.
5.2 Coleções
Coleções é um framework composto por interfaces, métodos e classes; além de
elementos como Enuns, Annotations, pacotes (OPENAI, 2024e). Coleções ou
Collections, promete alta performance em ordenação e filtragem de grupos de objetos
que podem ser mais facilmente manipulados e armazenados de forma padronizada.
Provê iteradores como forma de acesso. Possui o recurso de objetos mapa formados por
chave e valor (SCHILDT, H; COWARD, D.; 2024, p. 582 e 583, tradução nossa).
Objetos implementados através de interfaces como List, Queue, Set e Map
fornecem métodos para pesquisar, ordenar, incluir, remover conteúdo de objetos que
favorecem o processamento de coleções com alta performance ([Link]
acessado em 04/07/2024).
5.2.1 List (ArrayList, LinkedList, Vector e Stack)
5.2.2 Queue (PriorityQueue)
5.2.3 Set (HashSet, LinkedHashSet)
5.2.4 Map (HashMap, LinkedHashMap e HashTable)
5.2.1 Genéricos
80
5.2.2 Operador diamante (<>)
5.3 Streams
5.4 Threads
Todo programa tem uma Thread principal que inicia no método main quando ele
começa a ser executado, “ela pode ser controlada através de um objeto Thread”. Existem
duas formas de criar nossas threads: uma é estendendo a classe Thread; e a outra é
implementando a interface Runnable. Ambas as formas exigem que seja implementado
o método run(). A primeira forma impede que a classe que estende Thread possa
estender outras classes, por isso, deve ser utilizada menos que a implementação da
interface Runnable (SCHILDT, H.; COWARD, D., 2024, p. 253-258, tradução nossa).
Podemos trabalhar com várias threads, de acordo com nossa necessidade,
considerando que os processadores de hoje tem vários núcleos e as necessidades da
computação exigem recursos que trabalhem em pararelo diversas linhas de
processamento ao mesmo tempo.
1 package Programas;
2
3 class Contador extends Thread {
4 private String nome;
5
6 public Contador(String nome) {
7 [Link] = nome;
8 }
9
10 @Override
11 public void run() {
12 for (int i = 0; i <= 10; i++) {
13 [Link]("Thread " + nome + ": " + i);
14 try {
15 [Link](110);
81
16 } catch (InterruptedException e) {
17 [Link]();
18 }
19 }
20 }
21 }
22
23 public class Principal01 {
24 public static void main(String[] args) {
25 Contador threadA = new Contador("A");
26 Contador threadB = new Contador("B");
27 Contador threadC = new Contador("C");
28
29 [Link]();
30 [Link]();
31 [Link]();
32
33 for (int i = 0; i <= 5; i++) {
34 [Link]("Thread Principal: " + i);
35 try {
36 [Link](330);
37 } catch (InterruptedException e) {
38 [Link]();
39 }
40 }
41 }
42 }
Programa 5.2 – Implementado sob orientação do autor pelo Chat GPT (OPENAI, 2024e). Execução
conferida pelo autor.
Observe o programa acima (5.4.1), neste programa há a criação de três threads
adicionais a do método main. O objeto que cria essas threads recebe o nome de A, B e
C, elas fazem a contagem até dez “dormindo” a cada 110 milisegundos enquanto a
thread principal conta até cinco “dormindo” a cada 330 milisegundos.
5.4 Programa exemplificando o capítulo 5
82
6 BANCO DE DADOS E JDBC
6.1 Sistemas gerenciadores de banco de dados
6.2 Driver e JDBC
6.3 Executando comandos no Banco de dados com JDBC
6.4 Programa de cadastro exemplificando o capítulo 6
83
7 O SISTEMA DESENVOLVIDO
7.1 Cadastros
7.2 Movimentos
7.3 Consultas em tela
7.4 Relatórios impressos
7.5 Manutenção do sistema
7.6 Arquivos de configuração e bibliotecas incorporadas
84
8 PADRÕES DE PROJETO
Os padrões de projeto são soluções reutilizáveis para problemas recorrentes no
desenvolvimento de software. Descrevem formas bem definidas de organizar e estruturar
o código dos programas. Na obra Design Patterns: Elements of Reusable Object-
Oriented Software, que tem os seguintes autores: Erich Gamma, Richard Helm, Ralph
Johnson e John Vlissides – Gang of Four (GoF), Gangue dos Quatro, são documentados
23 destes padrões. É importante ter em mente, que esse livro da GoF, foi desenvolvido
para programação orientada ao objeto usando C++ e Smalltalk, desta forma, é preciso
adaptar para a linguagem Java. Um livro escrito para Java e que serve de referência
para esta obra é Design Patterns in Java, escrito por S. J. Metsker e W. C. Wake. Segue
a enumeração dos nomes dos 23 padrões de projeto, de acordo com a Gang dos Quatro:
(1) Abstract Factory – Fábrica Abstrata, (2) Builder – Construtor, (3) Factory Method
– Método de Fábrica, (4) Prototype – Protótipo, (5) Singleton – Instância Única, (6)
Adapter – Adaptador, (7) Bridge – Ponte, (8) Composite – Composto, (9) Decorator –
Decorador, (10) Facade – Fachada, (11) Flyweight – Peso-Mosca, (12) Proxy –
Procurador, (13) Chain of Responsibility – Cadeia de Responsabilidade, (14)
Command – Comando, (15) Interpreter – Interpretador, (16) Iterator – Iterador, (17)
Mediator – Mediador, (18) Memento - Registro (ou Memória), (19) Observer –
Observador, (20) State – Estado, (21) Strategy – Estratégia, (22) Template Method -
Método Modelo, e (23) Visitor – Visitante (OPENAI, 2024e).
Neste trabalho nos restringiremos a estes 23 padrões, mas existem muitos outros.
De fato, estes padrões se sobressaem como soluções úteis que podem ser empregadas
em diversas situações. No livro Design Patterns in Java, de Metsker e Wake, os 23
padrões de projeto estão divididos em cinco categorias (METSKER, S. J.; WAKE, W. C.;
2006):
a) Padrões de Interface: Adaptador, Fachada, Composto, Ponte;
b) Padrões de Responsabilidade: Instância Única, Observador, Mediador,
Procurador, Cadeia de Responsabilidade e Peso-Mosca;
c) Padrões de Construção: Construtor, Método Fábrica, Fábrica Abstrata,
Protótipo e Registro (ou Memória);
85
d) Padrões de Operação: Método Modelo, Estado, Estratégia, Comando,
Interpretador;
e) Padrões de Extensão: Decorador, Iterador e Visitante.
8.1 Padrões de interface
8.1.1 Adaptador (ou Adapter)
[Link] Adaptador de classe (ou Class Adapter)
Você está codificando um programa para controlar processos com tomadas. Há
tomadas de várias nacionalidades, vamos supor, brasileira e americana.
Considere que uma classe Cliente usa uma classe do padrão de projetos
adaptador para chamar um método da classe TomadaAmericana, em vez do método
abstrato conectar() da interface Tomada. O método conectar() é implementado por
classes que implementam Tomada, como é o caso de TomadaBrasileira. No entanto,
o cliente deverá usar o método plugin() da classe TomadaAmericana, pois suas
necessidades determinam que seja usado esse método – suponhamos, que por razão
da complexidade deste método e seus efeitos sobre a classe Cliente. De fato, nosso
exemplo é bastante simples, mas o método plugin() poderia trazer questões que
somente são tratadas adequadamente por ele, desta forma, obrigando o cliente a fazer
uso deste método. Para tanto, o padrão adaptador foi implementado pela classe
AdaptadorTomadaAmericana, que também implementa a interface Tomada. No
momento em que é chamado o método conectar() o adaptador chama o método
plugin(), conforme o Programa 8.1, resolvendo o problema de compatibilidade das
tomadas.
No IDE (Ambiente de Desenvolvimento Integrado) de sua escolha crie um projeto
chamado Java21. Dentro desse projeto crie o pacote Adapter, nesse pacote crie uma
nova classe Java com o nome Cliente. Criando essa classe, automaticamente um
arquivo [Link] é iniciado, nesse arquivo você deve digitar o programa 8.1 e salvar.
86
Siga as linhas do programa, procurando entender o funcionamento da classe
AdaptadorTomadaAmericana que é nossa classe do padrão de projeto adapter.
0 package adapter;
1 // Interface de destino que o cliente espera
2 interface Tomada {
3 void conectar();
4 }
5
6 // Implementação concreta da Tomada
7 class TomadaBrasileira implements Tomada {
8 public void conectar() {
9 [Link]("Tomada brasileira conectada.");
10 }
11 }
12
13 // Classe existente com uma interface incompatível
14 class TomadaAmericana {
15 public void plugIn() {
16 [Link]("Tomada americana plugada.");
17 }
18 }
19
20 // Adaptador que permite que uma TomadaAmericana seja usada como uma Tomada
21 class AdaptadorTomadaAmericana extends TomadaAmericana implements Tomada {
22 public void conectar() {
23 plugIn();
24 }
25 }
26
27 // Classe cliente que usa a interface de destino
28 public class Cliente {
29 public static void main(String[] args) {
30 // Usando uma Tomada brasileira
31 Tomada tomadaBrasileira = new TomadaBrasileira();
32 [Link]();
33
34 // Usando uma Tomada americana via Adaptador
35 Tomada adaptador = new AdaptadorTomadaAmericana();
36 [Link]();
37 }
38 }
Programa 8.1 – Padrão de projeto adaptador de classe ou class adapter (MICROSOFT COPILOT, 2024d).
Revisado e testado pelo autor.
[Link] Adaptador de objetos (ou Object Adapter)
Neste caso, a interface Tomada não existe e, portanto, esse contrato dentro do
código se torna mais fraco (METSKER, S. J.; WAKE W. C.; 2006, p. 350, tradução
nossa), ou inexistente. Cria-se a necessidade de instanciar as classes
AdaptadorTomadaAmericana e TomadaAmericana. Pode ser notado que a motivação
para implementação deste padrão atende as mesmas questões do item [Link].
87
No seu IDE, crie a classe Java Cliente2, digite o código do programa abaixo, grave
o arquivo e execute-o. Note a diferença entre o programa 8.1 e este, que é o programa
8.2.
0 package adapter;
1 // Classe existente com a interface incompatível
2 class TomadaAmericana {
3 public void plugIn() {
4 [Link]("Tomada americana plugada.");
5 }
6 }
7
8 // Interface de destino que o cliente espera
9 class TomadaBrasileira {
10 public void conectar() {
11 [Link]("Tomada brasileira conectada.");
12 }
13 }
14
15 // Adaptador que permite que uma TomadaAmericana seja usada como uma TomadaBrasileira
16 class AdaptadorTomadaAmericana {
17 private TomadaAmericana tomadaAmericana;
18
19 public AdaptadorTomadaAmericana(TomadaAmericana tomadaAmericana) {
20 [Link] = tomadaAmericana;
21 }
22
23 public void conectar() {
24 [Link]();
25 }
26 }
27
28 // Classe cliente que usa a interface de destino
29 public class Cliente2 {
30 public static void main(String[] args) {
31 // Usando uma Tomada brasileira
32 TomadaBrasileira tomadaBrasileira = new TomadaBrasileira();
33 [Link]();
34
35 // Usando uma Tomada americana via Adaptador
36 TomadaAmericana tomadaAmericana = new TomadaAmericana();
37 AdaptadorTomadaAmericana adaptador = new AdaptadorTomadaAmericana(tomadaAmericana);
38 [Link]();
39 }
40 }
Programa 8.2 – Padrão de projeto adaptador de objetos ou object adapter (MICROSOFT COPILOT,
2024d). Revisado e testado pelo autor.
8.1.2 Fachada (ou Facade)
A programação orientada ao objeto é propícia para o desenvolvimento deste padrão
de projeto, sua filosofia é da mesma natureza. Este padrão de projeto se destina a
oferecer código reutilizável, fornecendo uma interface simplificada para um conjunto de
funcionalidades de um sistema ou subsistema mais complexo (OPENAI, 2024e).
Fachada é uma classe configurável, com uma interface de alto nível, que torna o
subsistema mais fácil de usar. Um exemplo do padrão de projeto facade, ou fachada, é
88
a classe JOptionPane do Swing (METSKER, S. J.; WAKE W. C.; 2006, p. 46, tradução
nossa), que mostra mensagens de diversas naturezas em uma forma gráfica (GUI), e
manipula botões JButton, textos JLabel e elementos JDialog, sem que o usuário do
código tenha que manipular diretamente esses elementos.
Abaixo temos um programa com algumas classes, ilustrando operações de criação
de conta, depóstio e transferência. A classe fachada é BancoFacade. Para estudar esse
padrão de projeto, usando sua IDE, e dentro do projeto que você criou para adaptador,
crie um pacote chamado facade. Dentro desse pacote crie uma classe [Link] e digite
o código do programa abaixo. Não esqueça de salvar os arquivos sempre que modificá-
los. Execute o arquivo e veja os resultados.
1 package facade;
2
3 /**
4 *
5 * @author Chat GPT e Ronaldo R. Godoi
6 */
7
8 // Classe que representa uma conta bancária
9 class Conta {
10 private String numero;
11 private double saldo;
12
13 public Conta(String numero, double saldoInicial) {
14 [Link] = numero;
15 [Link] = saldoInicial;
16 }
17
18 public String getNumero() {
19 return numero;
20 }
21
22 public double getSaldo() {
23 return saldo;
24 }
25
26 public void depositar(double valor) {
27 saldo += valor;
28 }
29
30 public void sacar(double valor) throws IllegalArgumentException {
31 if (saldo >= valor) {
32 saldo -= valor;
33 } else {
34 throw new IllegalArgumentException("Saldo insuficiente.");
35 }
36 }
37 }
38
39 // Classe que gerencia transferências
40 class Transferencia {
41 public void transferir(Conta origem, Conta destino, double valor) {
42 [Link](valor);
43 [Link](valor);
44 }
45 }
46
47 // Classe que fornece consulta de saldo
48 class Saldo {
89
49 public double consultar(Conta conta) {
50 return [Link]();
51 }
52 }
53
54 // Classe que gerencia saques
55 class Saque {
56 public void sacar(Conta origem, double valor) {
57 [Link](valor);
58 }
59 }
60
61 // Classe que gerencia depositos
62 class Deposito {
63 public void depositar(Conta origem, double valor) {
64 [Link](valor);
65 }
66 }
67
68 // Classe que gerencia o histórico de transações
69 class Historico {
70 public void registrar(String mensagem) {
71 [Link]("Histórico: " + mensagem);
72 }
73 }
74
75 // Classe Facade que simplifica o uso do sistema bancário
76 class BancoFacade {
77 private Transferencia transferencia;
78 private Saldo saldo;
79 private Saque saque;
80 private Deposito deposito;
81 private Historico historico;
82
83 public BancoFacade() {
84 [Link] = new Transferencia();
85 [Link] = new Deposito();
86 [Link] = new Saque();
87 [Link] = new Saldo();
88 [Link] = new Historico();
89 }
90
91 public void transferir(Conta origem, Conta destino, double valor) {
92 try {
93 [Link](origem, destino, valor);
94 [Link]("Transferência de R$" + valor
95 + " de " + [Link]()
96 + " para " + [Link]());
97 } catch (IllegalArgumentException e) {
98 [Link]("Erro: " + [Link]());
99 }
100 }
101
102 public void sacar(Conta origem, double valor) {
103 try {
104 [Link](origem, valor);
105 [Link]("Saque de R$" + valor
106 + " de " + [Link]());
107 } catch (IllegalArgumentException e) {
108 [Link]("Erro: " + [Link]());
109 }
110 }
111
112 public void depositar(Conta origem, double valor) {
113 try {
114 [Link](origem, valor);
115 [Link]("Deposito de R$" + valor
116 + " de " + [Link]());
117 } catch (IllegalArgumentException e) {
118 [Link]("Erro: " + [Link]());
119 }
90
120 }
121
122 public void consultarSaldo(Conta conta) {
123 double saldoAtual = [Link](conta);
124 [Link]("Saldo da conta " + [Link]() + ": R$" + saldoAtual);
125 }
126 }
127
128 // Exemplo de uso
129 public class Main {
130 public static void main(String[] args) {
131 // Criando contas
132 Conta conta1 = new Conta("123", 1000.0);
133 Conta conta2 = new Conta("456", 500.0);
134
135 // Criando a fachada
136 BancoFacade bancoFacade = new BancoFacade();
137
138 // Consultando saldo inicial
139 [Link](conta1);
140 [Link](conta2);
141
142 // Realizando uma transferência
143 [Link](conta1, conta2, 200.0);
144
145 // Consultando saldo após a transferência
146 [Link](conta1);
147 [Link](conta2);
148
149 [Link](conta1, 150);
150 [Link](conta2, 150);
151
152 [Link](conta1);
153 [Link](conta2);
154 }
155 }
Programa 8.3 – Programa para demonstrar o padrão facade ou fachada (OPENAI, 2024e). Revisado e
testado pelo autor.
Note que existem classes para deposito, saque, transferência, saldo e histórico mas
não há necessidade de instanciar estas classes no programa Main, pois a classe facade
BancoFacade se encarrega de utilizar todas essas classes internamente. Na classe
Main são criadas duas contas e feitas operações de consulta, transferência, etc. sem
que seja necessário instanciar as classes responsáveis por estas operações.
8.1.3 Composto (ou Composite)
O padrão de design Composto (Composite) é utilizado para estruturar objetos em
uma árvore hierárquica, onde os elementos podem ser tratados de forma uniforme,
independentemente de serem compostos ou folhas. Esse padrão é ideal para
representar partes de um sistema que possuem uma estrutura recursiva, ou seja,
onde um componente pode conter outros componentes, e esses, por sua vez,
podem ser compostos por mais componentes. Um exemplo clássico do padrão
composto é o sistema de arquivos, onde tanto arquivos (folhas) quanto pastas
(compostos) podem ser manipulados de maneira semelhante, permitindo adicionar,
remover ou consultar componentes em qualquer nível da estrutura.
91
Em termos práticos, o padrão composto é frequentemente aplicado em sistemas
que envolvem hierarquias, como sistemas de arquivos, interfaces gráficas, ou até
mesmo estruturas organizacionais. Ele permite que objetos complexos sejam
manipulados de maneira simples e consistente, sem se preocupar com o tipo de
objeto que está sendo tratado, o que facilita a manutenção e a expansão desses
sistemas. No exemplo fornecido, o sistema de arquivos simula a relação entre
arquivos e pastas, utilizando recursividade para exibir a estrutura e calcular o
tamanho total, evidenciando a flexibilidade e a utilidade do padrão (OPENAI,
2024e).
Imagine uma situação em que temos dois tipos de estrutura que derivam de uma
classe abstrata, a primeira estrutura será a estrutura final, por exemplo, um arquivo; a
segunda estrutura será uma pasta, que pode conter dentro de si a estrutura final (arquivo)
ou uma nova composição (pastas). Note que pasta pode conter arquivos ou pastas em
seu “interior”, mas arquivos são uma estrutura final (não contém outros arquivos dentro
de si, nem outras pastas).
O padrão composto pode ser útil quando temos uma linha de produção com
máquinas e outras linhas de produção dentro da primeira. Assim, a linha de produção
que está dentro da primeira linha, pode conter também máquinas ou outras linhas de
produção. Podemos fazer uma analogia com uma árvore onde as máquinas são folhas
e as linhas de produção são ramos que podem ter folhas ou outros ramos em seu interior.
Observe o exemplo de código para o padrão composto, abaixo. Neste exemplo
temos arquivos e pastas. Os arquivos são as folhas (uma estrutura final) e as pastas o
composto, são os ramos. A classe abstrata ComponenteSistemaArquivo tem métodos
abstratas que tanto folhas como ramos precisam implementar e, também, tem o método
que retorna o(s) nome(s) de seu(s) conteúdo(s), que funciona tanto para arquivos (folhas)
como para pastas (composto, ou ramos). Nesta estrutura de estudo há o emprego da
recursividade nos métodos exibirEstrutura() e obterTamanho(). O processamento vai
ocorrendo camada por camada da estrutura de dados, tendo como resultado, a exibição
da estrutura e obtendo o valor total do tamanho dos arquivos.
Note que o método obterTamanho() na classe Arquivo retorna o tamanho do
arquivo que é utilizado para totalizar essa quantidade. Quando o método é aplicado a
uma pasta, o método executado é da classe Pasta que soma todos os valores fornecidos
do método obterTamanho () dos arquivos, resultando em um valor total ao fim do
processamento.
92
1 package composite;
2
3 /**
4 *
5 * @author Chat GPT e Ronaldo R. Godoi
6 */
7 import [Link];
8 import [Link];
9
10 // Componente abstrato
11 abstract class ComponenteSistemaArquivos {
12 protected String nome;
13
14 public ComponenteSistemaArquivos(String nome) {
15 [Link] = nome;
16 }
17
18 public abstract int obterTamanho(); // Tamanho em KB
19
20 public abstract void exibirEstrutura(String prefixo);
21
22 public String getNome() {
23 return nome;
24 }
25 }
26
27 // Folha: Arquivo
28 class Arquivo extends ComponenteSistemaArquivos {
29 private int tamanho; // Tamanho em KB
30
31 public Arquivo(String nome, int tamanho) {
32 super(nome);
33 [Link] = tamanho;
34 }
35
36 @Override
37 public int obterTamanho() {
38 return tamanho;
39 }
40
41 @Override
42 public void exibirEstrutura(String prefixo) {
43 [Link](prefixo + "- [Arquivo] " + nome + " (" + tamanho + " KB)");
44 }
45 }
46
47 // Composto: Pasta
48 class Pasta extends ComponenteSistemaArquivos {
49 private List<ComponenteSistemaArquivos> componentes = new ArrayList<>();
50
51 public Pasta(String nome) {
52 super(nome);
53 }
54
55 public void adicionarComponente(ComponenteSistemaArquivos componente) {
56 [Link](componente);
57 }
58
59 public void removerComponente(ComponenteSistemaArquivos componente) {
60 [Link](componente);
61 }
62
63 @Override
64 public int obterTamanho() {
65 return [Link]().mapToInt(ComponenteSistemaArquivos::obterTamanho).sum();
66 }
67
68 @Override
69 public void exibirEstrutura(String prefixo) {
70 [Link](prefixo + "+ [Pasta] " + nome);
93
71 for (ComponenteSistemaArquivos componente : componentes) {
72 [Link](prefixo + " ");
73 }
74 }
75 }
76
77 // Programa principal
78 public class SistemaArquivos {
79 public static void main(String[] args) {
80 // Criando arquivos
81 Arquivo arquivo1 = new Arquivo("[Link]", 50);
82 Arquivo arquivo2 = new Arquivo("[Link]", 300);
83 Arquivo arquivo3 = new Arquivo("video.mp4", 1200);
84 Arquivo arquivo4 = new Arquivo("[Link]", 200);
85 Arquivo arquivo5 = new Arquivo("[Link]", 500);
86 Arquivo arquivo6 = new Arquivo("[Link]", 80); // Novo arquivo
87
88 // Criando pastas
89 Pasta pastaDocumentos = new Pasta("Documentos");
90 Pasta pastaImagens = new Pasta("Imagens");
91 Pasta pastaVideos = new Pasta("Vídeos");
92 Pasta pastaProjetos = new Pasta("Projetos");
93 Pasta subpastaRelatorios = new Pasta("Relatórios");
94 Pasta pastaRaiz = new Pasta("Raiz");
95
96 // Criando a nova pasta com subpasta e arquivo
97 Pasta pastaProjetosComSubpastaEArquivo = new Pasta("Projetos com subpasta");
98 Pasta subpastaArquivos = new Pasta("Arquivos Antigos");
99 // Adicionando arquivo
100 [Link](arquivo6);
101 // Adicionando subpasta
102 [Link](subpastaArquivos);
103
104 // Montando estrutura
105 [Link](arquivo4);
106 [Link](arquivo5);
107
108 [Link](subpastaRelatorios);
109 [Link](arquivo1);
110
111 [Link](arquivo2);
112 [Link](arquivo3);
113
114 [Link](pastaProjetos);
115 [Link](pastaImagens);
116 [Link](pastaVideos);
117 // Adicionando a nova pasta
118 [Link](pastaProjetosComSubpastaEArquivo);
119
120 // Exibindo a estrutura do sistema de arquivos
121 [Link]("Estrutura do Sistema de Arquivos:");
122 [Link]("");
123
124 // Calculando tamanho total
125 [Link]("\nTamanho total do sistema de arquivos: "
126 + [Link]() + " KB");
127 }
128 }
Programa 8.4 – Programa para demonstrar o padrão composto ou composite (OPENAI, 2024e). Revisado
e testado pelo autor.
8.1.4 Ponte (ou Bridge)
O padrão de projeto Ponte (Bridge) é utilizado quando se deseja separar uma
abstração de sua implementação, permitindo que ambas evoluam de forma
94
independente. No cenário de controle de dispositivos, como TV e rádio, esse
padrão proporciona flexibilidade ao permitir que o controle remoto (abstração)
interaja com diversos dispositivos (implementação) sem que a alteração de um
dispositivo afete a abstração. Em ambientes profissionais, o padrão Ponte é
amplamente aplicado em sistemas que necessitam suportar diferentes tipos de
dispositivos ou plataformas, como no controle de diversos tipos de dispositivos
eletrônicos ou na criação de interfaces gráficas com múltiplas plataformas de
renderização. É comum encontrar sua utilização em sistemas que precisam ser
escaláveis e adaptáveis, como em frameworks gráficos, drivers de dispositivos, ou
até mesmo no desenvolvimento de sistemas de UI (Interface de Usuário) onde há
a necessidade de separar a lógica de interface das plataformas de exibição
(Windows, Linux, Web). Esse padrão favorece a extensibilidade, pois novos tipos
de dispositivos podem ser adicionados sem modificar o controle remoto ou outras
partes do sistema, garantindo uma manutenção mais fácil e uma evolução contínua
sem impacto significativo no código existente. Além do padrão Ponte, padrões
como Abstract Factory, Factory Method e Adapter são frequentemente utilizados
para resolver problemas semelhantes de desacoplamento entre componentes,
aumentando a flexibilidade e modularidade dos sistemas (OPENAI, 2024e).
Imagine um programa Java (programa 8.5, abaixo), que controla diversos
dispositivos, como, TV e rádio, permitindo ligar, desligar e ajustar o volume. Para isso,
temos uma interface Dispositivo, implementada pelas classes TV e Radio. A abstração
é representada pela classe ControleRemoto, que define operações básicas como ligar
e desligar. No entanto, para adicionar funcionalidades avançadas, como alterar o volume,
criamos a classe ControleRemotoAvancado, que estende ControleRemoto e
implementa esse método. A classe ControleRemoto contém uma referência ao objeto
Dispositivo, permitindo que diferentes dispositivos sejam controlados de forma
independente. Assim, o padrão bridge separa a abstração (controle remoto) da
implementação (dispositivos), promovendo flexibilidade e extensibilidade (OPENAI,
2024e).
O diagrama UML (Unified Modeling Language), em português: Linguagem de
Modelagem Unificada; que está abaixo, tem a representação de todas as classes de
nosso exemplo. ControleRemoto é uma classe abstrata, TV e Radio são classes que
implementam a interface Dispositivo e ControleRemotoAvancado estende a classe
abstrata ControleRemoto. Classes abstratas não podem ser instanciadas.
95
Figura 8.1 – Diagrama UML do programa que ilustra o padrão de projeto ponte.
Para visualizar a execução do programa abaixo, abra seu IDE e crie no seu projeto
DesignPatternsInJava, o pacote Java chamado bridge, dentro deste pacote crie a classe
Main, no menu de sua IDE e digite o código do programa. Salve o programa e execute.
01 package bridge;
02
03 /**
04 *
05 * @author Chat GPT e Ronaldo R. Godoi
06 */
07
08 // Implementador
09 interface Dispositivo {
10 void ligar();
11 void desligar();
12 void alterarVolume(int volume);
13 }
14
15 // Implementador concreto 1
16 class TV implements Dispositivo {
17 private boolean ligado = false;
18 private int volume = 50;
19
20 @Override
21 public void ligar() {
22 ligado = true;
23 [Link]("TV ligada.");
24 }
25
26 @Override
27 public void desligar() {
28 ligado = false;
96
29 [Link]("TV desligada.");
30 }
31
32 @Override
33 public void alterarVolume(int volume) {
34 [Link] = volume;
35 [Link]("Volume da TV ajustado para: " + [Link]);
36 }
37 }
38
39 // Implementador concreto 2
40 class Radio implements Dispositivo {
41 private boolean ligado = false;
42 private int volume = 30;
43
44 @Override
45 public void ligar() {
46 ligado = true;
47 [Link]("Rádio ligado.");
48 }
49
50 @Override
51 public void desligar() {
52 ligado = false;
53 [Link]("Rádio desligado.");
54 }
55
56 @Override
57 public void alterarVolume(int volume) {
58 [Link] = volume;
59 [Link]("Volume do Rádio ajustado para: " + [Link]);
60 }
61 }
62
63 // Abstração
64 abstract class ControleRemoto {
65 protected Dispositivo dispositivo;
66
67 public ControleRemoto(Dispositivo dispositivo) {
68 [Link] = dispositivo;
69 }
70
71 public abstract void ligar();
72 public abstract void desligar();
73 }
74
75 // Abstração refinada
76 class ControleRemotoAvancado extends ControleRemoto {
77
78 public ControleRemotoAvancado(Dispositivo dispositivo) {
79 super(dispositivo);
80 }
81
82 @Override
83 public void ligar() {
84 [Link]("Usando controle avançado:");
85 [Link]();
86 }
87
88 @Override
89 public void desligar() {
90 [Link]("Usando controle avançado:");
91 [Link]();
92 }
93
94 public void alterarVolume(int volume) {
95 [Link]("Controle avançado ajustando o volume.");
96 [Link](volume);
97 }
98 }
99
97
100 // Classe principal (Demo)
101 public class Main {
102 public static void main(String[] args) {
103 Dispositivo tv = new TV();
104 Dispositivo radio = new Radio();
105
106 ControleRemoto controleTV = new ControleRemotoAvancado(tv);
107 ControleRemoto controleRadio = new ControleRemotoAvancado(radio);
108
109 [Link]();
110 ((ControleRemotoAvancado) controleTV).alterarVolume(70);
111 [Link]();
112
113 [Link]();
114 ((ControleRemotoAvancado) controleRadio).alterarVolume(50);
115 [Link]();
116 }
117 }
Programa 8.5 – Ilustração do padrão de projeto ponte ou bridge (OPENAI, 2024e). Revisado e testado
pelo autor.
8.2 Padrões de responsabilidade (ou responsibility patterns)
8.2.1 Instância Única (ou Singleton)
Esse padrão de projeto declara o construtor da classe que deverá ter apenas uma
instância como privado, dando acesso a ele apenas através de um método dentro da
própria classe através de um método getInstancia(). Outra questão que se apresenta é
a criação de uma variável privada estática para armazenar a instância da classe
singleton. É necessário, ao acessar o getInstancia, que seja testada a condição de
existência anterior da instância com o if(...) questionando se a variável estática que deve
conter a instância tem um conteúdo nulo, caso o valor da variável seja nulo deve ser
chamado o construtor privado para criar a instância junto com a palavra new.
No IDE de sua escolha crie o pacote singleton e dentro dele a classe Java
GerenciadorFilaImpressao, digite o programa que segue. Da mesma forma, crie a classe
Java Principal, no mesmo pacote, e digite o programa adequado que segue abaixo.
Desta vez teremos dois arquivos .java com classes públicas que devem estar no mesmo
pacote. Estude a execução destes programas.
1 package singleton;
2
3 import [Link];
4 import [Link];
5
6 public class GerenciadorFilaImpressao {
98
7 // Instância única do Singleton
8 private static GerenciadorFilaImpressao instanciaUnica;
9
10 // Fila para armazenar os trabalhos de impressão
11 private Queue<String> filaDeImpressao;
12
13 // Construtor privado para evitar instâncias externas
14 private GerenciadorFilaImpressao() {
15 filaDeImpressao = new LinkedList<>();
16 }
17
18 // Método sincronizado para obter a instância única
19 public static synchronized GerenciadorFilaImpressao getInstancia() {
20 if (instanciaUnica == null) {
21 instanciaUnica = new GerenciadorFilaImpressao(); // Cria a instância se não existir
22 }
23 return instanciaUnica;
24 }
25
26 // Método para adicionar um trabalho à fila
27 public void adicionarTrabalho(String trabalho) {
28 [Link](trabalho);
29 [Link]("Trabalho adicionado à fila: " + trabalho);
30 }
31
32 // Método para processar o próximo trabalho da fila
33 public void processarProximoTrabalho() {
34 if ([Link]()) {
35 [Link]("A fila de impressão está vazia.");
36 } else {
37 String trabalho = [Link]();
38 [Link]("Processando trabalho de impressão: " + trabalho);
39 }
40 }
41 }
Programa 8.6 – Classe no padrão de projeto singleton (OPENAI, 2024e). Revisado e testado pelo autor.
1 package singleton;
2
3 public class Principal {
4 public static void main(String[] args) {
5 // Obtendo a instância única do Gerenciador de Fila de Impressão
6 GerenciadorFilaImpressao gerenciador = [Link]();
7
8 // Adicionando trabalhos à fila de impressão
9 [Link]("Documento 1");
10 [Link]("Relatório Anual");
11 [Link]("Planilha Financeira");
12
13 // Processando trabalhos da fila
14 [Link]();
15 [Link]();
16 [Link]();
17 [Link](); // Tentativa de processar com fila está vazia
18 }
19 }
Programa 8.7 – Simulação de inclusão em uma lista de impressão (singleton) (OPENAI, 2024e). Revisado
e testado pelo autor.
Neste exemplo de código, em vez de um arquivo com várias classes utilizamos dois
arquivos .java para conter nossas classes publicas Principal, que testa o singleton; e
GerenciadorFilaImpressao, que é a própria classe no padrão de projeto singleton. Para
que não sejam criadas várias instancias no caso de haver vários acessos concorrentes
99
ao método getInstancia, esse método é declarado sincronizado com a palavra
synchronized em sua definição.
8.2.2 Observador (ou Observer)
O padrão de projeto Observer é amplamente utilizado em situações onde é
necessário manter um conjunto de objetos sincronizados com as mudanças de
estado de um objeto principal. Ele é ideal para cenários onde múltiplos
componentes precisam ser notificados automaticamente quando ocorre uma
atualização, evitando a necessidade de verificações manuais constantes.
No meio profissional, esse padrão é frequentemente aplicado em sistemas de
notificações e assinaturas, como no envio de alertas por e-mail ou push
notifications para usuários cadastrados. Também é útil em sistemas de
monitoramento, onde mudanças em sensores, mercado financeiro ou redes
sociais precisam ser comunicadas em tempo real para diferentes partes
interessadas. Além disso, o Observer é comum em arquiteturas de software
baseadas em eventos, como aplicações que utilizam o padrão Publisher-
Subscriber em microserviços, garantindo comunicação eficiente entre módulos
desacoplados (OPENAI, 2024e).
Imagine que você está fazendo um programa para distribuir notícias a pessoas
inscritas em uma lista. Sempre que uma notícia for publicada, os inscritos receberão uma
mensagem com seu conteúdo.
Para implementar essa funcionalidade de forma clara, existe a interface Sujeito,
que é implementada pela classe AgenciaDeNoticias. Essa classe armazena os
observadores em uma lista (ArrayList<Observador>), representando os assinantes que
receberão as notificações. Os assinantes são instâncias da classe Assinante, que
implementa a interface Observador.
Além de armazenar os observadores, a classe AgenciaDeNoticias controla e
notifica os observadores (Assinante) sempre que há uma nova notícia, garantindo que
todos os assinantes registrados recebam a atualização.
Para demonstrar o funcionamento do padrão observador (ou observer), foram
criados três observadores (João, Maria e Carlos). Apenas João e Maria foram
adicionados à agencia, enquanto Carlos não foi registrado e, portanto não receberá as
notificações. Além disso, no método main, a observadora Maria foi removida da lista de
observadores e, por isso, não recebeu a última notícia.
100
1 package observer;
2
3 import [Link];
4 import [Link];
5
6 // Interface Observador
7 interface Observador {
8 void atualizar(String mensagem);
9 }
10
11 // Interface Sujeito
12 interface Sujeito {
13 void adicionarObservador(Observador observador);
14 void removerObservador(Observador observador);
15 void notificarObservadores(String mensagem);
16 }
17
18 // Implementação do Sujeito (AgenciaDeNoticias)
19 class AgenciaDeNoticias implements Sujeito {
20 private List<Observador> observadores = new ArrayList<>();
21
22 @Override
23 public void adicionarObservador(Observador observador) {
24 [Link](observador);
25 }
26
27 @Override
28 public void removerObservador(Observador observador) {
29 [Link](observador);
30 }
31
32 @Override
33 public void notificarObservadores(String mensagem) {
34 for (Observador observador : observadores) {
35 [Link](mensagem);
36 }
37 }
38
39 // Método para simular uma mudança no estado
40 public void publicarNoticia(String noticia) {
41 [Link]("Publicando notícia: " + noticia);
42 notificarObservadores(noticia);
43 }
44 }
45
46 // Implementação do Observador (Assinante)
47 class Assinante implements Observador {
48 private String nome;
49
50 public Assinante(String nome) {
51 [Link] = nome;
52 }
53
54 @Override
55 public void atualizar(String mensagem) {
56 [Link](nome + " recebeu a notícia: " + mensagem);
57 }
58 }
59
60 // Classe principal para testar o padrão Observer
61 public class ExemploObserver {
62 public static void main(String[] args) {
63 // Criação do Sujeito (agência de notícias)
64 AgenciaDeNoticias agencia = new AgenciaDeNoticias();
65
66 // Criação de Observadores (assinantes)
67 Observador assinante1 = new Assinante("João");
68 Observador assinante2 = new Assinante("Maria");
101
69 Observador assinante3 = new Assinante("Carlos");
70
71 // Registrando os Observadores no Sujeito
72 [Link](assinante1);
73 [Link](assinante2);
74
75 // Publicando notícias
76 [Link]("Nova vacina aprovada!");
77 [Link]("Eleições presidenciais começam amanhã!");
78
79 // Removendo um Observador e publicando mais uma notícia
80 [Link](assinante2);
81 [Link]("Clima chuvoso esperado para o fim de semana.");
82 }
83 }
Programa 8.8 – Exemplo de código para o padrão observador (ou observer) (OPENAI, 2024e). Revisado
e testado pelo autor.
Publicando notícia: Nova vacina aprovada!
João recebeu a notícia: Nova vacina aprovada!
Maria recebeu a notícia: Nova vacina aprovada!
Publicando notícia: Eleições presidenciais começam amanhã!
João recebeu a notícia: Eleições presidenciais começam amanhã!
Maria recebeu a notícia: Eleições presidenciais começam amanhã!
Publicando notícia: Clima chuvoso esperado para o fim de semana.
João recebeu a notícia: Clima chuvoso esperado para o fim de semana.
Saída do programa 8.8 (OPENAI, 2024e).
8.2.3 Mediador (ou Mediator)
O padrão Mediator é útil em situações onde vários objetos precisam se comunicar
entre si de forma organizada e sem dependências diretas. Ele centraliza a
comunicação em um único mediador, reduzindo o acoplamento entre os
componentes e facilitando a manutenção do código.
No ambiente profissional, esse padrão é frequentemente utilizado em sistemas de
reservas e coordenação de recursos compartilhados, como o controle de salas
de reuniões, alocação de equipamentos ou gerenciamento de acessos a recursos
limitados. Além disso, é muito aplicado em interfaces gráficas, onde diversos
componentes (botões, menus, janelas) interagem sem se referenciar diretamente,
e em sistemas distribuídos, onde módulos independentes se comunicam por
meio de um intermediário, como um barramento de mensagens ou um sistema de
eventos (OPENAI, 2024e).
Suponha que haja a necessidade de criar um controle de salas reservadas para
usuários. Usando o padrão mediador, conforme o programa 8.9, temos um projeto mais
sofisticado onde existe uma única instância do objeto mediador que contém uma lista de
usuários e uma lista de reservas de salas. Note que essa instância do objeto mediador
tem referências em todos os usuários registrados, mas é um único objeto na memória. É
um recurso interessante, que permite acessar o mesmo objeto mediador em todos os
102
usuários registrados no mediador. Esses usuários são diversos objeto, enquanto o
mediador é um único objeto para todos os usuários.
No IDE que você está utilizando, com seu projeto aberto, acrescente o pacote
mediator e, neste, crie a classe Java SistemaReservaSalas. Digite as classes que
compõe o programa 8.9 dentro desta classe, que assumirá a extensão .java. Observe
que é delegado responsabilidade para a classe MediadorReservaConcreto, que vai
armazenar os usuários e as salas reservadas dentro de elementos ArrayList. Basta criar
o mediador na classe principal, método main(), incluir os usuários e efetuar as reservas
através do mediador. Note que não podem haver mais de uma reserva para a mesma
sala.
1 package mediator;
2
3 /**
4 *
5 * @author Chat GPT e Ronaldo R. Godoi
6 */
7
8 import [Link];
9 import [Link];
10
11 // Interface Mediador
12 interface MediadorReserva {
13 void registrarUsuario(Usuario usuario);
14 boolean solicitarReserva(String sala, Usuario usuario);
15 }
16
17 // Classe Mediador Concreto
18 class MediadorReservaConcreto implements MediadorReserva {
19 private List<Usuario> usuarios = new ArrayList<>();
20 private List<String> salasReservadas = new ArrayList<>();
21
22 @Override
23 public void registrarUsuario(Usuario usuario) {
24 [Link](usuario);
25 [Link](this);
26 }
27
28 @Override
29 public boolean solicitarReserva(String sala, Usuario usuario) {
30 if ([Link](sala)) {
31 [Link]("Sala " + sala + " já está reservada.");
32 return false;
33 }
34 [Link](sala);
35 [Link]("Usuário " + [Link]() + " reservou a sala " + sala + ".");
36 return true;
37 }
38 }
39
40 // Classe Colega
41 class Usuario {
42 private String nome;
43 private MediadorReserva mediador;
44
45 public Usuario(String nome) {
103
46 [Link] = nome;
47 }
48
49 public void setMediador(MediadorReserva mediador) {
50 [Link] = mediador;
51 }
52
53 public String getNome() {
54 return nome;
55 }
56
57 public void reservarSala(String sala) {
58 if (mediador != null) {
59 [Link](sala, this);
60 } else {
61 [Link]("Mediador não configurado para o usuário " + nome + ".");
62 }
63 }
64 }
65
66 // Classe Principal
67 public class SistemaReservaSalas {
68 public static void main(String[] args) {
69 MediadorReserva mediador = new MediadorReservaConcreto();
70
71 Usuario usuario1 = new Usuario("Alice");
72 Usuario usuario2 = new Usuario("Bob");
73
74 [Link](usuario1);
75 [Link](usuario2);
76
77 [Link]("Sala A");
78 [Link]("Sala A"); // Tentativa de reserva da mesma sala
79 [Link]("Sala B");
80 }
81 }
Programa 8.9 – Ilustração do padrão de projeto mediador (ou mediator) (OPENAI, 2024e). Revisado e
testado pelo autor.
8.2.4 Procurador (ou Proxy)
8.2.5 Cadeia de Responsabilidade (ou Chain of Responsibility)
8.2.6 Peso-Mosca
8.3 Padrões de Construção (ou construction)
8.3.1 Construtor
8.3.2 Método Fábrica
8.3.3 Fábrica Abstrata
8.3.4 Protótipo
8.3.5 Registro (ou Memória)
104
8.4 Padrões de Operação
8.4.1 Método Modelo
8.4.2 Estado
8.4.3 Estratégia
8.4.4 Comando
8.4.5 Interpretador
8.5 Padrões de Extensão
8.5.1 Decorador
8.5.2 Iterador
8.5.3 Visitante
105
106
9 CONCLUSÃO
Uma introdução na filosofia, elementos, classes, métodos, sintaxes foi apresentada
nos capítulos iniciais desta obra. Outras funcionalidades da linguagem, como streams,
threads, collections e expressões lambda seguiram os primeiros passos e tiveram
espaço importante neste trabalho. Os tópicos principais de um sistema – classes e
métodos codificados – de controle de estoque desenvolvido em linguagem Java foram
implementados com conceitos de POO (programação orientada ao objeto), integração
com um sistema gerenciador de bancos de dados e conhecimentos da biblioteca da
interface gráfica de usuário Swing, que é uma parte da API (Application Programming
Interface) gráfica do Java. Nos dois apêndices, temos conceitos de JavaFX e Servlets.
Os próximos trabalhos poderiam envolver novos elementos como o mundo IOT
(Internet Of Things) com a integração de dispositivos aos sistemas por meio de Java;
sistemas em redes desenvolvidos em Java; e os novos aspectos acrescentados pela
inteligência artificial ao mundo da programação em Java. Todos esses conceitos já fazem
parte da realidade de hoje, afinal, vivemos a era 4.0.
Perceba que devemos considerar que já estamos vivendo num mundo em que a
inteligência artificial (IA), uma tecnologia com muitas facilidades, está modificando o
paradigma da programação. Um exemplo da IA batendo a nossa porta, é a produção de
código Java artificialmente que se encontra em muitos dos programas de exemplo que
ilustram este trabalho. A maioria deles tem influência de IA ou foram desenvolvidos
totalmente com essas entidades que estão disponíveis na internet gratuitamente e,
quando pagas, apresentam ainda mais poder produtivo.
Muitas classes, muitos métodos e muitos detalhes da linguagem Java não foram
alcançados por este trabalho. E a importância destes conteúdos não é desprezável, mas
temos aqui um bom começo de tudo. O passo inicial já foi dado e o caminho a ser
percorrido pela experiência é o desenvolvimento de muitos outros projetos, o que vai
agregar os conhecimentos das particularidades desta linguagem que não estão conosco
nesse momento. Elementos de sistemas em redes, e implementações de sistemas
voltados para internet precisam ser produzidos. A necessidade de se confrontar com
minúcias e detalhes que não estão presentes nesta obra aparecem nos contornos das
107
sombra deste trabalho. O caminho está aberto e o programador deve apresentar vontade
de estudar e aprender mais em todos os momentos de sua carreira, para poder atingir
um corpo de conhecimento completo da linguagem Java.
108
REFERÊNCIAS
Obras que serviram de referência
BRANCO, D. C. Java | O que o cafezinho tem a ver com a origem da linguagem de
programação? 2022. Disponível em: <[Link]/software/java-o-que-o-
cafezinho-tem-a-ver-com-a-origem-da-linguagem-de-programacao-207312>. Acesso
em: 25/02/2024.
CHIN, S.; VOS, J.; WEAVER, J. The Definitive Guide to Modern Java Clients with
JavaFX: Cross-Platform Mobile and Cloud Development. New York: Apress. 2019.
Digital Innovation One (DIO). Desenvolvimento Java com IA.
<[Link] Acesso em 04/07/2024.
FACULESTE. Acessando banco de dados com Java. Coronel Fabriciano: Faculeste.
FACULESTE. Desenvolvimento de sistemas com Java. Coronel Fabriciano:
Faculeste.
FACULESTE. Introdução a programação WEB. Coronel Fabriciano: Faculeste.
FINEGAN, E.; LIGUORI, R. OCA Java SE 8: guia de estudos para o Exame IZO-808.
Porto Alegre: Bookman, 2018.
FURGERI, S. Java Ensino Didático – Desenvolvimento e Implementação de
Aplicações. São Paulo: Érica, 2018.
GODOI, R. Tópicos avançados de Ciência e Tecnologia aplicados ao Ensino Médio:
Física dos componentes dos computadores atuais e tendências futuras. 2004. 98 f.
Monografia (Especialização Lato Sensu) – Universidade Estadual de Londrina,
Londrina, 2004.
GOSLING, J. et al. The Java Language Specification Java SE 14 Edition. 2020.
Disponível em: <[Link]
Acesso em: 06/11/2023.
JANDL Junior, P. Java – Guia do Programador – Atualizado para Java 16. 4. ed. São
Paulo: Novatec Editora Ltda., 2021.
MICROSOFT COPILOT (2024a). Lista com todas as anotações do pacote [Link].
2024. Disponivel em: <[Link] Acesso em: 02/03/2024.
MICROSOFT COPILOT (2024b). Implementação de listener em Java e código mais
limpo e fácil de entender. 2024. Disponivel em: <[Link]
Acesso em: 14/05/2024.
MICROSOFT COPILOT (2024c). Pergunta sobre herança, classe pai ou super classe
e a anotação @Override em métodos sobrescritos. Disponível em:
<[Link] Acesso em 18/05/2024.
MICROSOFT COPILOT (2024d). Perguntas diversas. 2024. Disponível em:
<[Link] >. Acesso em 24/05/2024.
109
OPENAI (2024a). Resposta gerada por assistente de inteligência artificial sobre
containers disponíveis no Java Swing. 2024. Disponível em:
<[Link]/c/9be2bdcd-b423-4399-ada2-5d86ee9de673>. Acesso em:
15/05/2024.
OPENAI (2024b). Resposta gerada por assistente de inteligência artificial sobre
botões Swing e desenvolvimento de programa de exemplo e expressão lambda. 2024.
Disponível em: <[Link]
Acesso em: 18/05/2024.
OPENAI (2024c). Programação orientada ao objeto: Encapsulamento. 2024.
Disponível em: <[Link]>. Acesso em: 23/05/2024.
OPENAI (2024d). No contexto de programação POO para Java, que apresenta as
quatro características típicas: herança, polimorfismo, encapsulamento e abstração;
fale um pouco sobre abstração e exemplifique com código. 2024. Disponível em:
<[Link] Acesso em
24/05/2024.
OPENAI (2024e). Perguntas diversificadas sobre programação com Java. 2024.
Disponível em: <[Link]>. Acesso em 25/05/2024-em aberto.
SCHILDT, H. Java para Iniciantes. 6. ed. Porto Alegre: Bookman, 2015.
SCHILDT, H.; COWARD, D. Java: The Complete Reference, Thirteenth Edition.13. ed.
New York: Mc Graw Hill. 2024.
SILVA, E. J.; SIDNEI, B. C.; COLARULHO, M. A. Introdução. Informática e seu
Raciocínio Lógico. LTC – Livros Técnicos e Científicos: 1988.
WIKIEPÉDIA. Java (linguagem de programação). Disponível em:
<[Link] 2024. Acesso em:
01/11/2023.
METSKER, S. J.; WAKE, W. C. Design Patterns in Java. 2. Ed. Boston: Addison-
Wesley. 2006.
110
APÊNDICE A – JAVA FX
A.1 O que é Java FX
A.2 J ajlkdfjlasdjflajsdflkasdjflk asdfklj sdfj asdljk
A.3 J wir wioeoijsdlk l jasdf kjsladjlk
A.4 J woie lskdflkj lk xcmvn mn
A.5 S aklsfj laslkdfjlsjd oiwei
111
APÊNDICE B – SERVLET
B.1 O que é Servlet
B.2 Desenvolvimento de páginas para internet
B.3 ...
112
113
INDICE REMISSIVO