Uploaded by erik marcondes

Alex Coelho - Java POO

advertisement
JAVA com Orientação a Objetos
©
!"#$% #!&'&#!!(% ) * + % % , , -% .% / 0 % 1 + 23% % 4 14 , 5 , 4 5 )
6 5 ) 5* , / ,
7 23% % + / % 8 / 7 % 23 / + 239 , ) 2 0 * : !"#$
JAVA com Orientação a Objetos
6 ; : %'$#'
#Informática, 2. Programação de Computador – Programas e Dados
< =>;!?(@(A@B!!@$'36@1
$01.642
$$A
% !&
& ' () * + ,' , * - ./&01/231/
4.35 ../32))).6 7 4.35 ../32)80)
2! !9!&"&-
:::&!&"&-
/363.
Agradecimento
Inicialmente agradeço a Deus por me dar forças e condições de realizar este
trabalho. Agradeço ainda a meus amigos, todos esses anos trabalhando com
vocês só me fizeram uma pessoa melhor. Em especial, a minha família, a minha querida esposa Fernanda, a meu pai Alexandre, a minha mãe Leonidia, a
meus irmãos e a meus queridos sobrinhos. Obrigado a todos pelo apoio tão
importante que me fornecem.
Introdução
Caro leitor, é um prazer apresentar para vocês a tecnologia Java e os
conceitos vinculados à programação orientada a objetos. A ideia central deste
livro é garantir aos estudantes da área de Ciência da Computação e afins uma
forma alternativa de conhecer essa tecnologia e o paradigma que atualmente
dominam parte do mercado. O livro é resultado de anos de experiência trabalhando com disciplinas ligadas à área de programação, no qual foram aplicados conhecimentos da tecnologia Java, assim como a prestação de serviços
que envolviam tais elementos. Ao final da leitura deste livro, espera-se que o
leitor tenha conhecimento suficiente para desenvolver e entender as aplicações que utilizam as diversas características que norteiam a tecnologia Java.
Nos últimos anos, a programação Java tornou-se uma das mais populares do mercado e tem feito com que os profissionais, que dominam seus
preceitos, passem a ser mais valorizados e até mesmo disputados por empresas, considerando a carência existente para tais nichos de mercado. Tal fatia
de mercado, dominada pela tecnologia Java, deve-se em sua maioria às suas
potencialidades, uma vez que são aplicações que podem estar presentes em
desde eletrodomésticos, aparelhos celulares, carros até as mais simples, tais
como as existentes na Web. Assim, a linguagem Java tem características que
a tornam diferenciada, sendo de grande importância no que tange a criação
de software.
Diante disso, foi montado um roteiro neste livro, no qual se priorizou garantir ao leitor uma visão ampla de todos os conceitos importantes
para sua iniciação na programação com a tecnologia Java. No Capítulo 1, são
apresentados os fundamentos da programação com a linguagem Java, considerando elementos importantes, como, por exemplo, as palavras reservadas,
estrutura básica de um programa Java e as vantagens e desvantagens de utilizar a tecnologia mantida atualmente pela Oracle.
No segundo capítulo, começaremos a ver os fundamentos do paradigma de programação orientada a objetos, considerando passo a passo cada
conceito, fazendo com que o leitor entenda os aspectos básicos e necessários
para a continuidade no aprendizado, como, por exemplo, a ideia de classe
e objetos na tecnologia Java. Continuando, no Capítulo 3, são trabalhadas
VI t JAVA com Orientação a Objetos
as estruturas mais avançadas da programação orientada a objetos, fazendo
com que o leitor consiga dar mais um passo no aprendizado do paradigma e
da tecnologia Java, sendo considerados conceitos, tais como construtores,
destrutores e outros.
Assim como no Capítulo 3, no Capítulo 4 continuaremos a trabalhar
com os elementos mais avançados da programação orientada a objetos, tais
como os importantíssimos conceitos de herança e polimorfismo, que devem
ser vistos e tratados como elementos-chave da tecnologia Java e do paradigma de programação orientada a objetos. Claro que devemos lembrar sempre
do conceito de portabilidade oriundo da estruturação muito bem pensada da
tecnologia Java como um importante diferencial.
Em nosso quinto capítulo, será apresentado a você, leitor, outros elementos da tecnologia Java, no qual iremos lidar com ferramentas úteis para
o dia a dia de um programador. Trabalharemos nesse capítulo com bibliotecas matemáticas, números randômicos ou ainda veremos como lidar com datas, manipular strings e assim por diante. Logo, serão consideradas diversas
possibilidades e vantagens que podem ser exploradas, o que com certeza irá
auxiliar os programadores a dominarem tais conceitos.
O Capítulo 6 tem por objetivo apresentar o trabalho de tratamento de
exceções, sendo de extrema importância seu entendimento, já que se trata
de um elemento central nas atividades que exigem a manipulação de dados,
seja em arquivos texto, seja mesmo em um banco de dados. Este tema é de
grande importância, pois a tecnologia Java possibilita, em sua programação,
que sejam tratados eventuais problemas ou erros antes que estes venham a
realmente ocorrer, permitindo que sejam tomadas as devidas providências
para uma autorrecuperação do software durante a execução. Podemos enxergar isso como uma prevenção para os possíveis erros nos trechos mais
suscetíveis a problemas.
Finalizando, em nosso sétimo capítulo, começaremos a trabalhar com
a manipulação de arquivos, lidando com o processo de criação, persistência
de dados e leitura de dados utilizando classes e interfaces disponibilizadas
pela tecnologia Java.
Amigo leitor, em todo o livro tentarei manter uma linguagem mais
simples, clara e cotidiana, na qual o principal objetivo e descomplicar o entendimento dos diversos conteúdos mencionados. Recomendo que sejam
feitos os exemplos disponibilizados, que foram pensados considerando os
aspectos que devem acompanhá-lo em sua jornada. Logo, vamos ao que interessa e tenha uma ótima leitura.
Sumário
Capítulo 1
Conceitos básicos da tecnologia Java..........................1
1.1 Fundamentos da tecnologia Java ....................................................1
1.2 Estrutura e conceitos básicos da programação Java .......................5
1.3 Entrada de dados ........................................................................... 13
1.4 Estruturas de controle................................................................... 15
1.5 Arrays e matrizes ........................................................................... 21
1.6 Funções. ......................................................................................... 25
Capítulo 2
Programação orientada a objetos.............................29
2.1 Conceitos da programação orientada a objetos ........................... 30
2.1.1 Classe .......................................................................................... 32
2.1.1.1 Qualificadores de acesso ......................................................... 33
2.1.1.2 Pacotes ..................................................................................... 35
2.1.1.3 Import...................................................................................... 36
2.1.1.4 Comentários ............................................................................ 37
2.1.2 Atributos ..................................................................................... 38
2.1.3 Métodos ...................................................................................... 39
2.1.3.1 Identificador this ..................................................................... 40
2.1.4 Mensagem e modularização de código ...................................... 41
2.1.5 Objeto ou instanciação............................................................... 44
Capítulo 3
Construtores, destrutores e encapsulamento............51
3.1 Construtores .................................................................................. 51
3.2 Destrutores e Garbage Collector (Coletor de Lixo) ...................... 54
3.2.1 Garbage Collector ....................................................................... 55
3.3 Encapsulamento ............................................................................ 56
VIII t JAVA com Orientação a Objetos
Capítulo 4
Herança, polimorfismo e interface...........................63
4.1 Herança .......................................................................................... 63
4.2 Polimorfismo ................................................................................. 69
4.2.1 Sobrecarga .................................................................................. 70
4.2.2 Cast ............................................................................................. 71
4.2.3 instanceof ................................................................................... 73
4.2.4 Sobrecarga de construtores ....................................................... 74
4.2.5 Redefinição ................................................................................. 74
4.3 Interface ......................................................................................... 76
4.3.1 Herança múltipla ........................................................................ 83
Capítulo 5
Ferramentas úteis para o dia a dia............................85
5.1 Manipulação de strings ................................................................. 85
5.1.1 Construtores da classe String .................................................... 86
5.1.2 Outros métodos.......................................................................... 86
5.2 Data e hora ..................................................................................... 88
5.3 Operações matemáticas ................................................................ 93
5.4 Trabalho com vetores especiais .................................................... 95
5.4.1 Classe Vector............................................................................... 96
5.4.2 Classe HashMap ......................................................................... 98
5.4.3 Classe Arrays .............................................................................. 99
Capítulo 6
Tratamento de exceções e entrada de dados............101
6.1 Tratadores de exceções ................................................................ 102
6.1.1 Throws....................................................................................... 106
6.2 Entrada de dados ......................................................................... 107
SumáriotIX
Capítulo 7
Manipulação de arquivos de texto ..........................111
7.1 Arquivos ....................................................................................... 111
7.2 Entradas ....................................................................................... 112
7.3 Saídas ........................................................................................... 114
7.4 Streams ........................................................................................ 116
7.5 Acessos aleatórios em arquivos .................................................. 118
Referências Bibliográficas................121
Apêndice I
Instalação do Sdk e Configuração das Variáveis de Ambiente
(Windows Xp) .................................................................................... 123
Apêndice II
JAVADOC ........................................................................................... 129
Capítulo 1
Conceitos básicos da tecnologia Java
Quando falamos em programação, não há como deixar de vislumbrar
tal atividade como sendo um elemento central na vida de um profissional
ligado à área de Ciência da Computação e afins. No cotidiano, o mínimo que
se espera de um profissional de TI é que este domine os elementos e os conceitos básicos, aplicados a uma determinada linguagem de programação.
O mercado atualmente oferta uma infinidade de possibilidades
de linguagens de programação para a construção de softwares, indo das
mais complexas e trabalhosas até as mais simples. Existem, entre essas
linguagens de programação, várias que são bem aceitas no mercado, que
consideram obviamente variáveis estratégicas, tais como preço, velocidade
da curva de aprendizagem e desenvolvimento. Entre tais, a linguagem e a
plataforma de desenvolvimento de aplicações Java vêm no decorrer dos anos
tornado-se uma das mais utilizadas e aceitas no mercado. Isso, graças ao seu
potencial tecnológico combinado ao paradigma de orientações a objetos,
além de considerar os custos envolvidos.
O Java é uma tecnologia que foi originalmente desenvolvida pela Sun
Microsystem em meados de 1995, sendo regida pela GPL (General Public
License). A Oracle Corporation adquiriu todos os direitos pela Sun e obviamente, também sobre a tecnologia Java. Para a maioria dos autores e desenvolvedores, a tecnologia Java pode ser definida como:
“simples, distribuída, interpretada, robusta, segura, de arquitetura
neutra, portátil, multifunções e dinâmica“.
Ao considerar tais perspectivas, o objetivo deste capítulo é apresentar
e preparar o leitor para compreender e aplicar os fundamentos básicos da tecnologia Java. Estes são úteis e de extrema importância para a compreensão
dos demais conceitos vinculados à plataforma de desenvolvimento mantida
pela Oracle e que serão considerados nos próximos capítulos de nosso livro.
1.1 Fundamentos da tecnologia Java
A maioria dos autores restringe-se a citar que o Java consiste em uma
2 t JAVA com Orientação a Objetos
linguagem de programação orientada a objetos baseada na sintaxe da linguagem de programação C. Porém, podemos ir além disso ao citar que o Java é
uma plataforma tecnológica que possui diversas características que o mercado considera como importante e que devido a isto, tem garantido uma fatia
considerável das aplicações desenvolvidas com essa tecnologia. Com certeza,
pode-se afirmar que a característica que mais se destaca neste processo e a
torna diferenciada diante das demais linguagens consiste na portabilidade
dos sistemas desenvolvidos com o Java.
Entende-se portabilidade como, nada mais nada menos, a independência de plataforma - no caso, o sistema operacional (Windows, Linux,
Solaris, MAC OS) e o hardware, no qual os softwares serão executados. Tal
característica nasce do fato da estrutura das aplicações desenvolvidas com a
tecnologia Java ser inicialmente compilada e depois, interpretada. Indo um
pouco mais além no entendimento, o ponto central desse processo esta na
geração do bytecode, também conhecido como arquivo com extensão “.class”.
Este é obtido ou gerado por meio da compilação do código-fonte dos arquivos “java puros” ou de extensão “.java”, que serão interpretados futuramente. É importante mencionar que com isso, graças ao fato da tecnologia Java
possuir para cada plataforma (sistema operacional + hardware) uma Máquina Virtual Java, também conhecida como Java Virtual Machine (JVM), este
processo torna todas as aplicações Java portáveis entre si. Assim, quem irá
preocupar-se com as especificidades de cada plataforma não é mais o programador, mas sim a JVM fornecida.
Essa máquina virtual é capaz de interpretar os arquivos compilados
(“.class”), iniciando a execução do software, independentemente da plataforma na qual o programa foi construído. Por exemplo, uma vez compilado nosso programa utilizando o sistema operacional Windows em uma estrutura de
hardware da família x86, caso queiramos que nosso programa rode em uma
máquina com o sistema operacional Linux, não será necessário realizar uma
nova compilação para este software, como acontece com as outras tecnologias e linguagens de programação. Neste contexto, será apenas necessária a
presença da JVM para o Linux instalado, o que hoje é algo natural nas máquinas comercializadas.
Como o leitor pode reparar, esta característica ímpar só é possível graças à existência da JVM, que é fornecida pela tecnologia Java e na qual cada
sistema operacional possui uma específica, distribuída pela mantenedora da
tecnologia. A JVM pode ser vista como sendo uma camada intermediária
Capítulo 1 - Conceitos básicos da tecnologia Javat3
entre sua aplicação e a plataforma que executará seu programa. A maioria
dos computadores atuais já é vendida com algum tipo de Máquina Virtual Java e isso tem garantido uma popularização ainda maior das aplicações
Java, o que nos faz crer que, em sua maioria, qualquer aplicação Java funcionará corretamente nos computadores obtidos atualmente no mercado. Para
uma melhor visualização do processo citado como portabilidade, a Figura
1 demonstra o modelo computacional definido pela tecnologia Java e suas
aplicações. Vamos conferir?
Figura 1: Estrutura de compilação e execução de programas no Java.
Como definição, podemos mencionar que um programa Java consiste em um conjunto de instruções, que a JVM é responsável por interpretar
e dessa forma, garantir a independência de plataforma na qual o programa
foi construído e será executado. Isto é, basta que haja uma implementação
de uma máquina virtual para a plataforma ser utilizada. O termo plataforma, como já vimos, normalmente no meio computacional é definido como a
combinação de sistema operacional mais hardware. Porém, para a plataforma
Java, ela é definida somente com o software, por isso o conceito de máquina
virtual.
A plataforma Java possui dois componentes que precisamos conhecer
quando a adquirimos, sendo:
4 t JAVA com Orientação a Objetos
Máquina Virtual Java (JVM): A máquina imaginária que é implementada por meio da emulação em um software executado em uma máquina real
e que fornece as especificações da plataforma de hardware para a qual todo o
código Java está compilado. A JVM faz parte do ambiente de execução Java,
mais popularmente conhecido como JRE (Java Run-Time Environment);
Interface para Desenvolvimento de Aplicações (API Java): Trata-se
das interfaces de desenvolvimento para a utilização na criação de aplicações
Java, no caso são as bibliotecas para a criação de programas que a própria linguagem Java fornece, disponibilizados por meio do JDK (Java Development
Kit) ou como vem sendo disponibilizado nos últimos anos como SDK (Source
Development Kit).
Para o desenvolvimento de aplicações Java, é necessário que se tenha
instalado no computador o SDK. Desta forma, obtenha uma cópia na página
oficial da Oracle e instale em seu computador.
O Apêndice deste livro contém mais informações sobre
o processo de instalação do pacote SDK, assim como a configuração das variáveis de ambiente necessárias para a correta
utilização do conjunto de ferramentas da tecnologia Java.
Após a correta instalação e configuração das variáveis de ambiente
do SDK, podemos usar um editor de texto qualquer para a construção do
código de nosso programa Java. Porém, existem diversos ambientes de desenvolvimento, também conhecidos como IDE (Integrated Development
Environment), para a programação Java, sendo que os mais utilizados no
mercado são: Eclipse, NetBeans e JBuilder.
Como nossa intenção consiste no aprendizado da linguagem Java, não
nos apegaremos a nenhuma IDE, o que deixa você, leitor, livre para a utilização ou não de uma dessas ferramentas de desenvolvimento, sendo que
apenas um editor de texto será suficiente para concluir o aprendizado deste
livro.
Um aspecto importante a ser citado aqui é que a linguagem Java possui diferentes tipos de programas, sendo que a classificação é feita por meio
da modalidade X localização de execução dos mesmos. Assim, esses programas podem ser definidos como: applications, applets e servlets. Nesta unidade, somente trabalharemos com as applications no console, ou seja, prompt de
comando, seja Windows, seja Linux.
Capítulo 1 - Conceitos básicos da tecnologia Javat5
1.2 Estrutura e conceitos básicos da programação Java
Agora, vamos ao que interessa. Nesta seção do livro, vamos aprender
como são construídas as estruturas básicas para os programas escritos na
linguagem Java, além de conhecer os conceitos mais básicos da programação
com a tecnologia. O primeiro detalhe que deve ser considerado na linguagem
consiste no fato de que todo programa Java é uma classe, independentemente das três possíveis modalidades dos programas Java citadas anteriormente
(Applications, Applets e Servlets). A estrutura básica é sempre a mesma: “uma
classe”. Um programa Java é organizado em arquivos de texto com a extensão “.java” e cada arquivo pode conter várias classes, mas usualmente teremos apenas uma. Aqui, temos uma consideração importante e que você não
deve esquecer jamais:
O nome do arquivo da classe java deve possuir exatamente o mesmo nome que foi dado internamente à classe,
pois a máquina virtual procura a classe por meio do nome do
arquivo.
Uma metodologia que pode auxiliá-lo a entender a tecnologia Java
neste momento consiste na utilização de analogias com outras linguagens
de programação, das quais você já tenha conhecimento e domínio. Então,
vamos a elas! Se fôssemos realizar uma comparação básica, poderíamos dizer
que as classes no Java podem ser comparadas às famosas units do Pascal. Já
para os adeptos do C e C++, a estrutura de nossos programas praticamente
será a mesma, uma vez que a tecnologia Java está baseada em tais linguagens. Ainda, veremos mais à frente em nossos capítulos que os atributos no
Java são como as variáveis existentes em Pascal e C, e os métodos das classes
Java seriam os procedimentos e as funções da linguagem C e Pascal. Vários
destes aspectos citados serão considerados quando estivermos tratando do
conceito de orientação a objetos.
Outro detalhe a ser considerado é a definição de blocos de código que,
no Java, são definidos com a utilização de “{“ e “}”, assemelhando-se assim
ao C ou C++, diferentemente dos elementos em Pascal, que utilizam no caso,
“BEGIN” e “END”. Por enquanto, preocupe-se apenas em entender como
os blocos são definidos, sendo de extrema importância para começarmos a
6 t JAVA com Orientação a Objetos
entender os conceitos fundamentais para a construção de programas simples
com a tecnologia Java.
Então, diante disso, vamos ao que interessa. Chegou a hora de desenvolvermos nosso primeiro programa em Java. Transcreva o conteúdo abaixo
para um editor de texto simples, tal como o ‘notepad’ do Windows ou mesmo
o ‘Vi’ do Linux. Por enquanto, siga o exemplo conforme é apresentado, digitando como pode ser observado na primeira coluna, visto que a linguagem
Java é case sensitive, ou seja, existem diferenças entre as letras maiúsculas e
minúsculas em sua compilação e interpretação. A seguir, faremos uma análise do que foi feito em nosso primeiro programa.
//Classe PrimeiroPrograma.java
class PrimeiroPrograma{
public static void main (String arg []){
System.out.println(“Olá leitor”);
}
}
Agora que você já construiu seu primeiro programa em Java e considerando o que já foi citado anteriormente neste capítulo, salve seu programa
lembrando que o nome do arquivo Java deve ser necessariamente igual ao
nome de declaração da classe - no caso específico, o arquivo deve ser salvo
como “PrimeiroPrograma.java”. Salve o arquivo tomando o cuidado necessário para assegurar que a extensão “.java” seja garantida por seu editor de
textos, uma vez que eles podem manter a extensão “.txt” e isso impedirá a
compilação e a interpretação de seu programa.
Feito isso, vamos à explicação de nosso programa, entendendo o código apresentado. Para que você tenha um programa executável construído
com a linguagem Java, sua classe deve sempre possuir um método específico, que caracteriza a classe como uma aplicação que a Máquina Virtual Java
deverá interpretar no momento da execução do programa. Todo programa/
classe Java que possuir a função executável ou, como veremos nos capítulos
seguintes, o método executável com a assinatura “public static void main
(String[] args){ }” apresentada na linha 3 de nosso código anterior, será
considerado um programa executável pela JVM.
Capítulo 1 - Conceitos básicos da tecnologia Javat7
Logo, este trecho será executado automaticamente quando for encontrado no programa submetido à maquina virtual. Um detalhe importante
que deve ser comentado nesse trecho trata-se do vetor (array) “String[]
args”. Esse trecho presente na assinatura de nossa função executável pode
conter parâmetros, que foram passados pela linha de comando na execução
de nosso programa. Demonstraremos isso a seguir.
Outro detalhe a ser destacado e que você deve reparar no código apresentado, está no fato de que, por convenção, ou seja, algo aceito por todos
os programadores Java, os nomes das classes no Java são sempre iniciados
com letra maiúscula e, caso venham a ser compostos, cada nova palavra será
iniciada novamente com uma letra maiúscula, definindo seu início. Note que
isso é feito em nosso código anterior e, diante disso, passe a adotar em seus
programas Java. Na quarta linha de nosso primeiro programa, é apresentada
a linha System.out.println(“Olá leitor”);, que nada mais faz do que realizar a impressão em console. Esse comando informa ao sistema (System) que
será realizada uma operação de saída (out), sendo que tal operação consiste
em uma impressão com quebra de linha (println). Existem outras possibilidades de realizar a impressão em console Java e estaremos lidando com elas
no momento certo. Então, memorize a linha demonstrada, pois sempre iremos utilizar essa linha de código quando quisermos realizar a impressão em
console em nossos próximos programas.
Certo pessoal, até aqui conseguimos vencer uma parte importante de
nosso primeiro capítulo, considerando que os aspectos citados são comuns a
qualquer programa Java que você venha a construir de agora em diante. Mas,
está faltando visualizarmos o resultado de nosso primeiro programa. Para
isso. teremos de compilar e interpretar nosso programa e, logo, necessitamos
de outras ferramentas.
Existem dois comandos básicos que a tecnologia Java fornece por
meio do JDK para tais funções, sendo: javac e java, respectivamente. Tais
comandos só funcionarão em seu prompt de comando, como, por exemplo, o
DOS do Windows, depois de definidas as variáveis de ambiente, para que os
comandos estejam disponíveis em qualquer caminho de seu sistema operacional, sendo:
t
JAVA_HOME: Deve conter o caminho da pasta, na qual foi instalado o SDK, ou seja, o local onde possui todas as ferramentas e APIs
básicas fornecidas pela tecnologia Java em seu computador;
8 t JAVA com Orientação a Objetos
t
CLASSPATH: Deve conter o caminho no qual estão localizadas as
bibliotecas dos programas que você está utilizando. Por padrão,
o valor desta variável é apenas “.”, que significa que as bibliotecas utilizadas estão na mesma pasta da aplicação que você está
desenvolvendo. Caso seja necessário, incremente a essa variável
os caminhos nos quais existam outras APIs importantes para o
desenvolvimento de sua aplicação, que podem ter sido adquiridas
na Internet, por exemplo.
Para que você obtenha maiores informações sobre a criação de variáveis de ambiente, disponibilizamos, ao final deste livro, no ANEXO I, um
tutorial sobre o assunto. Além disso, podem ser encontrados na Internet diversos tutoriais sobre o assunto, sendo que sempre devemos levar em consideração o sistema operacional a ser utilizado no momento do desenvolvimento e da configuração.
Uma vez que o JDK está instalado corretamente e foram definidas
as variáveis de ambiente, estamos prontos para compilar e executar nosso
primeiro programa. Assim, digite os comandos apresentados abaixo em um
prompt de comando do seu sistema operacional, considerando que no prompt,
devemos estar no diretório no qual são mantidas nossas classes. Em nosso
exemplo, a classe PrimeiroPrograma.java está no diretório c:/>programas.
javac PrimeiroPrograma.java
java PrimeiroPrograma
O primeiro comando apresentado consiste no javac que irá realizar a
compilação de sua classe. Já o segundo comando apresentado, java, irá realizar a interpretação do bytecode gerado por meio da compilação, conforme a
estrutura apresentada na Figura 1.
Certo! Se tudo ocorreu conforme o esperado, será impresso o texto
“Olá leitor”, como é apresentado na imagem a seguir.
Capítulo 1 - Conceitos básicos da tecnologia Javat9
Caso o leitor tenha dúvidas sobre como realizar a navegação nos diretórios de seu computador, indicamos a leitura de qualquer tutorial que
auxilie na manipulação de comandos na linha de comando de seu sistema
operacional.
Mas, outro ponto importante na construção de uma aplicação no Java,
ou seja, na construção das classes e que deve ser considerado para darmos
um passo adiante em nosso aprendizado consiste na definição das variáveis
de uma classe Java. Em muitos momentos, conforme já mencionamos, as
variáveis na linguagem Java aparecerão nos textos, livros e tutoriais, sendo
tratadas como atributos. Logo, qual a diferença entre atributos e variáveis?
A diferença entre eles é que um atributo está intrinsecamente ligado
às características da classe, ou seja, descreve algo ligado ao contexto ou à
ideia central à qual a classe está relacionada ou preocupa-se em descrever.
Um exemplo seria um atributo para manter os dados de RG de uma pessoa.
Já as variáveis não precisam ser aplicadas necessariamente a um contexto da
classe, como, por exemplo, em uma variável auxiliar para o comando de repetição FOR ou mesmo uma constante. Por enquanto, não se preocupe com
isso, pois no decorrer da disciplina, essa diferença se tornará mais clara e
logo você saberá diferenciar tais elementos de programação com base nos
paradigmas da orientação a objetos.
Em continuidade ao nosso aprendizado sobre os conceitos básicos da
tecnologia Java, vamos por a mão na massa e criar nosso segundo programa, utilizando o armazenamento em memória com variáveis. Transcreva o
código a seguir para um editor de texto, assim como fizemos em nosso primeiro programa. Depois disso, compile e execute-o por meio dos comandos
javac e java, respectivamente, em um prompt de comando de seu sistema
10 t JAVA com Orientação a Objetos
operacional, conforme realizado no exemplo anterior. Não se esqueça: Para
que você consiga executar tais comandos seguido do nome de seu programa,
aqui SegundoPrograma.java, você deve estar localizado no diretório no qual
a classe se encontra, no caso onde foi salva. Fique atento também ao fato
mencionado de que o nome de nosso arquivo Java deve ser igual ao da classe
- neste exemplo, nosso arquivo Java deve ser nomeado como SegundoPrograma.java.
// SegundoPrograma.java
class SegundoPrograma{
public static void main (String arg []){
int a = 5;
a = 5+10;
System.out.println(“O valor é:”+a);
}
}
No código de nosso segundo programa, começamos a manipular o armazenamento na memória volátil, ou seja, a criação de variáveis. No programa, foi inicialmente criada uma variável do tipo inteiro ‘a’, que recebe uma
atribuição de valor, no caso o valor ‘5’. Depois, na quarta linha de nosso segundo programa, é feita uma operação cujo resultado é armazenado na mesma variável ‘a’, que é impressa na linha seguinte. Note que, em regra, quando
se quer definir variáveis, constantes e mesmo atributos no Java, como veremos nos conceitos de orientação a objetos, em primeiro lugar você deve
informar o tipo de dado que será mantido, no caso String, int ou qualquer
outro tipo de dados, sendo ele primitivo ou não. Depois, é dado o nome de
acesso à variável e finalmente, atribuímos um valor inicial ou não, conforme
é apresentado a seguir.
Tipo variável = ValorInicial;
final tipo CONSTANTE = Valor;
Capítulo 1 - Conceitos básicos da tecnologia Javat11
Na segunda linha de nossa caixa anterior, a palavra reservada final
determina que tal variável não sofrerá alterações, sendo mantido o valor atribuído no momento de sua declaração, consistindo assim em uma constante
para todo o programa, enquanto este estiver em execução. Outro detalhe
importante que você deve saber e nunca mais esquecer é que o Java é uma
linguagem de programação fortemente orientada a objetos e com exceção
dos tipos primitivos, “qualquer” coisa no Java consiste em uma classe/objeto. Devemos destacar que existem várias palavras que são reservadas pela
linguagem Java e elas não podem ser utilizadas em vários momentos em seu
programa, como, por exemplo, na definição de nome dos atributos, classes
ou outros elementos. Tais palavras reservadas são apresentadas na Tabela 1
a seguir.
Tabela 1. Palavras reservadas.
abstract
double
int
strictfp
boolean
else
interface
super
break
extends
long
switch
byte
native
synchronized
case
new
this
catch
package
throw
char
for
private
throws
class
goto
protected
transient
const
if
public
try
continue
implements
return
void
default
import
short
volatile
do
instanceof
static
while
Várias dessas palavras reservadas são elementos naturais em outras linguagens de programação, principalmente na linguagem C. Assim, o leitor que já
possui algum domínio do C ou C++, não terá dificuldades com a manipulação
das palavras reservadas no Java.
Outro ponto importante no Java é que devem ser considerados, uma
vez que são extremamente necessários, os operadores matemáticos. No caso,
utilizamos os operadores +, -, * e / para realizarmos as operações matemáticas básicas, ou seja, soma, subtração, multiplicação e divisão, respectivamente.
No Capítulo 5, iremos trabalhar com a API Math, que possibilitará a você
realizar operações matemáticas mais complexas. Devemos ainda considerar
12 t JAVA com Orientação a Objetos
outros operadores que são de suma importância para a linguagem Java, estando relacionados principalmente ao processo lógico dentro de nossos programas, como no caso dos testes condicionais, sendo que os mais utilizados
são listados no quadro da Tabela 2.
Tabela 2. Operadores no Java.
OPERADORES
DESCRIÇÃO
==
Igualdade
!=
Diferente
>
Maior
<
Menor
>=
Maior igual
<=
Menor igual
!
Negação
&&
E lógico
||
Ou lógico
Entre as várias considerações que podem ser feitas quanto ao uso dos
operadores, devemos destacar ainda a outra funcionalidade atribuída ao operador “+”, que consiste na concatenação de Strings, como pode ser observado
em nosso próximo exemplo. Conforme mencionado anteriormente e como
pode ser vislumbrado na Tabela 1, uma String não é um tipo primitivo, mas
sim uma classe. Logo, você deve estar perguntando-se: “Então, qual a diferença entre um tipo primitivo e uma classe”? Existem várias diferenças que
poderiam ser citadas, mas como diversas delas estão relacionadas ao conceito de orientação a objetos, do qual ainda não tratamos, vamos prender-nos
inicialmente à diferença de que uma classe geralmente agrega funcionalidades das quais os tipos primitivos não dispõem.
Essas funcionalidades, no caso, são denominadas de métodos e que conheceremos mais profundamente no Capitulo 2. Um exemplo que demonstra essa diferença com a utilização de uma funcionalidade pode ser observado no exemplo a seguir, no qual é realizada a conversão de uma String em um
inteiro, sendo de extrema importância para os vários programas que iremos
construir. Logo, tente memorizar tal funcionalidade da classe Integer.
Capítulo 1 - Conceitos básicos da tecnologia Javat13
// TerceiroPrograma.java
public class TerceiroPrograma {
public static void main(String args[]){
String entrada = “1”;
entrada = entrada+”1”;
Integer numero = Integer.parseInt(entrada);
System.out.println(entrada);
System.out.println(numero);
}
}
Note que no exemplo anterior, é criada uma variável do tipo String denominada entrada, que recebe o caractere “1”. Note que não é um número,
mas sim um caractere. Em nossa segunda linha do exemplo, o valor existente
na variável de entrada é concatenado a um novo caractere, no caso “1”. Note
que o resultado dessa operação matematicamente seria o valor 2, mas, no
caso, por se tratar de manipulação, em específico uma concatenação, o valor
mantido na variável será “11”, pois não estamos lidando com uma operação
matemática. Na terceira linha é feita a transformação desse conjunto de caracteres em um tipo número, que possibilitará as operações matemáticas.
Nas duas últimas linhas do exemplo são realizadas as impressões em console
dos valores existentes nas duas variáveis do programa.
1.3 Entrada de dados
Bom pessoal, um importante aspecto de qualquer programa computacional consiste na entrada de dados. Pois bem, a primeira coisa a ser feita
é conhecer a classe Scanner do pacote java.util. A classe Scanner possibilita
que seja realizada a leitura dos dados procedentes tanto do teclado quanto de
outras fontes como arquivos de texto. A Scanner passou a ser disponibilizada
a partir da distribuição Java 5, na qual se buscou simplificar todo o trabalho com a manipulação de dados oriundos de fontes de dados, no caso as
streams vindas do sistema (System.in) ou mesmo de arquivos de texto. Pois
bem, nada como um exemplo para que possamos memorizar todos os conceitos e detalhes vinculados à classe Scanner. Assim, transcreva o código a
seguir e execute, observando a utilização de nossa nova funcionalidade.
14 t JAVA com Orientação a Objetos
//QuartoPrograma.java
import java.util.Scanner;
class QuartoPrograma{
public static void main(String[] arg){
Scanner entrada = new Scanner(System.in);
System.out.println(“Digite um numero “);
//lendo o número digitado no teclado
int valor = entrada.nextInt();
System.out.println(“o numero digitado “ + valor);
}
}
Repare que em nosso exemplo anterior, surgiu um novo elemento que
está relacionado à importação de uma classe para sua utilização. Este conceito ficará mais claro em nosso próximo capítulo quando estivermos falando
sobre os conceitos relacionados à mensagem entre as classes por meio de
métodos. Mas, voltando ao exemplo, repare que na quarta linha é criada uma
variável denominada entrada do tipo Scanner, que é inicializada ‘escutando’
as entradas do sistema (System.in). Repare que na sexta linha de nosso programa, por meio da função ou do método nextInt( ) da variável de entrada, o
valor digitado pelo usuário será atribuído à variável valor, que será impressa
na linha seguinte. No momento da execução do programa, você irá reparar
que logo após ser impressa a mensagem solicitando que o usuário digite um
número, o programa aguardará a entrada do valor. Isso se deve às características do método nextInt( ) da classe Scanner.
Um ponto importante a ser citado está no fato de que, assim como
no caso das impressões no console, existem outras possibilidades para realizar a entrada de dados no Java, porém, com certeza, a utilização da classe
Scanner é a mais simples. Portanto, em nossos exemplos iniciais, a utilização
da classe Scanner será o suficiente até que conheçamos as outras possibilidades e você escolha qual a mais adequada para seus programas.
Capítulo 1 - Conceitos básicos da tecnologia Javat15
1.4 Estruturas de controle
Certo pessoal, chegamos a um ponto importante de sua leitura, no
qual iremos aprender a trabalhar com uma das características mais importantes em um programa, que consiste nas estruturas de controle. Agora,
comentaremos sobre os famosos if/else, switch, while, do-while e for. Podemos mais uma vez considerar a relação de analogia para entender estes comandos. Essencialmente, a única diferença desses comandos no Java para a
programação em Pascal está nos indicadores de bloco “{” e “}”, sendo, porém,
semelhantes em C ou C++.
Então, vamos trabalhar com cada um deles. O primeiro consiste no
comando de seleção if que possibilita que um programa analise se uma expressão lógica é verdadeira ou falsa e a partir disto, direcione a execução de
seu programa. A estrutura do if trabalha com a perspectiva de acontecimentos, logo, realiza o que sua tradução do inglês, que é “Se”, se propõe. Sendo
mais detalhista, trata-se de uma estrutura na qual, “Se” a expressão lógica
do comando for verdadeira, a sequência natural do if será executada. Porém,
“Se” a expressão lógica for falsa, surgirá um novo elemento na estrutura, que
consiste na cláusula else (então). Essa cláusula será executada na sequência
do if, obviamente se ela tiver sido implementada.
if (expressão_lógica){
Sequência 1;
}else{
Sequência 2;
}
Para que não existam dúvidas, vamos a um pequeno exemplo de fixação dos conceitos do comando de seleção if/else.
16 t JAVA com Orientação a Objetos
//Classe ExemploIf.java
class ExemploIf{
public static void main (String[] args){
if (args.length > 0){
System.out.println(“Dentro do if”);
}else{
System.out.println(“Dentro do else”);
}
}
}
Assim como fizemos em nossos exemplos anteriores, salve sua classe
com o nome “ExemploIf.java” e utilize os comandos javac e java, respectivamente, para a compilação e a execução de seu programa, conforme é apresentado na figura a seguir.
Repare que em nossa figura anterior, junto ao comando java e ao nome
da classe, consta um conjunto de valores “Teste do if/else”. Estes, por sua
vez, são os argumentos passados para o programa durante a execução, que
podem ser acessados por meio do vetor de String “args”, presente na assinatura do método principal void main. No exemplo apresentado no código
java, o código dentro do bloco definido pela cláusula if só será realizado caso,
pelo menos, um argumento seja passado no momento da execução do programa, do contrário, o vetor args estará vazio e o bloco da cláusula else será
executado.
Capítulo 1 - Conceitos básicos da tecnologia Javat17
Outra interessante estrutura de seleção que deve ser mencionada consiste na cláusula switch. A switch é equivalente de maneira lógica a um conjunto de cláusulas if organizadas de forma encadeada e isso é usualmente
mais eficiente durante uma execução de um programa. A expressão utilizada pelo switch deve retornar necessariamente um resultado ordinal, ou seja,
classificado em um dos possíveis blocos da cláusula, mais conhecidos como
“case”. As diretivas encontradas a partir do caso escolhido são executadas até
o final da cláusula switch ou até uma ordem de “break” que encerra a cláusula.
Se o valor a ser testado não possuir um caso específico, então será executada
a diretiva default, que é opcional, sendo colocada ao final da cláusula switch,
após todos os casos de classificação possíveis. Assim, para um melhor entendimento do mencionado, vamos a mais um exemplo.
/Classe ExemploSwitch.java
class ExemploSwitch{
public static void main (String args[]){
switch(args[0].charAt(0)) {
case ‘A’:System.out.println(“Vogal A”);
break;
case ‘E’:System.out.println(“Vogal E”);
break;
default:System.out.println(“Não é vogal”);
}
}
}
Repare que, em nosso exemplo, trabalhamos mais uma vez com a utilização do vetor args, ao qual os valores são passados durante a execução,
conforme feito no exemplo para a cláusula if. Aqui, nosso programa faz uma
verificação se a vogal presente na primeira posição do vetor é a vogal ‘A’ ou
‘E’, ou seja, realiza a classificação dos valores testados. É interessante mencionarmos a existência da diretiva default no exemplo, que será executada se
nenhum dos casos for realizado. Então, salve seu programa e execute, conforme apresentado a seguir.
18 t JAVA com Orientação a Objetos
Veja que neste exemplo, fizemos uso mais uma vez dos argumentos
para passar valores para que sejam verificados em nosso programa, em específico a vogal ‘A’.
Prosseguindo, outra estrutura a ser mencionada no Java é a estrutura
de repetição condicional while. O while consiste em uma cláusula na qual seu
bloco será executado durante ou ‘ENQUANTO’ a expressão lógica a ser testada for verdadeira. A execução do bloco só será realizada desde que a expressão lógica analisada seja verdadeira. A síntaxe para a estrutura de repetição
while é descrita a seguir.
while ( expressão lógica ){
Sequência;
}
Assim, mais uma vez iremos recorrer a um exemplo para a fixação desta estrutura de controle de fluxo. No programa abaixo, iremos imprimir os
valores menores que o passado no argumento durante a execução para nosso
programa.
//Classe ExemploWhile.java
class ExemploWhile{
public static void main (String args[]){
int j = 10;
Capítulo 1 - Conceitos básicos da tecnologia Javat19
while (j > Integer.parseInt(args[0])){
System.out.println(«»+j);
j--;
}
}
}
Outra possibilidade na linguagem de programação Java para a estrutura de repetição while consiste na estrutura do-while, que pode ser utilizada
como uma condição ao final do loop. Essa estrutura está intrinsecamente ligada à tradução dos termos, que é: “faça/enquanto”. Isso faz com que tenhamos a garantia de que o bloco de código definido será executado ao menos
uma vez. No caso, ambas as estruturas apresentadas, while e do-while são
repetições baseadas em condições lógicas. Observe a seguir a síntaxe da estrutura do-while.
do{
Sequência;
}
while (expressão_lógica);
Vamos brincar com a estrutura do-while? Então, transcreva o código
abaixo, compile e execute-o.
//ExemploDoWhile.java
public class ExemploDoWhile {
public static void main (String args[]) {
int min, max;
Scanner entradas = new Scanner(System.in);
System.out.print(“Digite o valor minimo:”);
min = entradas.nextInt();
System.out.print(“Digite o valor maximo:”);
max = entradas.nextInt();
do {
System.out.println(“” + min + “ < “ + max);
min++;
20 t JAVA com Orientação a Objetos
max--;
} while (min < max);
System.out.println(“” + min + “ < “ + max +
“ Condicao invalida.”);
}
}
No exemplo, são capturados dois valores vindos do teclado. O mais
importante de nosso exemplo está na cláusula do-while, que irá imprimir os
valores mínimo e máximo passados, e que serão impressos em tela e decrementados, pois fazem parte do bloco “do”, ou seja, o bloco será executado ao
menos uma vez. Diante disso, outras impressões só ocorrerão caso a condição existente na diretiva while seja satisfeita. Repare, na figura a seguir, a
execução do código proposto.
Porém, existem situações nas quais a repetição necessita ser apenas
incremental e decremental, obviamente com uma condição de parada para
ambos os casos. Para estes momentos, a estrutura ‘for’ executará a sequência
do comando enquanto a expressão lógica for verdadeira. Um fato interessante que deve ser comentado sobre essa estrutura é a possibilidade da criação
e da inicialização da variável de controle na entrada do comando. Ela será
utilizada para o controle da condição, sendo incrementada ou decrementada
a cada repetição. A seguir observe a estrutura de repetição ‘for’.
Capítulo 1 - Conceitos básicos da tecnologia Javat21
for(inicialização;
condição;
incremento/decremento){
Sequência;
}
Assim como nos exemplos anteriores, vejamos uma aplicação dessa
estrutura.
//Classe ExemploFor.java
class ExemploFor{
public static void main (String args[]){
for (int j=0; j<10; j++){
System.out.println(j);
}
}
}
Repare que na condição a ser satisfeita, foi inicializada uma variável
‘j’ que irá auxiliar no processo de controle com o seu incremento. O mesmo
poderia ser feito com o decremento do valor atribuído a ‘j’. No caso, no Java,
assim como em C ou C++, o incremento e o decremento podem ser obtidos
apenas com a utilização dos comandos ‘j++’ ou ‘j- -’.
1.5 Arrays e matrizes
Agora, em nosso primeiro capítulo, trabalharemos com os arrays ou
também conhecidos como vetores e matrizes, que nada mais são que vetores
multidimensionais. No caso, os arrays são vistos como estruturas homogêneas e estáticas, ou seja, mantêm uma estrutura regular que não muda de tamanho. Logo, o espaço destinado aos armazenamentos de valores, bem como o
tipo de dado, é sempre do mesmo tipo do início ao fim da vida do programa.
A primeira posição de um vetor no Java consiste na posição 0. A
22 t JAVA com Orientação a Objetos
linguagem disponibiliza diversas propriedades que podem ser úteis na manipulação de vetores, uma vez que sua criação é muito simples, como veremos
no exemplo a seguir. Uma das propriedades mais importantes de um array
é length, que representa o tamanho de seu vetor. Ela se torna útil, pois além
de verificar qual o tamanho do vetor, auxilia no acesso a determinadas posições, como, por exemplo, a última, que iremos acessar como (length – 1).
Mas, vamos ao que realmente interessa, que é conhecer a sintaxe de criação
de nossos vetores, bem como verificar sua manipulação. Para isso, o código
abaixo demonstra a utilização dos arrays no Java.
//Classe ExemploArray.java
class ExemploArray{
public static void main(String[] args){
int[] vetor = new int[ 10 ];
System.out.println(“Tamanho vetor:”+vetor.
length);
for (int i = 0; i < vetor.length; i++) {
vetor[i] = i;
System.out.println(vetor[i]);
}
}
}
Uma vez executado como feito anteriormente, nosso exemplo define
um vetor com 10 posições do tipo inteiro. Repare que a definição do espaço
de memória para tal vetor depende da utilização da sintaxe ‘[ ]’. Várias podem
ser as possibilidades de utilização dos vetores, que, como mencionado, consistem em um espaço de memória destinado ao armazenamento de valores
durante a execução, auxiliando na organização de nossos programas, como
exemplificado com a utilização do comando for.
Eles serão muito úteis em exemplos nos quais não são utilizados conceitos relacionados ao banco de dados, pois auxiliam na manutenção dos
valores durante a execução. No caso, teremos acesso sempre a uma determinada posição, sendo que podemos considerar um vetor como uma linha na
qual iremos acessar um endereço de memória mantido em sequência, como
demonstrado abaixo.
Capítulo 1 - Conceitos básicos da tecnologia Javat23
0
10
1
20
2
50
3
70
4
5
6
7
8
9
Assim, se acessarmos o vetor em sua segunda posição, obteremos o valor 50. Outro detalhe a ser mencionado na definição de valores para um vetor
é que estes podem ser inseridos no vetor no momento da definição de seu
tamanho, no momento de sua criação. No caso, execute o exemplo a seguir e
observe seu funcionamento.
//Classe NovoVetor.java
class VetorInicializado{
public static void main(String[] args){
double[] nota = {7,8, 8,4, 4,2, 1,8, 6,4};
for(int i=0; i < nota.length-1;i++)
System.out.println(“Valor no vetor:”+nota[i]);
}
}
Repare que no exemplo, o tamanho do vetor apresentado é definido
pelos valores que serão armazenados e passados entre { }. No caso, teremos
um vetor com 5 posições, no qual sua instanciação é realizada no momento
de sua definição com valores do tipo double. Ainda no exemplo anterior, é
importante mencionar que devido à nossa estrutura de repetição ‘for’ possuir
apenas uma linha definida em seu bloco, no caso a impressão dos valores do
vetor, não é necessária a delimitação do bloco de escopo da estrutura com os
caracteres “{” “}”.
Certo, mas e quanto às matrizes? Quando falamos sobre matrizes
no Java, devemos ter em mente um aspecto importante que, porém, não
impedirá ou dificultará seu trabalho. O fato é que a linguagem Java possui
somente arrays unidimensionais, sendo que para a criação de arrays multidimensionais, ou seja, matrizes, o que podemos fazer é criar ou simular “pseudomatrizes” por meio da definição de um array de arrays.
24 t JAVA com Orientação a Objetos
Obviamente que isso passa de maneira despercebida na construção de
suas matrizes, sendo apenas uma estruturação interna da linguagem e que,
como mencionado, não irá interferir em seu programa de maneira significativa. Você deve ter em mente apenas a constante necessidade de duas estruturas de repetições para percorrer todo o vetor, assim como em qualquer
outra linguagem de programação. Para entender melhor o processo, vamos
a um exemplo.
//Classe Matrizes.java
class Matrizes{
public static void main(String[] args){
int [][]matriz = new int[2][3];
for(int i=0; i< matriz.length; i++){
System.out.println(“Linha: “+i);
for(int j=0; j< matriz[i].length; j++){
matriz[i][j]= i+1;
System.out.println(“Valores da coluna”);
System.out.println(matriz[i][j]);
}
}
}
}
Após executar o programa, teremos como resultado a impressão da
coluna, da qual cada uma das posições faz parte no momento. Como mencionamos, são necessárias duas estruturas de repetição para percorrer toda
a matriz, lembrando que uma é responsável por fazer referência à linha e a
outra estrutura a um novo vetor, no caso, uma pseudocoluna, conforme é
impresso no programa anterior.
No Java, existem outras estruturas de vetores que, no caso, são classes que fornecem mais ferramentas e funcionalidades, agregando valor ao
programa. Em nosso sexto capítulo, iremos analisar mais e apresentar essas
estruturas adicionais para o trabalho com vetores, tais como as classes Array,
HashMap e Vector.
Então, para a definição de vetores e matrizes, sempre estaremos envolvidos em três etapas:
Capítulo 1 - Conceitos básicos da tecnologia Javat25
1-Declaração do vetor ou matriz: Ocorre da mesma forma como
em uma variável, porém basta acrescentar antes ou depois da variável um
par de colchetes.
Exemplo:
double posicao[][], controle[][];
int []indice;
2- Reservar o espaço de memória: No caso, é necessário definir o
tamanho do espaço de memória que será mantido para a estrutura. Para isso,
é utilizado o operador new.
Exemplo:
posicao = new double[10][20];
indice = new int[5];
3- Armazenar valores nas posições: Para armazenar um valor ou
informação em um dos elementos do vetor ou matriz, é necessário informar
o índice, ou seja, o local no qual iremos manter os dados.
Exemplo:
nota[3] = 7.8;
posição[4][6] = 3365;
double[] nota = {7,8, 8,4, 4,2, 1,8, 6,4};
A manipulação de vetores e matrizes é essencial para o trabalho com
as estruturas de dados. Diante disso, fica como sugestão que seja dada uma
especial atenção ao trabalho com vetores e matrizes, refazendo os exemplos
sugeridos e memorizando sua sintaxe.
1.6 Funções
Caro leitor, chegamos ao último tema que será analisado neste capítulo, que se trata do trabalho com funções. Assim como nas demais linguagens,
uma função consiste em uma rotina ou sub-rotina automatizada. As funções
26 t JAVA com Orientação a Objetos
são interessantes para as atividades que serão utilizadas rotineiramente, ou
seja, que se repetirão várias vezes e com isso, evitam que tenhamos de reimplementar tal trecho a cada momento de necessidade. Resumidamente, sempre que se quiser usar um mesmo trecho de código que realiza uma tarefa
específica repetidamente, o melhor a fazer é criar uma função.
O entendimento deste conceito é primordial para que, em nosso próximo capítulo, possamos lidar com o conceito de métodos. O primeiro passo
para criar uma função gira em torno do fato de que a função deve ser sempre
global, ou seja, deve ser enxergada por todo o código da classe e para tanto,
também deve ser estática, sendo definida pela palavra reservada static.
A palavra reservada static é responsável por garantir que ao ser realizada a execução de uma instância da classe mais de uma vez, somente haverá uma única referência para determinada variável ou função existente na
memória em seu computador. Ou seja, ao declarar uma função como static,
todas as instâncias de uma determinada classe irão acessar e compartilhar o
mesmo endereço de memória da função. Outro detalhe ao qual devemos ficar
atentos é que ao declararmos algum trecho, variável ou função como static,
isso nos permitirá que sejam acessados diretamente sem a necessidade de
criar uma instância da classe. Esses conceitos ficarão mais claros ao trabalharmos com a orientação a objetos, porém é importante saber que existem
diversos padrões de projetos que fazem referência à utilização da palavra reservada static, como, por exemplo, o Singleton.
As funções podem ser tanto sem argumentos quanto parametrizadas,
porém a estrutura padrão que deve ser digitada dentro da classe, mas fora da
função main, como é apresentada a seguir.
static void nomeDaFunção(lista de parametros){
// código da função
}
Em nosso exemplo, a estrutura apresentada faz referência a uma lista
de parâmetros, que pode ou não ser declarada. Assim, como mencionado,
existem funções nas quais não existem argumentos, mas necessariamente
devem existir os parênteses. Para ter uma melhor fixação da sintaxe, vamos
a um exemplo da utilização de funções. Digite o código a seguir e verifique
seu funcionamento, assim como nos demais exemplos averiguados até aqui.
Capítulo 1 - Conceitos básicos da tecnologia Javat27
// Classe ExemploFuncao.java
public class ExemploFuncao{
public static void mostrarMensagem() {
System.out.println(“Chamei minha função”);
}
public static void main(String[] args) {
// chamando a função dentro do
// programa principal
mostrarMensagem();
}
}
Em nosso exemplo, foi criada uma função chamada mostrarMensagem( ), que apenas imprime na tela. Nossa função é chamada dentro do método principal main( ), ou seja, poderíamos realizar a chamada quantas vezes fossem necessárias sem ter de reescrever todo o código novamente. Este
exemplo é um bom começo para o entendimento da ideia de mensagens, mas
isso é um assunto para o nosso próximo capítulo. É importante mencionar
que a ideia de função em java muitas vezes é confundida com o conceito de
métodos, sendo que iremos saber mais sobre eles em nosso segundo capítulo
Bom pessoal, assim finalizamos nosso primeiro capitulo e espero
que todos tenham assimilado, aprendido e gostado do fascinante e, ao mesmo tempo, importante mundo Java. Um detalhe que deve ser mencionado e
deve fazer parte de sua rotina como programador é que a atividade de programação torna-se cada vez mais clara com a prática. Desta forma, sempre
que possível, refaça todos os exemplos sugeridos neste capitulo. Nos vemos
no próximo capítulo.
Capítulo 2
Programação orientada a objetos
A tecnologia Java ganhou espaço nos últimos anos considerando diversos aspectos. Vários deles já comentamos, como, por exemplo, a ideia de
máquina virtual que possibilita a portabilidade entre os programas desenvolvidos com o Java ou mesmo o fato da tecnologia Java ser regida pela GPL.
Porém, outro aspecto deve ser mencionado neste contexto. Ele consiste no
fato da linguagem Java trabalhar sobre os conceitos oriundos do paradigma
da programação orientação a objetos. Neste capítulo, trabalharemos com os
conceitos básicos relacionados a este paradigma de programação, obviamente baseados na tecnologia Java. Fica como dica que é muito importante que
os temas abordados em nosso primeiro capítulo tenham sido assimilados
de maneira satisfatória, garantindo assim um melhor aproveitamento dos
tópicos que virão a ser tratados não só neste capítulo, como nos seguintes
também.
Certo! Vamos ao que realmente interessa. O paradigma de programação orientada a objetos trata de uma forma diferente o enfoque até então
trabalhado na maioria das linguagens de programa, que se sustentavam no
paradigma de programação estruturada. A ideia por trás do paradigma da
programação orientada a objetos baseia-se em uma contextualização mais
humana e próxima da realidade, isso considerando que quase tudo o que temos e lidamos em nosso dia a dia são objetos. Por exemplo, o carro que você
anda, a casa onde você mora.
Poderíamos, então, imaginar que até mesmo as pessoas, animais e
qualquer outro tipo de entidade podem ser vistos grosseiramente como objetos. Claro que existem situações nas quais esses “objetos” podem não ser
concretos e palpáveis, tal como o veículo que mencionamos, mas no caso abstrato, como, por exemplo, os softbots (robôs de software), também podemos
imaginar que eles existem e possuem elementos que os diferenciam, assim
como atitudes e funções que possibilitam que possamos imaginar que estes
podem servir de modelos computacionais.
O paradigma de programação orientada a objetos é relativamente
novo na concepção e na implementação de sistemas de software, considerado
30 t JAVA com Orientação a Objetos
os demais disponíveis no mercado. Vários são os benefícios que podem ser
vislumbrados com a utilização desse paradigma, entre eles estão o aumento
da produtividade de programadores por meio de uma maior expansibilidade
e a reutilização de código, ou mesmo o fato de controlar a complexidade e os
custos com a manutenção do software.
Neste caso, o objetivo central da orientação a objetos na criação de
programas computacionais é permitir que os programas possam ser desenvolvidos de maneira a espelhar o modo como os objetos são organizados no
mundo real, criando a figura de modelos que podem ser reutilizados quantas
vezes forem necessárias, bem como criar uma estrutura modular, na qual os
problemas possam ser resolvidos sem que o todo seja afetado. Tais benefícios
oriundos do paradigma ficarão mais claros com o decorrer da leitura do livro
e o conhecimento dos conceitos.
Diante do exposto, caro leitor, é importante que você saiba que a programação orientada a objetos baseia-se nos seguintes conceitos: classes, atributos, métodos e objetos que serão apresentados neste capítulo, além dos
construtores, encapsulamento, herança, polimorfismo e interface que serão
apresentados em nosso próximo capítulo.
2.1 Conceitos da programação orientada a objetos
Então, vamos lá amigo leitor! Aqui, iremos entender mais a fundo a
Programação Orientação a Objetos (POO). Como citado, grande parte de
nosso entendimento e relacionamento com as coisas do mundo real se dá
através do conceito de objetos concretos ou mesmo abstratos. Observando
ainda as coisas e seres que existem, há uma natural tendência a identificar o
que é cada uma destas diferentes entidades, um móvel, uma pessoa e assim
por diante.
Relembrando um exemplo claro deste fenômeno e que já citamos,
olhemos um carro. Logo, algo que se pode ver e reconhecer sua forma, tamanho, cor e aplicação. Porém, algo que acredito que muitos ainda não imaginaram é que tais observações podem ser feitas para todos os carros. Esse carro
que acabamos de observar não é o único que existe. Inúmeros outros estão
disponíveis com as mesmas características, porém sendo objetos completamente distintos. O que podemos começar a vislumbrar é que podemos ter
computacionalmente uma estrutura que garanta que as informações possam
ser mantidas e trabalhadas para inúmeras situações semelhantes.
Capítulo 2 - Programação orientada a objetost31
Voltemos ao exemplo do carro. Se existem vários carros com as mesmas características e funcionalidades, é de se imaginar que exista uma forma ou molde que garanta que todos saiam iguais, de forma padronizada e
serializada. Estes conceitos oriundos da administração foram absorvidos de
maneira eficiente pela computação e pelo paradigma da orientação a objetos, que adotou a ideia de criação de modelos computacionais que podem ser
reutilizados e fazer com que esses elementos se completem, modularizando
a programação.
O que temos inicialmente é a ideia de uma modelo, que a qualquer
momento podemos dispor de seu uso para criar novos ‘objetos’ de um determinado tipo de entidade que será mantida na memória.
É claro que cada objeto no mundo real é único, no caso, ninguém é
igual a ninguém. Cada objeto possui uma característica ou mesmo uma função que o torna único, servindo como uma identidade que o diferencia dos
demais, mesmo que sejam do mesmo tipo. Por exemplo, uma pessoa. Todas
as pessoas possuem um nome, endereço, trabalho, mas todas são diferentes
uma das outras, sendo que cada uma possui sua identidade própria, algo que
a torna única e diferenciada. Poderíamos grosseiramente dizer que se trata
do RG individual e pessoal de cada objeto. Assim pessoal, além das características genéricas vinculadas a todas as classes de objetos, existe também a
identidade vinculada a cada objeto individualmente.
Por meio dos exemplos citados, podemos descrever informalmente os
conceitos fundamentais da programação orientada a objetos, sendo eles:
t
t
t
t
t
t
t
t
t
Classe;
Atributos e métodos;
Objeto;
Referência a Objetos;
Construtores;
Encapsulamento;
Herança;
Polimorfismo;
Interface.
Como citado no início deste capítulo, iremos concentrar-nos agora
apenas nos quatro primeiros conceitos mencionados. Os demais serão apresentados nos próximos capítulos.
32 t JAVA com Orientação a Objetos
Caro leitor, mas o que você deve ter em mente quando iniciar o estudo
sobre os conceitos da orientação a objeto é o fato de que tal paradigma irá
auxiliá-lo a entender e visualizar de maneira mais ampla um problema a ser
resolvido, conseguindo assim, uma maior independência entre a programação e o modelo do problema a ser trabalhado, o que obviamente irá facilitar
possíveis manutenções e reutilizações com uma modularização, como já citei.
2.1.1 Classe
Em nosso primeiro capítulo, já trabalhamos com a criação de classes.
Mas, agora, vamos entender sua real contextualização, sendo o conceito primordial para o entendimento da POO. Uma classe é um modelo formado
por propriedades ou características, que serão definidas mais à frente como
atributos, operações e funcionalidades, que iremos conhecer como métodos
e definem o comportamento básico dos objetos oriundos de uma classe.
Podemos imaginar uma classe em seu sentido estrito e epistemológico, ou seja, uma representação genérica de um conjunto de entidades individuais e iguais. Por exemplo, uma classe de alunos, na qual todos são semelhantes dentro do contexto, com nome, matrícula e assim por diante. Em
nosso caso, a classe é a representação comum ou padrão de um conjunto de
entidades iguais. As classes são a modelagem das entidades do mundo real
de uma forma mais natural computacionalmente, pois estamos acostumados
a lidar com “objetos”, que possuem características, “atributos”, funcionalidades e “métodos”.
Tomemos como exemplo o carro. Este poderia ser definido por uma
classe que descreve de maneira comum um conjunto de objetos do tipo carro, independentemente de sua marca, cor ou tipo, atendo-se especificamente
aos conceitos que, de alguma maneira, descrevem a estrutura de um carro de
forma genérica. Então, vamos a um exemplo de uma classe, na qual podemos
descrever um conjunto de entidades a partir de um modelo computacional
genérico.
// Classe Carro.java
class Carro{
String cor;
String marca;
String modelo;
Capítulo 2 - Programação orientada a objetost33
void andar(){
System.out.println(“Carro andando”);
}
void parar(){
System.out.println(“Carro parado”);
}
}
Vale a pena novamente mencionar que os nomes das classes devem
ser iniciados com letras maiúsculas, diferenciando assim dos atributos e das
instâncias de objetos que utilizam letras minúsculas em suas iniciais. Caso o
nome seja composto por mais de uma palavra, recomenda-se que as demais
palavras também iniciem em letra maiúsculas. Exemplo: Carro, CarroDeCorrida.
Observando o exemplo de classe acima, verifica-se que ela não possui
um método principal, sendo assim, ela não retornará nenhuma ação ao ser
executada, sendo apenas a descrição de um objeto do mundo real, ou seja, a
ideia central e descrevendo o problema a ser implementado. Em nosso caso,
descreve uma estrutura para manter os dados e descreve as funcionalidades
de um carro como um modelo que será utilizado.
Isso é interessante, pois sempre que quisermos manter informações
ou mesmo utilizar suas funcionalidades, não precisaremos implementar novamente todo o código, necessitando apenas uma nova instanciação de um
objeto da classe ou mesmo sua cópia para outro projeto. De maneira mais
formal, podemos então dizer que uma classe é uma abstração que descreve
as propriedades relevantes de um conjunto de elementos em termos de sua
estrutura, atributos e comportamento - os métodos.
2.1.1.1 Qualificadores de acesso
Outro aspecto interessante e que auxilia em uma programação mais
‘realista’ com a utilização do paradigma orientado a objetos consiste na ideia
do qualificador de acesso. Tantos as classes que já conhecemos quanto os
atributos e métodos que ainda iremos conhecer fazem uso e estão na maioria
das vezes associados ao conceito de qualificador de acesso ou também conhecido como especificação de visibilidade.
34 t JAVA com Orientação a Objetos
Vamos a uma analogia para tentar entender o conceito de visibilidade. Você resolve parar um pouco com a leitura de seu livro de programação
com Java e resolve dar uma volta de carro. Diante disso, começa a lembrar
dos conceitos que leu, rememorando que seu carro é um objeto dentro de
uma classe que define todos os aspectos comuns de todos os carros. Mas,
ao ligar seu carro, você começa a pensar: “Bom, eu tenho visíveis várias características e várias funcionalidades, mas existem outras que não consigo
enxergar e que tenho certeza que contribuem para o bom funcionamento do
veículo, tais como a partida, rotação do motor e assim por diante”. Pois bem,
ao considerar esta situação, é possível verificar que, em diversos momentos
em nosso cotidiano, existem objetos, características e funcionalidades que
estão visíveis e outras não. Logicamente que isso irá depender dos aspectos
funcionais e da utilidade de cada objeto.
É aí que entram os especificadores ou os qualificadores de visibilidade.
Eles permitem definir quem ou o que pode ser visível ou acessível no momento do desenvolvimento de suas classes. Na maioria das situações, com as
quais você irá deparar-se, uma classe fará uso de outras classes e essas devem
estar acessíveis por meio da utilização dos qualificadores. Para tanto, deve-se utilizar uma das estruturas apresentadas a seguir. No caso, os tipos de
qualificadores básicos são:
Tabela 3. Qualificadores de acesso.
Qualificador
Descrição
public
Define que o conteúdo da classe é público e pode ser utilizado
livremente por outras classes do mesmo pacote ou de outro
pacote.
protected
Define que o conteúdo da classe está protegido e que só pode
ser utilizado por classes do mesmo pacote.
private
Define que o conteúdo é privado e só pode ser utilizado internamente na própria classe.
Assim, poderíamos enriquecer nosso exemplo da classe carro apresentada anteriormente com os qualificadores de visibilidade. Verifique que tanto
Capítulo 2 - Programação orientada a objetost35
a classe como suas variáveis que descrevem as características e as funções,
que descrevem seu comportamento ou funcionalidades, podem fazer uso de
tais especificadores.
//Classe Carro.java
public class Carro{
public String cor;
public String marca;
public String modelo;
protected void andar(){
ligar();
System.out.println(“Carro andando”);
}
protected void parar(){
System.out.println(“Carro parado”);
}
private void ligar(){
System.out.println(“Carro ligado”);
}
}
Os qualificadores de visibilidade são essenciais para o conceito de encapsulamento, com o qual trabalharemos em nosso próximo capítulo. Por padrão, quando não declarada a visibilidade de uma classe ou mesmo atributo
e método, a máquina virtual Java irá interpretar que tais elementos estarão
especificados com o operador protected, ou seja, protegidos e acessíveis apenas dentro do pacote do qual fazem parte.
2.1.1.2 Pacotes
Com isso, temos outro conceito que interfere nas classes tanto quanto a visibilidade. No caso, consiste na ideia de pacotes, declarados no Java
como package. A principal função desta diretiva no Java está na organização das classes e obviamente em sua visualização por outras classes de um
projeto. O que podemos então verificar é que o pacote está diretamente
36 t JAVA com Orientação a Objetos
relacionado aos qualificadores de visibilidade no acesso a classes, características e funcionalidades destas.
Ou seja, a utilização da diretiva package na classe indica que todo o
conteúdo público (public) pode ser utilizado por outras classes pertencentes
ao mesmo pacote ou não. O conteúdo determinado como protegido (protected)
na classe pertencente a um pacote só pode ser acessado ou utilizado por outras classes pertencentes ao mesmo pacote e o conteúdo definido como privado (private) só será acessível dentro da própria classe, independentemente
do pacote do qual faz parte. Caso o pacote de uma classe não seja informado,
essa classe passará a fazer parte do pacote default (src, abreviação de source,
traduzindo, fonte).
Do ponto de vista usual, dentro de seu projeto, um pacote de classes
Java consiste em um diretório, no qual existem uma ou mais classes, ou seja,
é um repositório de classes. Geralmente, colocam-se no mesmo package as
classes com o mesmo propósito. Sob certos aspectos, os packages reproduzem a ideia de bibliotecas, que podem ser importadas em uma classe Java por
meio do comando import.
2.1.1.3 Import
O comando import é responsável por garantir a reutilização e a modularização dos programas no Java. Logicamente, tais conceitos são semelhantes ao include do C ou C++ e garantem, com os demais conceitos de visibilidade e pacotes, definições de segurança e organização. Certo pessoal! Diante
do exposto até aqui, vamos a um exemplo de uma estrutura padrão de uma
classe, considerando todos os conceitos apresentados. Além disso, apresentaremos um novo elemento da linguagem neste exemplo, que consiste nos
comentários.
package local.diretorio;
// atributos da classe
// métodos da classe
}
Capítulo 2 - Programação orientada a objetost37
Note que a estrutura apresentada apenas define os conceitos já vistos
por você nos exemplos anteriores. Porém, deve-se tomar muito cuidado com
a manipulação de pacotes e imports, pois podem ocorrer erros de referência,
uma vez que as classes e os pacotes não existam.
2.1.1.4 Comentários
Bom pessoal, os comentários são extremamente úteis na linguagem
Java. Isso porque não somente permitem os simples comentários, mas ganham valor ao considerarmos as possibilidades que a própria máquina virtual define para tal elemento. No caso, a tecnologia Java define três tipos de
comentários, sendo eles: com uma linha, múltiplas linhas e documentação.
O primeiro, com uma linha, utiliza duas barras (//) para marcar seu
início e tudo após as duas barras é considerado um comentário pela JVM.
O segundo tipo de comentário utiliza a combinação /* e */ para delimitar as
múltiplas linhas de comentários. E o último tipo consiste no comentário de
múltiplas linhas, semelhante ao segundo, porém com o propósito de documentar a programação. No Java, recomenda-se como uma boa prática de programação, que todas as classes sejam documentadas e para isso, a tecnologia
Java nos dá uma mãozinha, como pode ser observado no Apêndice II deste
livro. Na Tabela 4, é demonstrada a utilização dos comentários apresentados.
Tabela 4. Tipos de comentários
Tipos de comentários
// comentário de uma linha
// tudo após as duas barras é um comentário
/*
comentário
de múltiplas linhas
*/
/** comentário de documentação que também
* podem ter múltiplas linhas
*/
38 t JAVA com Orientação a Objetos
Usualmente, o comentário com o objetivo de documentação é posicionado antes do elemento a ser documentado, sendo que seu conteúdo é
extraído automaticamente pelo utilitário javadoc fornecido com o JDK.
Como mencionado, o apêndice deste livro traz exemplos
para a criação da documentação de suas classes, com a utilização do suporte fornecido pela JVM.
Conforme já citado em nosso capítulo anterior e algumas vezes na seção onde definimos as classes, os atributos e os métodos são conceitos que
estão intrinsecamente associados à ideia de classe. Então, mãos à obra! Vamos conhecer um pouco mais sobre esses conceitos.
2.1.2 Atributos
Um atributo nada mais é que uma variável contextualizada, na qual
representa uma característica ou uma propriedade da classe de objetos em
questão. É uma variável destinada a armazenar informações associadas à
classe. Por exemplo, vamos considerar novamente o famoso carro apresentado nas seções anteriores. Seja qual for o carro, várias características podem
ser elencadas para ele, entre elas, sua marca. Trata-se de uma propriedade
comum a todos os carros, pois todo carro possui uma marca ou mesmo uma
cor. Assim, é natural que ao definirmos uma classe, por exemplo, CarroDeCorrida, ela possua um atributo ou no caso, uma variável contextualizada
destinada a armazenar sua marca e sua cor.
Podemos definir os atributos como “variáveis da classe que podem ser
de tipos primitivos ou de outras classes destinadas a manter os dados dentro
de um contexto”. A definição de um atributo dentro de uma classe Java é
feita da mesma maneira como em uma declaração de variável, sendo definido
o seu tipo e nome, que deve indicar qual seu propósito. Considere o exemplo
da classe CarroDeCorrida a seguir.
//Classe CarroDeCorrida.java
package fabrica;
Capítulo 2 - Programação orientada a objetost39
public class CarroDeCorrida{
public String cor;
public String marca;
}
Mais uma vez, temos de lembrar que, em nossa classe, não existe um
método executável, ou seja, o método main( ), sendo que tal classe representa uma descrição de um problema que, no caso, consiste em manter as informações sobre, por exemplo, um carro de corrida durante a execução. Poderíamos dizer grosseiramente que se trata de um mapeamento das informações
a serem mantidas. Mas, de nada valem os atributos sem que estes possam ser
trabalhados e transformados. É aí que entram os métodos.
2.1.3 Métodos
Como verificamos no tópico anterior, enquanto os atributos permitem
manter dados vinculados e contextualizados aos objetos, ou seja, valores que
descrevem as características de um objeto, os métodos são responsáveis por
realizar operações sobre os atributos, sendo capazes de especificar ações ou
transformações para uma classe de entidades. A ideia central na construção
de métodos está relacionada ao fato de conferir um caráter dinâmico aos objetos de uma classe, exibindo um comportamento que transforme e modifique seu estado atual. Na maioria das vezes, essa transformação se dá por
meio da alteração de valores dos seus atributos, tentando imitar o comportamento de um objeto real ou abstrato.
Bom, assim como nos demais conceitos, cada método possui uma assinatura e corpo definidos por:
t
t
t
t
qualificador de acesso;
tipo de retorno;
nome;
lista de parâmetros.
Definido o corpo do método, vem seu escopo, que define a implementação à qual o método se propõe, que deve estar entre “{ }”. Apresento a seguir
mais um exemplo, além de aproveitá-lo para demonstrar mais um elemento
da linguagem Java. Vamos utilizar o exemplo de um caixa eletrônico.
40 t JAVA com Orientação a Objetos
//Classe ContaCorrente.java
package banco;
public class ContaCorrente{
!" private String titular;
!# $ % & "'
this.saldo -= valor;
return “Saque realizado com sucesso!”;
}
!# $ % ! & "'
this.saldo = this.saldo + valor;
return “Deposito realizado com sucesso!”;
}
}
Repare que no exemplo, temos dois métodos, sacar e depositar. Por
convenção, os métodos sempre devem ser declarados com o verbo no infinitivo, tal como: desenhar, andar, propor, correr e assim por diante. No caso
de nomes compostos, o primeiro nome será no infinitivo e os demais declarados normalmente, tal como: gerarDocumentacao( ), depositarDinheiro( )
ou depositarCheque( ). Mas, um elemento deve ter chamado sua atenção no
exemplo. No corpo dos métodos pode ser visualizado o identificador this.
2.1.3.1 Identificador this
O identificador this é responsável por fazer referência a um atributo
ou método da própria classe. No exemplo apresentado anteriormente, é feita
a referência ao atributo saldo. Isso é muito útil, pois é comum que os parâmetros possuam nomes iguais aos já existentes nos atributos de uma classe,
diferenciando-os.
Uma alternativa para a utilização do identificador this é sua definição
nas classes Java para a chamada de um método construtor dentro da própria
Capítulo 2 - Programação orientada a objetost41
classe. Isso é feito por meio do método reservado this( ).
É importante que você saiba que todos os conceitos apresentados até
aqui devem ser sempre bem planejados e isso é geralmente feito no momento do projeto de suas aplicações. As classes, atributos e métodos podem ser
representados de maneira gráfica, o que é extremamente útil para enxergar
os possíveis gargalos ou deficiências em sua aplicação.
Para tanto, existem várias notações, geralmente gráficas, para a representação de classes, atributos e métodos, mas com certeza a mais utilizada
comercialmente trata-se da UML (Unified Modeling Language). Não iremos
aprofundar-nos quanto à utilização da UML em nosso livro. Fica como sugestão para o leitor que, como forma de enriquecer sua leitura e aprendizado da
programação orientada a objetos, procure conhecer mais sobre tal notação.
2.1.4 Mensagem e modularização de código
Como mencionado no início deste capítulo, um dos pontos-chave da
programação orientada a objetos consiste na possibilidade de modularizar o
código. Com isso, podemos chegar mais próximo da realidade vivida no cotidiano, bem como possibilitar uma manutenção mais simples. É considerando
tais perspectivas que surge um novo conceito relacionado à linguagem Java e
ao paradigma de orientação a objetos: a mensagem.
O conceito de mensagem entre as classes Java trabalha com a ideia de
que as classes, ou melhor, os objetos ou as instâncias de uma classe podem invocar métodos de objetos de outras classes para desempenhar alguma tarefa
de maneira a completar sua função e melhor definir o problema. O conceito
garante que caso uma classe necessite de um serviço ou funcionalidade, mas
a função faça parte de outro contexto, então será necessário apenas utilizar
o que é proposto em outra classe, mantendo assim a organização de modo a
garantir que todo o código fique em seu devido lugar. Obviamente, isso tornará seu código mais inteligível para as possíveis intervenções que venham a
ocorrer no futuro, como no caso de uma manutenção.
Dúvidas? Certo, vamos ao já mencionado no exemplo do carro. Imagine que se construa uma classe Carro que possui vários atributos, tais como:
cor, marca e modelo. Além disso, na classe sejam definidos os métodos andar( ) e parar( ). Pois bem, até aqui nada de novidade. Porém, para que um
veículo ande, é necessário que seu motor seja ligado. É aí que entra o conceito
de mensagem como uma forma de melhor definir o problema.
42 t JAVA com Orientação a Objetos
O correto seria a criação de uma nova classe, que defina a ideia do
objeto motor, que teria como atributos características, tais como: potência,
ignição e combustível. Além dos atributos, os métodos para a manipulação
desses atributos, que seriam, por exemplo, ligar( ) e desligar( ). Desta forma,
quando um carro anda, o que é acionado é o motor e para tanto, é ele que
deve ter seu estado alterado. O motor, como parte do carro, deve receber uma
mensagem oriunda da classe carro que informe tal solicitação, fazendo acesso ao método ligar( ). Logo, o conceito de mensagem trata da comunicação
entre as classes.
Para ter uma melhor fixação, vamos demonstrar tais conceitos da maneira como devem ser abordados, criando classes. Aqui, utilizaremos as classes que definem nosso problema e de forma legível para seu entendimento,
caro leitor. Então, vamos lá? Transcreva as classes abaixo para um editor de
texto. Entretanto, não é necessária a compilação e a execução do programa,
já que não temos uma classe principal que garanta uma visualização dos
resultados.
// Classe CarroNovo.java
package fabrica;
public class CarroNovo{
// atributos
String cor;
String marca;
String modelo;
Motor novoMotor = new Motor();
// métodos
public void andar() {
novoMotor.ligar();
System.out.println(“ANDANDO”);
}
public void parar() {
novoMotor.desligar();
System.out.println(“PARANDO”);
}
}
Capítulo 2 - Programação orientada a objetost43
_____________________________________________________
__
// Classe Motor.java
package fabrica;
public class Motor {
// atributos
Boolean ignicao;
String potencia;
String combustível;
// métodos
public void ligar() {
ignicao = true;
System.out.println(“Ligado”);
}
public void desligar() {
ignicao = false;
System.out.println(“Desligado”);
}
}
Neste exemplo, podemos notar que existe um novo elemento na classe
CarroNovo, que consiste na utilização de uma INSTÂNCIA da classe Motor.
Estarei abordando mais sobre a importância deste conceito na próxima seção com o uso da palavra reservada new. Outro exemplo que pode ser útil
para o entendimento do conceito de mensagens entre classes é apresentado
a seguir.
No próximo exemplo, é implementada uma classe denominada ProgramaPrincipal, com um método principal onde se cria uma variável ou para
que você vá acostumando-se, um objeto da classe Mensagem que possui o
método imprimir( ) acessado durante a execução pela classe ProgramaPrincipal. Trata-se de um exemplo sem uma contextualização, mas que é útil para
enxergar a divisão de tarefas e a modularização do código com os conceitos
da orientação a objetos. Considerando as características do exemplo, podemos compilar e executá-lo para a visualização.
44 t JAVA com Orientação a Objetos
// Classe ProgramaPrincipal.java
class ProgramaPrincipal{
public static void main (String arg []){
Mensagem m = new Mensagem();
m.imprimir();
}
}
// Classe Mensagem.java
class Mensagem{
public void imprimir(){
System.out.println(“Mensagem”);
}
}
No exemplo, é feita a utilização do método imprimir( ) da classe Mensagem pela classe ProgramaPrincipal. Ainda no exemplo, é feita novamente a
utilização do operador new. Trata-se de mais uma palavra reservada para
a tecnologia Java que é responsável pela atribuição de uma instância
a um atributo. A ideia de instanciação é essencial para compreendermos
todo o paradigma da programação orientada a objetos. Então, vamos conhecer o conceito de objeto.
2.1.5 Objeto ou instanciação
Como já deve ter sido percebido, a criação de novos objetos de uma
classe se chama instanciação ou simplesmente criação do objeto. O objeto
consiste em representar um único exemplar de uma determinada classe. Vamos voltar ao exemplo do carro. A classe CarroDePasseio representa os atributos (características) e os métodos (comportamento) de todos os carros de
passeio. Logo, a classe consiste em um modelo para reservar um espaço de
memória para manter e transformar os dados. A partir da classe CarroDePasseio, podemos criar vários objetos, ou seja, várias representações ou instâncias a partir do mesmo modelo, pertencente à classe. Por exemplo, podemos
manter os dados de uma Mercedez, um Gol ou qualquer que seja o carro de
passeio. Note, qualquer um dos veículos citados é um exemplar específico da
Capítulo 2 - Programação orientada a objetost45
classe CarroDePasseio. Cada carro em específico possui características, tais
como, cor, tamanho e marca, que o torna único e métodos que definem como
ele pode ser ligado, desligado e como deve andar.
Se fôssemos pensar grosseiramente, poderíamos ter uma classe Pessoa, da qual você, leitor, é um objeto ou uma instância da classe (conjunto)
de pessoas. Pensemos, você é único e ninguém é igual ao outro, onde cada um
possui características próprias e comportamentos distintos, ou seja, cada um
é um objeto diferente pertencente a uma determinada classe.
Pode-se definir um objeto como sendo uma instância de uma classe
(grupo) que tem valores próprios para os atributos definidos na classe, tendo
uma identidade única, ou seja, sendo único no conjunto de elementos do
mesmo tipo. A notação para instanciar um novo objeto utiliza o operador
new, destinado à sua criação.
NomeDaClasse nomeDoObjeto = new NomeDaClasse();
Exemplo:
CarroDePasseio mercedez = new CarroDePasseio();
Mensagem m = new Mensagem();
A primeira coisa que você deve ter reparado é que a declaração de um
objeto é realizada de maneira semelhante a dos atributos de uma classe. Então, é valido mencionar que as classes podem conter instâncias de outras
classes como atributos, com diversos elementos compondo um todo, assim
como apresentado na classe Carro e Motor.
Outro exemplo seria a mesma classe carro que pode ter como atributo
um proprietário, que pode ser definido como sendo de uma classe Pessoa,
como é apresentado no exemplo a seguir. Conforme já visto neste capítulo,
o exemplo abaixo apresenta o conceito de importação de outras classes. O
código é meramente ilustrativo, não sendo necessária sua execução.
// Classe Carro.java
package concessionaria;
import concessionaria.Pessoa;
public class Carro{
// atributos
46 t JAVA com Orientação a Objetos
int velocidadeMaxima = 50;
Pessoa proprietario = new Pessoa();
// métodos
public void mostrarVelocidadeMaxima(){
proprietario.dirigir();
System.out.println(velocidadeMaxima);
}
}
______________________________________________________
// Classe Pessoa.java
package concessionaria;
public class Pessoa{
String nome;
String endereco;
public void dirigir(){
System.out.println(“DIRIGINDO”);
}
}
Mas, quando falamos de objeto, o que realmente está por trás disso é
outra importante característica da orientação a objetos, no caso, o reuso do
código. Mais uma vez fazendo referência ao exemplo do carro, a mesma classe pode ser utilizada para definir vários objetos do mesmo tipo, sendo que
somente os dados referentes às características de cada um diferem.
Por exemplo, poderíamos ter um objeto denominado carroDePasseio e
outro instanciado com sua referência sendo carroDeCorrida. Ambos podem
ser instâncias da classe Carro, sendo que os dois objetos são oriundos da
mesma classe, diferenciados apenas pelos valores associados aos atributos
que cada um assume. Porém, ambos utilizam o mesmo modelo, mais especificamente, a classe Carro.
Cabe ainda destacar outros detalhes da instanciação de objetos. Agora que já sabemos como os objetos são criados, com a utilização do operador
new, é importante entender o que acontece quando criamos esses objetos.
Capítulo 2 - Programação orientada a objetost47
Quando um objeto é criado por meio do comando new, o que é retornado à
variável ou ao atributo, como já vimos, é uma referência. Referência? O que é
isso? Vamos à explicação.
Quando um objeto é criado, ele é uma referência ou uma indicação
para um determinado espaço de memória reservado, sendo que o nome da
variável torna-se apenas uma âncora para o acesso ao espaço de memória
no momento da execução de seu programa, no qual estão dispostos nossos
atributos e métodos. A ilustração apresentada a seguir torna isto mais claro.
Figura 2. Referência para objetos.
Após a explicação e as considerações feitas sobre o espaço de memória
ser o objeto, é necessário também dizer que o operador new não é a única
forma de atribuirmos referência a um objeto ou uma instância. Uma maneira
de visualizar o objeto como uma referência, no caso uma âncora, é o fato de
também se poder fazer a cópia de uma referência de um objeto para outro,
com ambos passando a apontar para o mesmo endereço de memória. Se copiarmos uma referência para outro objeto, ele estará referenciando o mesmo
objeto que a instância original está referenciando. Para ter um melhor entendimento do que foi dito, observe a Figura 3.
Figura 3. Cópia de referência de objetos.
48 t JAVA com Orientação a Objetos
Entendido o conceito, você deve perguntar: Mas, como fica isto em
linhas de código no Java? Respondendo ao seu questionamento, observe e
transcreva o código na sequência, onde são criadas duas classes que exemplificam o explicado. Conforme visto em nosso primeiro capítulo, utilizaremos
o método executável main para imprimir os valores que foram referenciados
aos atributos das classes.
//Classe Carro.java
package concessionaria;
import concessionaria.Pessoa;
public class Carro{
//atributos
int velocidadeMaxima = 50;
Pessoa proprietario = new Pessoa();
//métodos
public void mostrarVelocidadeMaxima(){
proprietario.dirigir();
System.out.println(velocidadeMaxima);
}
}
___________________________________________________
//Classe Pista.java
package concessionaria;
public class Pista{
public static void main(String[] args){
Carro carroDeCorrida = new Carro();
Carro carroDePasseio = new Carro();
carroDeCorrida.velocidadeMaxima = 300;
carroDePasseio.velocidadeMaxima = 60;
carroDeCorrida.mostrarVelocidadeMaxima();
carroDePasseio.mostrarVelocidadeMaxima();
Capítulo 2 - Programação orientada a objetost49
Carro novoCarro = carroDeCorrida;
carroDeCorrida.mostrarVelocidadeMaxima();
carroDePasseio.mostrarVelocidadeMaxima();
novoCarro.mostrarVelocidadeMaxima();
}
}
Entre as diversas linhas que devem ser observadas no código anterior,
devemos dar mais importância à linha na qual a instância da classe Carro
chamada novoCarro recebe a referência, ou seja, o endereço de memória do
objeto carroDeCorrida, observe.
Carro novoCarro = carroDeCorrida;
Os atributos carroDeCorrida e novoCarro fazem referência ao mesmo
espaço de memória, pois não foi atribuído um novo espaço de memória para
o objeto novoCarro, mas sim, o mesmo espaço ao qual o objeto bolaGrande
já fazia referência. Esta é uma característica fundamental da linguagem Java,
sendo que sempre são passadas referências no Java, não valores. Tal fato torna o trabalho e a manipulação das estruturas de dados com o Java muito
simples. Assim, finalizamos nosso segundo capítulo e no próximo, iremos
continuar a conhecer outros conceitos vinculados à programação orientada
a objetos.
Capítulo 3
Construtores, destrutores e encapsulamento
Dando continuidade à nossa análise sobre os conceitos relacionados
ao paradigma da programação orientada a objetos, iremos, neste capitulo,
descrever e aprender a lidar com três novos e importantes elementos para
a tecnologia Java. É importante que os conceitos expostos nos capítulos anteriores tenham sido compreendidos para ter um melhor aproveitamento
do conteúdo que será apresentado neste capítulo. Como visto nos capítulos
anteriores, a ideia de classe na linguagem Java é o núcleo de todo o processo
da orientação a objetos, no qual podemos representar qualquer objeto, seja
ele concreto, tal como um carro, seja mesmo abstrato, tal como um softbot.
Mas, não podemos deixar de mencionar outros conceitos importantes
que auxiliam a concretizar o paradigma, como, por exemplo, os atributos e os
métodos, conhecidos no capítulo anterior, ou mesmo os métodos construtores, destrutores e a ideia de encapsulamento na qual iremos trabalhar. Tais
ferramentas tecnológicas possibilitam que a linguagem Java possa descrever
com maior clareza o problema e com isso, obter uma solução melhor. O primeiro conceito no qual iremos trabalhar são os construtores e sua função no
paradigma e na linguagem Java.
3.1 Construtores
Em nosso segundo capítulo, analisamos a criação de objetos, que denominamos de instanciação de um objeto. Ali, obtínhamos uma referência
para um espaço de memória, lembra? Pois bem, como vimos, isso nos obriga
a seguir uma determinada sintaxe, conforme é apresentado a seguir.
Carro novoCarro = new Carro();
Já foi analisada a utilização da palavra reservada new no ato da instanciação de um objeto ao verificar sua responsabilidade pela nova instância.
Mas, podemos dizer que é uma meia verdade. Isto porque, como visto no
exemplo anterior, o operador new antecede a chamada de um método. É esse
52 t JAVA com Orientação a Objetos
método que denominamos de construtor do objeto ou apenas construtor.
Sua função, como o próprio nome explicita, é construir, ou melhor,
preparar o espaço de memória definido para um objeto para receber os dados conforme determinado na estrutura definida na classe. Sua invocação
geralmente é feita no momento da criação de um objeto, da mesma maneira
como é feita para qualquer método. Porém, um detalhe a ser considerado é
que os métodos construtores possuem o mesmo nome da classe na qual são
definidos.
Por definição, os métodos construtores são métodos especiais invocados pelo sistema na instanciação de um objeto, sendo que o operador new
apenas informa qual método construtor de uma determinada classe será utilizado pela JDK no momento da criação do objeto para inicializar os valores
dos atributos.
Neste caso, cabe um exemplo para ter uma melhor visualização dos
conceitos. No exemplo a seguir, definimos um método para a classe Carro. Transcreva o código e tente visualizar as peculiaridades dos métodos
construtores.
// Classe Carro.java
package conceitos;
public class Carro {
// atributos
Boolean chave = true;
// métodos construtores
public Carro(){
this.chave = true;
}
public Carro(Boolean chave){
this.chave = chave;
}
// métodos funcionais
public void ligar() {
Capítulo 3 - Construtores, destrutores e encapsulamentot53
chave = true;
System.out.println(“Ligar”);
}
public void desligar() {
chave = false;
System.out.println(“Desligar”);
}
}
No exemplo apresentado, foi criada a classe Carro, sendo definidos
dois métodos funcionais: ligar e desligar. Pois bem, mas o que realmente importa para nós está na definição dos métodos construtores. Repare que no
exemplo, foi definido o método público Carro(). Ele consiste em um método
construtor.
A invocação de um método construtor nada mais é que uma referência
para a área de memória onde foi criado o novo objeto, podendo ser trabalhados os valores de inicialização do objeto. Desta forma, podemos, então,
dizer que o método construtor é responsável pela definição e pela alocação
de memória para um novo objeto.
Como mencionado, um método construtor deve possuir o mesmo nome da classe, porém verifique que, para tal método, não é definido
nenhum tipo de retorno. Isso acontece sempre e com qualquer que seja o
método construtor ou mesmo objeto a ser inicializado, pois nosso método
construtor é responsável por estruturar o espaço de memória e inicializar os
valores para os atributos de uma classe, não por fazer operações funcionais
do objeto criado e retornar valores.
O método construtor apresentado sem parâmetros também é conhecido como método construtor DEFAULT, sendo o mais comum. Nele, podem
ser realizadas todas as inicializações citadas para os métodos construtores
parametrizados. Outro aspecto interessante a ser destacado sobre os métodos construtores é a capacidade fornecida pela própria JVM, que no caso de
não ser definido nenhum método construtor para uma classe, a própria JVM
fica responsável por disponibilizar um método construtor para a inicialização do espaço de memória no ato da instanciação de um objeto. Esse método,
porém, sempre será vazio, sem parâmetros e sem inicializações com valores
para seus atributos. Um exemplo para tal citação é a criação do objeto ‘chave’
demonstrado no quadro anterior, no qual é utilizado o construtor default.
54 t JAVA com Orientação a Objetos
Em vários problemas, é interessante que os valores de determinados
atributos sejam inicializados no momento da instanciação dos objetos, como
ocorreu no nosso exemplo. No caso, podem ser criados quantos métodos
construtores forem necessários, diferenciados apenas pela lista de parâmetros definida para o método construtor PARAMETRIZADO. Assim, podem
ser passados valores para a inicialização dos atributos da classe no momento
de sua instanciação. Outra importante função dos métodos construtores é a
possível realização operações que auxiliem e interfiram no funcionamento
de um objeto, abrindo arquivos, estabelecendo comunicação, inicializando
dispositivos que serão úteis ao programa.
Carro novoCarro = new Carro(Boolean chave);
Resumidamente, uma classe Java pode possuir diversos construtores, todos obrigatoriamente devem possuir o mesmo nome da classe, SENDO SOMENTE UM DEFAULT, diferenciados por sua assinatura, no caso,
suas listas de parâmetros. Isto é o que denominamos de sobrecarga do construtor.
Isto torna o conceito de orientação a objetos mais próximo da realidade, facilitando o uso de classes em diferentes contextos e circunstâncias.
Detalhe importante a ser mencionado aqui é o fato de um método construtor
poder fazer a chamada de outros métodos da classe ou mesmo instanciar novos objetos, realizando assim, a chamada de métodos construtores de outras
classes e com isso, possibilitando a construção de objetos que se completam
e definem de maneira mais realista a representação do problema, já que, conforme vimos, em sua maioria, um programa é composto por várias classes
que se comunicam por meio de mensagens.
Uma vez criados os objetos, é interessante para nossa programação
entender a liberação dos recursos de memória utilizados. Para isso, iremos
detalhar e trabalhar com os destrutores e o coletor de lixo.
3.2 Destrutores e Garbage Collector (Coletor de Lixo)
Na tecnologia Java, existem os construtores que realizam as operações iniciais de um objeto, preparando-o para o correto funcionamento,
além da alocação de memória para estas, mas existem também os métodos
Capítulo 3 - Construtores, destrutores e encapsulamentot55
destrutores. Assim como os construtores, o destrutor é uma característica
oriunda da linguagem C++. É um método especial que tem por finalidade
liberar o espaço de memória utilizado por um objeto e com isso, finalizar
qualquer operação que esteja em andamento, como, por exemplo, fechar arquivos, encerrar a comunicação e liberar os recursos alocados no sistema,
e com isso, eliminar qualquer vestígio da instância do objeto. Os métodos
destrutores seguem uma sintaxe padrão, conforme é apresentado a seguir.
! " ?&' // Aqui é geralmente utilizado
// para liberar recursos.
}
Como apresentado, um método destrutor deve sempre ser identificado pelo nome finalize() e pode ser invocado a qualquer momento. Outro
detalhe que pode ser percebido quanto aos destrutores é que ao contrário
dos construtores, que podem conter parâmetros em sua assinatura, os destrutores não têm parâmetros na assinatura do método, porém sempre deve
ser definido o valor de retorno como void.
A real finalidade dos destrutores é liberar do sistema a memória usada durante a execução do objeto, sendo que este é um dos mais complexos
problemas encontrados no desenvolvimento de sistemas. Isto é devido ao
fato de que os recursos de um sistema são finitos. Importante detalhe a ser
citado é que a invocação de um método destrutor não caracteriza a finalização concreta do objeto instanciado, mas sim, apenas uma sinalização para
que o sistema, assim como definido pela JVM, libere os recursos recolhendo
o espaço de memória do objeto destruído. É aí que surge outro conceito.
3.2.1 Garbage Collector
Preocupados com o problema do estouro de memória comum e encontrado em diversas linguagens, os criadores da tecnologia Java incorporaram à maquina virtual um mecanismo que tem como objetivo a liberação da
memória automaticamente, que é executado simultaneamente ao programa
Java. Este recurso é denominado Garbage Collector ou mais popularmente conhecido como Coletor de Lixo. No Java, conforme mencionado no
56 t JAVA com Orientação a Objetos
capítulo anterior, um objeto sempre referencia um endereço de memória,
porém quando esse endereço deixa de ser referenciado pelo programa, no
caso, sai do processo de execução do programa em questão, este espaço de
memória é marcado para uma futura liberação pelo Garbage Collector. A ideia
é que uma vez marcado como um objeto no qual seu espaço de memória pode
ser devolvido para o sistema, o coletor de lixo devolve a memória ao sistema
para uma futura utilização.
Tal sinalização é feita por meio dos métodos destrutores já vistos.
Outro aspecto interessante do coletor de lixo está no fato de que este mecanismo tenta sempre se antecipar aos possíveis erros, oriundos da falta de
recursos do sistema. Para isso, quando não há mais memória disponível no
sistema, o garbage collector faz uma varredura para obter os espaços de memória ociosos para sua liberação de imediato para o sistema, prevenindo erros de estouro de memória.
Considerando tais elementos da tecnologia Java, um aspecto positivo é que não é necessário o tratamento para a liberação da memória, ficando
a critério do programador a utilização dos métodos destrutores, diferentemente de outras linguagens de programação, como, por exemplo, o C++, que
deve realizar tal tratamento. Um detalhe importante para finalizar nossa
análise sobre o coletor de lixo e os métodos destrutores está no fato de que
um objeto no Java pode ser eliminado com uma nova inicialização, por meio
da invocação dos métodos construtores que irão reiniciar a referência de uma
variável ou um atributo. No caso, o objeto perderá a referência ao espaço
de memória anterior. Outra possibilidade de fazer com que o coletor de lixo
libere recursos é atribuir o valor null a um objeto, sendo que com isso, sua referência de memória ficará à disposição. Tais possibilidades são importantes,
pois garantem que não ocorram erros no sistema, devido à falta de recursos
do hardware.
3.3 Encapsulamento
Iremos, agora, trabalhar com outro importante elemento conceitual
do paradigma de programação orientada a objetos - o encapsulamento. Este
conceito é útil para garantir que a solução dos problemas propostos seja sempre realista e que cada elemento só enxergue o que realmente é importante. No caso, consiste em um mecanismo que é utilizado com a finalidade de
esconder os detalhes de implementação das classes Java, de maneira a se
Capítulo 3 - Construtores, destrutores e encapsulamentot57
assemelhar à realidade dos objetos reais. O ponto central do conceito é possibilitar uma maior segurança e domínio da complexidade, pois uma classe
deve ofertar apenas o que ela pode fazer, não como ela faz.
Vamos ser um pouco mais claros. O objetivo do encapsulamento é que
uma classe impeça que seus atributos e métodos sejam acessados diretamente. Para isso, o certo é disponibilizar apenas métodos públicos para o acesso aos valores produzidos que realmente devem ser visualizados, ofertando
apenas a real funcionalidade que a classe se propõe a realizar. Note que aqui é
feita uma referência aos qualificadores de visibilidade apresentados em nosso segundo capítulo, sendo: public, protected e private.
Tais características são oriundas da ideia cotidiana que encontramos e
que geralmente passam despercebidas em nosso dia a dia na abstração de um
objeto do mundo real. Vamos a um exemplo para ter um melhor entendimento. Ao assistirmos televisão ou mesmo ouvirmos um rádio, não temos nenhum conhecimento de como estes objetos trabalham internamente. Aqui,
apenas são fornecidas interfaces ou maneiras de interagir com estes equipamentos, por exemplo: um botão para ligar/desligar ou mesmo aumentar/
diminuir o volume. Todos esses elementos são funcionalidades que mexem
com parâmetros ou propriedades internas (atributos) do equipamento. Então, o encapsulamento é útil na proteção dos atributos da classe, disponibilizando, por meio de métodos, o acesso às propriedades da classe e com isso, a
alteração do estado destas e do sistema.
Definindo os métodos de encapsulamento, eles fornecem uma forma
de comunicação entre os usuários de uma classe e a implementação disponível nesta, facilitando a escrita, manutenção e alteração dos programas. Existem métodos convencionados entre os programadores Java que criam uma
interface entre os atributos e o usuário, sendo uma camada de comunicação
publica. São denominados métodos get e set vinculados a cada atributo das
classes e evitam o acesso direto a tais atributos, aumentando a segurança ao
impedir que a implementação e a forma como os atributos são tratados estejam visíveis. Um exemplo desses métodos é apresentado no quadro a seguir.
// Classe Carro.java
package conceitos;
public class Carro {
// atributos
58 t JAVA com Orientação a Objetos
private Boolean chave = true;
// método construtor
public Carro(){
setChave(true);
}
public Carro(Boolean chave){
setChave(chave);
}
// métodos de encapsulamento
private void setChave(Boolean status){
this.chave = status;
}
private Boolean getChave(){
return this.chave;
}
// métodos de funcionamento
public void ligar() {
setChave(true);
System.out.println(“###LIGADO###”);
}
public void desligar() {
setChave(false);
System.out.println(“###DESLIGADO###”);
}
// método destrutor
! " ?&' chave = null;
}
}
Destrinchando o código apresentado no quadro anterior, observe que
os métodos que descrevem o funcionamento da classe, aqui ligar e desligar,
Capítulo 3 - Construtores, destrutores e encapsulamentot59
não fazem mais referência direta aos atributos, como ocorria nos outros
exemplos apresentados até aqui. Algumas partes do código devem ser citadas. Note que o atributo-chave passou a ser declarado como privado, evitando o acesso de qualquer outra classe diretamente a ele. Foram implementados os métodos set e get, que garantem o encapsulamento dos atributos, no
caso, as interfaces de acesso para a inserção de valores e recuperação. Assim,
garantimos que qualquer requisição desse atributo deve ser realizada através
dos métodos get e set definidos para o atributo.
Nesta nova contextualização, os atributos devem obrigatoriamente ser
acessados por meio de métodos. Os métodos set são utilizados para atribuir
valores e os métodos get para a recuperação dos valores atuais dos atributos.
Isso consiste em uma boa prática de programação que deve ser inserida em
seu cotidiano, ainda mais no que tange à programação Java.
Vamos a mais um exemplo para demonstrar os vários conceitos vistos
até aqui. Transcreva o programa a seguir e execute-o, conforme já foi realizado em vários exemplos dos Capítulos 1 e 2.
// Classe Carro.java
package conceitos;
import java.util.Scanner;
public class Carro{
// atributos
private Boolean chave = true;
// métodos construtores
public Carro(){
setChave(true);
}
public Carro(Boolean chave){
setChave(chave);
}
// métodos de encapsulamento
private void setChave(Boolean status){
this.chave = status;
}
60 t JAVA com Orientação a Objetos
private Boolean getChave(){
return this.chave;
}
// métodos de funcionamento
public void ligar() {
setChave(true);
System.out.println(“###LIGADO###”);
}
public void desligar() {
setChave(false);
System.out.println(“###DESLIGADO###”);
}
// método destrutor
! " ?&' chave = null;
}
// método executável
public static void main(String[] args){
Scanner scn = new Scanner(System.in);
Carro novoCarro = new Carro();
System.out.println(“Digite 1 para ligar \n 2 para“
+ desligar);
int opcao = scn.nextInt();
switch(opcao){
case 1: novoCarro.ligar();
break;
case 2: novoCarro.desligar();
break;
}
}
}
Capítulo 3 - Construtores, destrutores e encapsulamentot61
No exemplo, é solicitada uma entrada ao usuário que define qual será a
funcionalidade a ser acessada. Para tanto, a classe Scanner é responsável por
capturar essas entradas em um console, sendo utilizado o método nextInt( ). O
programa apresentado inicialmente importa a classe Scanner para que possamos capturar uma entrada do teclado, criando uma interação com o usuário. Após a definição da classe Carro, foi criado o atributo-chave, bem como
os métodos de encapsulamento get e set para acesso a este. Foram criados os
métodos construtores, configurando a sobrecarga dos construtores, com um
possuindo parâmetros e o outro, não.
Um importante detalhe a ser mencionado para os construtores é que,
caso o programador defina pelo menos um método construtor, o método
default disponibilizado pela própria JVM deixará de existir, sendo necessária
a declaração do método construtor default pelo programador, conforme é
exemplificado. Este é um conceito que está relacionado à ideia de polimorfismo que iremos explorar mais a fundo no próximo capítulo. Por enquanto, atente ao fato de que sempre que um método construtor for definido, o
método default disponibilizado pela JVM deixará de existir e você deverá
fornecê-lo, caso queira utilizá-lo. Finalizando, o exemplo anterior possui um
método executável que define uma instância, no caso, o objeto da classe carro denominado ‘novoCarro’ que permite o acesso ao espaço de memória com
seus atributos e métodos.
Assim, finalizamos mais um capítulo, no qual conhecemos os elementos do paradigma da programação orientada a objetos. Fica como sugestão
que todos os exemplos sejam refeitos para que todos os conceitos vistos sejam mais bem assimilados. Em nosso próximo capítulo, continuaremos a trabalhar com a orientação a objetos aplicada à tecnologia Java.
Capítulo 4
Herança, polimorfismo e interface
Caro leitor, neste capítulo iremos conhecer outros conceitos ainda
vinculados ao paradigma da programação orientada a objetos. Sendo bem
mais específico, poderíamos dizer que este é o mais importante dos capítulos
estudados até aqui, pois daremos os princípios que tornam o paradigma e a
tecnologia diferenciados.
Como mencionado diversas vezes no decorrer do livro, o paradigma da
Programação Orientada a Objetos (POO) trabalha determinando um retrato
fidedigno dos objetos do mundo real. Logo, nada mais sensato e interessante
que a existência de um conceito que denote a hierarquia existente entre os
objetos reais. Tal conceito é de extrema importância e diferencia a linguagem
de programação Java que, aliada à orientação a objetos, possibilita um melhor aproveitamento e reutilização do código por meio da ideia de herança.
Outra característica da POO que iremos analisar está vinculada ao polimorfismo. A palavra polimorfismo é oriunda da palavra grega polimorfos,
que significa diversas formas. Este conceito é fundamental, já que possibilita
chamadas ou invocações semelhantes para diferentes implementações por
meio da redefinição ou da sobrecarga de métodos.
Finalizando, neste capítulo ainda conheceremos o conceito de
interface, que nos possibilitará uma maior abstração da modelagem do mundo real para as classes no Java. Isso torna a linguagem Java mais extensível
e reutilizável.
4.1 Herança
Como mencionamos, iremos trabalhar nesta seção com um dos mais
importantes conceitos da orientação a objetos, se não o mais importante:
a herança. Este, por sua vez, consiste na aplicação de uma técnica muito
simples e que segue os preceitos do mundo real. Um bom começo para o entendimento da herança é vislumbrar os aspectos já bem conhecidos da estrutura familiar, na qual existe uma hierarquização entre pais e filhos, normal
a qualquer ser humano. Ao definir a herança, pode-se dizer que consiste em
64 t JAVA com Orientação a Objetos
uma técnica na qual uma determinada classe, denominada subclasse, utiliza
atributos e métodos já definidos em outra classe denominada superclasse
(pai), especificada como ancestral da subclasse (filho).
Sendo mais claro, a herança consiste no compartilhamento de atributos e métodos, assim como acontece num relacionamento genético do tipo
pai e filho. A classe-pai, que é denominada superclasse ou classe base, possui
definições gerais que podem ser utilizadas nas classes-filho, também conhecidas como subclasses. Isto significa que podem ser definidas classes mais
genéricas, ou seja, em um sentido mais amplo, sendo refinadas sucessivamente em subclasses que herdam todas as características das classes mais
genéricas, sendo assim mais específicas.
Isso é conhecido como especificação de uma classe. A especificação,
termo muito utilizado quando se fala de herança, centra no fato de que as
subclasses possuem características próprias que as diferenciam das classes-pai ou mesmo de outras classes que também herdam das mesmas superclasses, das quais são herdados atributos e métodos. Um exemplo disso seria
imaginarmos uma estrutura familiar, no qual um filho herda de seu pai diversas características, tais como a cor dos olhos, cabelos e voz. Porém, esse filho,
que herda tais atributos, possui características que são especificamente suas,
como, por exemplo, o jeito de andar ou mesmo de correr. Isso o torna mais
especifico que o ente do qual herdou várias características, no caso, seu pai.
Podemos considerar que sempre que uma classe herda de outra, a classe que
herda passa a ser um tipo especial da classe-pai.
Complicado? Veremos que não. Vamos a mais um exemplo, desta vez
contextualizado a linguagem Java para clarear esta ideia. Tome como exemplo uma subclasse Paciente que herda todos os atributos e métodos de uma
superclasse Pessoa, sendo assim, um paciente é um tipo de pessoa, porém
mais especifico, já que além de herdar todos os atributos e métodos definidos
na classe-pai, no caso a classe Pessoa, a Paciente tem seus próprios atributos
e métodos que a diferenciam para o contexto que se predispõe a representar.
Caro leitor, acredito que devidamente apresentado o conceito de herança, é necessário conhecermos a sintaxe para sua utilização na linguagem
Java. O conceito de herança, no qual uma classe estende outra, é realizado
por meio da utilização da palavra reservada extends, conforme é demonstrado hierarquicamente no quadro a seguir.
Capítulo 4 - Herança, polimorfismo e interfacet65
// Classe SuperClasse.java
public class SuperClasse{
//atributos e métodos da superclasse
}
// Classe SubClasse.java
public class SubClasse extends SuperClasse{
//atributos e métodos da subclasse
}
No quadro anterior, é interessante notar que a superclasse não recebe
nenhuma marcação que a diferencia. Porém, repare que para a subclasse, é
definida a herança por meio da palavra reserva extends, que a torna uma
extensão da superclasse. Com isso, podemos dizer que a subclasse, mais específica, possui o comportamento da superclasse.
Outro ponto importante a mencionar, que é oriundo da utilização dos
procedimentos de herança, é a utilização da palavra reservada super. Sua
função é possibilitar à subclasse o acesso à superclasse. Assim, quando for
necessário acessar algum método ou atributo na superclasse, a sintaxe para
tanto será “super.nomeMétodo( )”. Desta forma, você está explicitando
que deseja invocar um método da superclasse. Outro detalhe importante é
quanto a invocar o método construtor da classe base e isto pode ser feito
através da invocação do método “super( )”.
Provavelmente, você ainda não deve ter percebido a importância do
que foi dito até aqui. Pois bem, a importância está no fato de que, com a
utilização dos princípios da herança, não é necessário que seja reescrita boa
parte do código em um programa Java, pois tudo que já foi produzido na
classe-pai fica à disposição da classe-filho. Outro detalhe importante a ser
citado é que uma parte do código, que será comum a várias outras partes de
um programa, pode ser escrita somente uma vez em uma classe-pai, como
mencionamos. Com isso, caso seja necessária a manutenção de alguma parte
desse código, não será necessário que seja alterado em todas as classes-filho,
somente na classe-pai, e as demais subclasses herdarão a alteração. Do ponto
de vista da manutenção do código, isso é um diferencial a ser considerado no
processo de desenvolvimento com a tecnologia Java. Como nos demais conceitos trabalhados até aqui, vamos a um exemplo para fixar esse importante
princípio da orientação a objetos que imita a realidade. Então, transcreva a
classe a seguir.
66 t JAVA com Orientação a Objetos
// Classe Pessoa.java
package consultorio;
public class Pessoa{
// atributos
String nome;
String endereco;
// métodos
public void setNome(String newNome){
this.nome = newNome;
}
public void setEndereco(String newEndereco){
this.endereco = newEndereco;
}
public String getNome(){
return this.nome;
}
public String getEndereco(){
return this.endereco;
}
public void andar(){
System.out.println("Estou andando");
}
}
Em nosso exemplo, foi definida a classe Pessoa, na qual estão detalhadas algumas características e comportamentos inerentes a uma pessoa real.
Feito isto, contextualizando o problema, todos sabemos que um médico nada
mais é que genericamente uma pessoa. Mas, não podemos deixar de considerar que ele, além de suas peculiaridades humanas, possui especificidades
quanto ao exercício da profissão de um médico, que o diferem das demais
pessoas, sendo assim, temos uma especificação da classe Pessoa.
Desta maneira, podemos dizer que um médico tem uma relação “é
um(a)” pessoa, com características e métodos específicos a este e que devem
Capítulo 4 - Herança, polimorfismo e interfacet67
ser considerados. Ao trabalhar com as relações do tipo “é um(a)”, temos uma
chave para identificar e determinar quando uma classe deve ou não ser descendente de outra existente. Para ter uma melhor visualização do processo,
transcreva a classe Medico e note que ela explicita o conceito de herança de
atributos e métodos da classe Pessoa por meio da palavra reservada extends
após o nome da classe.
// Classe Medico.java
package consultorio;
public class Medico extends Pessoa{
// atributos
String horario;
String especialidade;
// métodos
public void setHorario(String newHorario){
this.horario = newHorario;
}
public void setEspecialidade(String newEspecialidade){
this.especialidade = newEspecialidade;
}
public String getHorario(){
return this.horario;
}
public String getEspecialidade(){
return this.especialidade;
}
}
Como dito, note que os atributos e os métodos que definem um médico como pessoa não foram novamente reescritos, pois já foram definidos na
classe Pessoa, sendo reaproveitados pela classe Medico por meio da herança.
Com isso, aquilo que foi definido para certa classe não precisa ser repetido
para uma classe mais especializada originada da primeira, logo, seus atributos e métodos. Desta forma, o paradigma da orientação a objetos auxilia a
68 t JAVA com Orientação a Objetos
reduzir a repetição do código em um programa e em sua manutenção.
Para ter uma melhor visualização prática das vantagens obtidas com a
herança, vamos a um exemplo. Nele, utilizamos as classes criadas anteriormente na definição de um programa executável, no qual é feito o cadastro de
um médico. Note que aqui, começamos a fazer com que a solução de nossos
problemas passe a ter uma característica modular, aumentando o reuso e facilitando posteriormente a manutenção.
// Classe Consultorio.java
package consultorio;
import java.util.Scanner;
public class Consultorio{
public static void main(String[] args){
// instância da classe Medico
Medico novoMedico = new Medico();
Scanner scn = new Scanner(System.in);
// entrada de dados
System.out.println("#####Cadastro Clinico#####");
System.out.println("Entre com o nome do médico:");
novoMedico.setNome(scn.next());
System.out.println("Entre com o endereço do” +
“médico:");
novoMedico.setEndereco(scn.next());
System.out.println("Entre com o horario do” +
“médico:");
novoMedico.setHorario(scn.next());
System.out.println("Entre com a especialidade do” +
”médico:");
novoMedico.setEspecialidade(scn.next());
//impressão dos dados obtidos e mantidos para o
//objeto
System.out.println("\n####DADOS DO MÉDICO####");
System.out.println("Nome do médico:"+novoMedico.
getNome());
System.out.println("Endereço do médico:"+novoMedico.
Capítulo 4 - Herança, polimorfismo e interfacet69
getEndereco());
System.out.println("Horário do médico:"+novoMedico.
getHorario());
System.out.println("Especialidade:"+novoMedico.
getEspecialidade());
}
}
No exemplo anterior, o mais importante a ser mencionado é que, mesmo não definidos na classe Medico, os atributos e os métodos referentes ao
nome e ao endereço, que são oriundos da classe Pessoa, estão disponíveis
para a instância da classe Medico denominada novoMedico. Isto, relembrando, só é possível graças à herança existente na classe Medico da classe Pessoa, evitando a reescrita do código já existente na superclasse Pessoa para a
subclasse Medico.
A utilização do processo de herança também é muito importante
para as estruturas correlatas ou mesmo as definições de melhores práticas,
como no caso de vários padrões do projeto. Fica como sugestão a leitura sobre design pattern, que pode enriquecer seus programas, além de evitar, em
muitos casos, o retrabalho.
Finalizando nossa seção que trata dos princípios da herança na orientação a objetos com Java, é importante citar que a linguagem não suporta a
herança múltipla, assim como o C++. Entretanto, existem alternativas para
lidar com tal limitação, que serão consideradas ainda neste capítulo.
Com isso, chegamos ao final desta seção, na qual trabalhamos a importante característica do paradigma de orientação a objetos, que é a herança. Mas, iremos analisar outra característica não menos importante para a
orientação a objetos baseada na tecnologia Java, o polimorfismo, que consiste em uma estrutura fortemente dependente do processo de herança.
4.2 Polimorfismo
Como já dito na introdução deste capítulo, a palavra polimorfismo advém do grego polimorfos e significa diversas formas. Como observado em sua
essência epistemológica da palavra, o conceito segue os mesmo princípios.
Sendo mais especifico, o polimorfismo nada mais é que a definição de métodos com o mesmo nome, que definem a solução de um mesmo problema,
70 t JAVA com Orientação a Objetos
seja na própria classe, seja na superclasse. O conceito, mais um do paradigma da orientação a objetos, pode ser aplicado de duas formas: sobrecarga ou
redefinição.
Com certeza, a forma mais simples de obter o polimorfismo é por
meio da sobrecarga de métodos ou também conhecido como overload.
4.2.1 Sobrecarga
A sobrecarga, em sua essência, consiste na possibilidade de ter em uma
mesma classe, vários métodos com o mesmo nome, “porém com assinaturas diferentes”. Mas, como assim com assinatura diferente? Certo, se você
se lembra do conceito de assinatura visto em nosso primeiro capítulo, ele é
aplicado aos métodos definindo o nome, tipo de retorno e lista de parâmetros ou argumentos. Neste caso, o que foi citado é que apesar do nome ser
igual, o tipo de retorno ou mesmo a lista de parâmetros deve ser diferente
para um método.
Com isso, os métodos podem possuir o mesmo nome, sendo considerados diferentes por receberem um diferente número ou tipo de parâmetros,
ou mesmo o tipo de retorno pode ser diferente. Este é um conceito muito
simples de ser assimilado e para tanto, vamos utilizar nosso exemplo recorrente da classe Carro, na qual pode acontecer o polimorfismo.
// Classe Carro.java
package fabrica;
public class Carro{
// atributos
Combustivel combustivel = new Combustivel();
// métodos
public void setCombustivel(Combustivel
newCombustivel){
this.combustivel = newCombustivel;
}
public Combustivel getCombustivel(){
return this.combustivel;
}
Capítulo 4 - Herança, polimorfismo e interfacet71
KK U X public void abastecer(Alcool abstAlcool) {
setCombustivel((Combustivel) abstAlcool);
System.out.println(“Abastecido com álcool”);
}
public void abastecer(Gasolina abstGasolina) {
setCombustivel((Combustivel) abstGasolina);
System.out.println(“Abastecido com gasolina”);
}
public void abastecer(BioDiesel abstBioDiesel) {
setCombustivel((Combustivel) abstBioDiesel);
System.out.println(“Abastecido com biodiesel”);
}
}
Aqui, é demonstrado um exemplo de sobrecarga dos métodos de uma
mesma classe, no qual um carro pode ser abastecido de diversas maneiras,
porém a essência da função é a mesma, no caso, abastecer o carro para que ele
possa andar. É importante mencionar que o exemplo apresentado é apenas
ilustrativo, uma vez que não foram apresentadas as classes Combustivel e
suas especificações, no caso, Gasolina, Alcool e BioDiesel, que também foram
utilizadas no exemplo. A ideia aqui é que seja entendida a essência do conceito de polimorfismo e sua abstração do mundo real.
Conforme pode ser observado, em nosso dia a dia, uma mesma funcionalidade pode ser desempenhada de várias maneiras, porém devendo
ser guardadas as devidas especificidades de funcionamento de cada uma. O
exemplo disso é um carro bicombustível. Em nosso exemplo, mais um elemento importantíssimo que foi utilizado deve ser comentado - o cast.
4.2.2 Cast
O cast é a forma mais comum de conversão de tipos, porém sua utilização possui algumas peculiaridades que devem ser observadas na hora da
72 t JAVA com Orientação a Objetos
utilização do recurso. O cast no Java pode ser realizado de duas formas, considerando a conversão dos tipos, sendo: implícito e explícito. Estes recursos,
por sua vez, estão geralmente associados à utilização do conceito de herança,
no qual, como vimos, uma classe-filho (subclasse) é semelhante à classe-pai
(superclasse), da qual foram herdados os atributos e os métodos.
É deste ponto que podemos partir para o entendimento do cast implícito. Neste caso, toda subclasse pode ser associada à declaração de uma
superclasse. Esta é a forma mais simples, pois não há a necessidade de utilização de nenhum recurso adicional para ser efetuado, sendo apenas descrito
por meio da declaração convencional de um objeto da classe-pai e do método
construtor da classe-filho. Para ter uma melhor visualização, vamos a um
exemplo.
Pai objeto = new Filho();
Em nosso exemplo, o objeto é declarado como uma instância da classe-pai, porém ele é ‘construído’ como sendo do tipo da classe filho, ou seja, recebe uma área de memória referente à subclasse. Note que há uma conversão
natural, pois ambos são semelhantes devido ao processo de herança. Grosseiramente, poderíamos dizer que isso é possível, pois a classe-filho pode ser
hierarquicamente inferior à classe-pai, conhece e sabe que ela própria é do
tipo superclasse e, portanto, não precisa necessariamente ser informada que
será convertida. Isto é feito no momento em que utilizamos a palavra extends
na classe-filho, ou seja, a herança.
Já o cast em sua forma explicita, é denominado assim, pois é necessária a informação para a Máquina Virtual Java de qual classe se deseja fazer a
conversão, no caso o cast. Neste caso, de uma classe-pai, tenta-se convertê-la em uma classe-filho. Porém, ao fazer novamente uma análise hierárquica
para ter um melhor entendimento, a superclasse não sabe e em momento
algum é informada quais são suas classes-filho. Portanto, é necessário informar explicitamente em qual tipo de classe-filho a instância da classe-pai será
convertida. Então, caro leitor, aqui o convido a verificar como isso pode ser
realizado na linguagem Java, observando a sintaxe no exemplo a seguir.
Pai objetoPai = new Filho();
Filho objetoFilho = (Filho)objetoPai;
Capítulo 4 - Herança, polimorfismo e interfacet73
Em nosso exemplo, o objeto declarado como objetoPai é instanciado
como sendo do tipo da classe Filho. Logo após, a referência do objetoPai é
convertida por meio de um cast explicito no objetoFilho. Estes conceitos são
importantes para que possamos também conhecer outro importante elemento da linguagem Java - o operador instanceof.
4.2.3 instanceof
O instanceof é um operador utilizado para a realização de casts do tipo
explícito, nos quais não se tem conhecimento de qual classe o define. Aqui,
podemos trabalhar um pouco mais o exemplo apresentado da classe Carro
quanto ao seu abastecimento. Vamos a ele então.
...
public void encherTanque(Combustível c){
if(c instanceof Alcool){
abastecer((Alcool) c);
}else{
abastecer((Gasolina) c);
}
}
...
No código anterior, repare que temos um método ‘encherTanque’ que
recebe como parâmetro um objeto da classe Combustível. Dentro desse método, é verificado, por meio da utilização dos operadores condicionais if/else,
qual o tipo de combustível. Para isso, dentro do teste, é utilizado o operador
instanceof, que realiza a verificação de qual é o combustível selecionado. De
acordo com o valor da instância, é chamado o método abastecer usando a
sobrecarga de métodos.
Simploriamente, instanceof significa “instância de” ou “é do tipo”. Assim, em nossa verificação anterior, no teste condicional o que é feito nada
mais é que uma comparação para definir se o objeto passado como parâmetro
é uma instância da classe Alcool ou da classe Gasolina.
74 t JAVA com Orientação a Objetos
4.2.4 Sobrecarga de construtores
Outro exemplo que pode ser citado para o polimorfismo é a sobrecarga
dos métodos construtores, onde são definidos diversos métodos com o mesmo nome, porém com assinaturas diferentes. Tais considerações já foram
feitas em nosso terceiro capítulo, no qual definimos dois métodos construtores para uma classe, pois, conforme visto, uma vez declarado um construtor, qualquer que seja sua assinatura, passa a ser obrigatória a definição do
método construtor default para sua utilização, já que o método construtor
fornecido pela JVM deixará de ser fornecido.
4.2.5 Redefinição
Nossa segunda forma de utilização do conceito de polimorfismo consiste na redefinição de métodos ou também conhecido como sobrescrita
(override), isso realizado sobre os métodos existentes nas superclasses. Tal
conceito está baseado na herança, da qual as subclasses herdam atributos e
métodos de uma classe-pai. Isso devido ao seguinte fato: Imagine que nem
sempre os métodos fornecidos pela classe-pai são suficientes para resolver os
problemas da subclasse, visto que a classe-filho é uma especialização, tendo
suas particularidades a serem consideradas no processo. É aí que entra a sobrescrita dos métodos, na qual a subclasse reescreve um método utilizando a
mesma assinatura definida na classe-pai, sendo que no momento da invocação do método, dentro da subclasse, será acionado o método ali definido que
se sobrepõe ao da superclasse. Vamos a um exemplo: imagine um pai e um
filho, ambos são pessoas e assim, possuem características e comportamentos
de um ser humano. É natural que o filho herde os trejeitos de seu pai, porém
existem características e comportamentos que, por mais inerentes ao processo de herança que sejam, precisam ser especializados, ou seja, por mais que
exista o processo de herança, ao falar, por exemplo, o filho possui especificidades em sua voz que são só suas, apesar da aproximada semelhança com a
de seu pai.
Para o paradigma da orientação a objetos, uma operação, ação ou mesmo uma transformação que um objeto realiza por meio dos métodos são implementações específicas das operações desejadas para uma classe. Então,
mesmo que esta venha a herdar os atributos e os métodos de outra classe,
sempre terão prioridade as características e os comportamentos definidos
Capítulo 4 - Herança, polimorfismo e interfacet75
para o escopo específico da classe. Para ter um melhor entendimento, vamos
a uma implementação. Altere a classe Medico do exemplo apresentado na
seção anterior, no qual essa classe herda os atributos e os métodos da classe
Pessoa.
// Classe Medico.java
package consultorio;
public class Medico extends Pessoa{
// atributos
String horario;
String especialidade;
// métodos
public void setHorario(String newHorario){
this.horario = newHorario;
}
public void setEspecialidade(String newEspecialidade)
{
this.especialidade = newEspecialidade;
}
public String getHorario(){
return this.horario;
}
public String getEspecialidade(){
return this.especialidade;
}
KK YZ X public void andar(){
System.out.println("Estou andando rápido");
}
}
Note que no exemplo apresentado, foi criado na classe Medico o método andar( ). Tal método já havia sido definido na classe Pessoa, na seção
anterior, e devido ao processo de herança existente entre as classes Pessoa e
76 t JAVA com Orientação a Objetos
Medico, não foi necessária sua reescrita na classe filho, ou seja, na classe Medico. Porém, devido às características específicas da classe Medico, esse método é redefinido e, então, passa a atender as particularidades dessa classe.
Assim, a máquina virtual Java entenderá que ao ser invocado um método, no
qual houve o processo de redefinição, o método da subclasse será o que deve
ser acionado. Caro leitor, acredito que até aqui você deve ter verificado que o
conceito de polimorfismo é importantíssimo para o paradigma de orientação
a objetos, assim como os demais mecanismos de abstração de dados, herança
e encapsulamento já apresentados.
4.3 Interface
Amigo leitor, agora que já conhecemos os conceitos de herança e polimorfismo, vamos a outro conceito-chave no paradigma da orientação a objetos - as interfaces. O conceito de interface é um tanto amplo, já que ao
trabalharmos com o Java, ele está constantemente presente. De um lado,
como vimos, toda classe no Java tem um propósito específico, geralmente se
relaciona com outras classes e aí, temos um sistema completo e funcional que
se comunica por meio de mensagens. Essas mensagens são trabalhadas por
meio dos métodos definidos segundo os modificadores de acesso e tais métodos são interfaces (entradas) para o acesso ao conteúdo de uma instância.
Mas, a tecnologia Java ampliou o conceito, tornando-o ainda mais
flexível. Poderíamos dizer grosseiramente que a tecnologia Java possui um
tipo especial de classe que são as interfaces. Isso ocorre devido à sintaxe de
definição de uma interface no Java ser semelhante à utilizada para uma classe. Entretanto, no lugar do identificador class, deve ser utilizada a palavra
reservada interface.
A ideia central do conceito de interface é a modelagem dos comportamentos que são esperados na definição de uma determinada classe. O nome
interface coerentemente tem o objetivo de explicitar que esse conceito Java
pretende disponibilizar um meio padronizado de acesso a diferentes tipos de
implementação como uma interface propriamente dita.
Para isso, em uma interface são definidos apenas métodos abstratos
e variáveis finais. Um ponto importante a ser destacado é o fato de uma
interface não ter uma implementação para seus métodos ou mesmo instâncias para suas variáveis. Logo, a utilização das interfaces garante que
você sempre se concentre nos objetos e nos relacionamentos que existirão,
Capítulo 4 - Herança, polimorfismo e interfacet77
construindo, desta forma, uma espécie de planta baixa do sistema para possibilitar a construção baseada nos modelos existentes, definindo assim uma
camada extra de abstração para seu sistema que servirá de base para o restante. O código a seguir apresenta a sintaxe para a definição de interfaces
nos Java.
// Interface Aluno.java
package escola;
public interface Aluno{
!# ^`|~~U€~€ ‚
!# # &'
public abstract int faltas();
!# # & '
!# # ƒ& '
}
Algumas considerações devem ser feitas e estar sempre em mente
quanto à utilização das interfaces. Uma interface não pode ser instanciada, sendo que seu objetivo é definir um modelo de comportamento abstrato para as classes. Tais classes que se propõem a implementar a interface
devem fornecer a implementação dos métodos declarados na interface ou,
ao menos, declará-los em seu escopo. Uma boa analogia para as interfaces é
imaginá-las como sendo um contrato, no qual qualquer classe que se propõe
a implementar a interface deve, ao menos, declarar métodos e variáveis estáticas e finais em seu escopo.
Note que no exemplo anterior, é feita a utilização da palavra reservada abstract. Ela pode referenciar tanto métodos quanto classes ao apresentar apenas a ideia ou mesmo um modelo do todo, sem que seja fornecida a
implementação para tais métodos ou classe, deixando explícita somente a
abstração do objeto. O conceito de abstração muitas vezes se confunde com o
conceito de interface. A função das classes abstratas é forçar o programador a
implementar subclasses para a resolução dos problemas. Assim, os métodos
da classe abstrata são declarados com o modificador abstract e sem corpo.
Voltando ao conceito de interface, para que uma classe implemente uma interface, em sua definição deve ser colocado o identificador
78 t JAVA com Orientação a Objetos
implements seguido da lista de interfaces separadas por vírgulas. Vamos a
um exemplo de uma classe que implementa a interface aluno definida no
quadro anterior.
// Classe AlunoImp.java
package escola;
public class AlunoImp implements Aluno{
// atributos
!" „ ƒ
private int faltas;
// métodos
!# &'
return nota1(nota1)+nota2(nota2)/2;
}
public int faltas(){
return faltas;
}
!# & '
this.nota1 = nota;
return nota1;
}
!# ƒ& '
this.nota2 = nota;
return nota2;
}
}
Caro leitor, vamos entender o exemplo apresentado no quadro anterior. Conforme pode ser observado, a classe AlunoImp implementa a
interface Aluno e, então, deve fornecer uma implementação para os métodos declarados na interface Aluno, que foi construída anteriormente. Além
de fornecer a ideia de um contrato entre as classes, conforme mencionado,
outro aspecto que pode ser explorado no conceito de interface, consiste
na ideia da plugabilidade. Vamos a um exemplo para deixar tal perspectiva
Capítulo 4 - Herança, polimorfismo e interfacet79
mais clara. Imagine uma classe que utiliza uma determinada interface que
disponibiliza os métodos abstratos para a conexão com um banco de dados.
Assim, existem diversas formas de implementar uma conexão. Logo, podemos ter uma interface que defina todas as maneiras como uma conexão
pode ser implementada, cabendo ao desenvolvedor plugar o método que lhe
for mais conveniente, isso sem a necessidade de alterações drásticas no programa, uma vez que todas as classes que implementam tal interface para a
conexão, de alguma maneira, deverá comprometer-se a fornecer todos os
métodos, como em um contrato, independentemente da forma como será
implementado cada método.
Vamos a outro exemplo clássico e bem simples para o entendimento da
ideia de plugabilidade, que é elemento central em vários padrões de projetos
utilizados. Pois bem, imagine uma empresa de software que possui um programa de controle médico. Este pode ser aplicado tanto a clínicas para tratar
pessoas quanto a clínicas veterinárias. Tal sistema é relativamente simples e
realiza apenas o controle de pacientes. Verificando o funcionamento de ambas as clínicas, os processos são idênticos, apesar de lidar com pacientes completamente diferentes. Resumindo, o sistema pode ser utilizado em ambos
os casos. Mas, como? No caso, poderia ser criada uma interface Paciente que
definiria os métodos comuns aos pacientes, restando as classes que implementam tal interface. Então, observe o código a seguir.
// Interface Paciente.java
package clinica;
public interface Paciente {
public void setNome(String nome);
public void setHistorico(String historico);
public String getNome();
public String getHistorico();
}
Observe a seguir que nossas classes Pessoa e Animal, que implementam a interface Paciente, a partir deste momento deveriam ter as seguintes
definições obrigatoriamente.
80 t JAVA com Orientação a Objetos
// Pessoa.java
package clinica;
public class Pessoa implements Paciente {
// atributos
private String nome;
private String historico;
// Métodos
public void setNome(String nome) {
this.nome = nome;
}
public void setHistorico(String historico) {
this.historico = historico;
}
public String getNome(){
return nome;
}
public String getHistorico(){
return historico;
}
}
Definida a classe Pessoa, para a classe Animal teríamos uma semelhança, uma vez que ela deve também implementar a interface Paciente.
// Animal.java
package clinica;
public class Animal implements Paciente {
// atributos
private String nome;
private String historico;
// Métodos
public void setNome(String nome) {
this.nome = nome;
Capítulo 4 - Herança, polimorfismo e interfacet81
}
public void setHistorico(String historico) {
this.historico = historico;
}
public String getNome(){
return nome;
}
public String getHistorico(){
return historico;
}
}
Isso permite uma padronização dos procedimentos ou mesmo que
o código possa ser utilizado duas ou mais vezes, dando extensibilidade ao
programa, bastando apenas que seja construída uma nova classe que se proponha a implementar a interface e assim, que se possam utilizar todos os
demais benefícios de seguir tais regras. Para tanto, teríamos como declaração
dos objetos das classes, por exemplo.
// Clinica.java
package clinica;
public class Clinica {
public static void main(String[] args){
Paciente p = new Pessoa();
Paciente a = new Animal();
// Dados para a pessoa
p.setNome(“Alex Coelho”);
p.setHistorico(“Apresentou problemas na” +
“garganta.”);
// Dados para o animal
a.setNome(“Totó”);
a.setHistorico(“Problemas na pata.”);
// Imprimindo os relatórios
82 t JAVA com Orientação a Objetos
Relatorios r = new Relatorios();
r.imprimirRelatorio(p);
r.imprimirRelatorio(a);
}
}
Note que em ambos os casos, o espaços de memória construídos são
diferentes, um para a pessoa ou para o animal. Porém, quanto à sua definição, são do mesmo tipo, no caso instâncias da interface Paciente. Repare.
Paciente p = new Pessoa();
Paciente a = new Animal();
Isso nos garante uma padronização nos processos, fazendo com que
outras funcionalidades do sistema possam ser utilizadas de forma extensível
a vários produtos. No nosso caso específico, a vantagem de utilizar a interface
Paciente se caracterizaria na utilização dos procedimentos de geração de relatório, por exemplo, como é apresentado no código a seguir e já referenciado
no exemplo anterior.
// Relatorios.java
package clinica;
public class Relatorios {
public void imprimirRelatorio(Paciente p) {
System.out.println(“Relatório de Pacientes”);
System.out.println(“Nome:”+p.getNome());
System.out.println(“Histórico:”
+p.getHistorico());
System.out.println(“Histórico:”
+p.getCadastro());
}
}
Para finalizarmos nosso capitulo, vamos a outra funcionalidade que o
conceito de interface possibilita, sendo a manipulação da herança múltipla
no Java.
Capítulo 4 - Herança, polimorfismo e interfacet83
4.3.1 Herança múltipla
Embora a linguagem Java não forneça um mecanismo explícito que
possibilite a herança múltipla, ou seja, uma classe herdar de mais de uma
classe, com a utilização de interfaces é possível obter algo próximo da essência do conceito, com a implementação de diversas interfaces por uma classe.
Como uma classe pode implementar diversas interfaces e deve prover
implementações para os métodos declarados nas interfaces, obtém-se uma
pseudosobrescrita dos métodos e com isso, ela disponibiliza todos os métodos e atributos para as classes que a utilizam. Para ter um melhor entendimento, vamos a mais uma analogia. Um exemplo clássico é o do carro anfíbio, que possui características e comportamentos tanto de um carro quanto
de um barco. Então, nosso primeiro passo é criar as interfaces que definem
os métodos.
// Carro.java
package estaleiro;
public interface Carro {
public void puxarFreioDeMao();
}
% †„ ‡ ?
na herança múltipla.
// Barco.java
package estaleiro;
public interface Barco {
public void navegar();
}
Feito isso, agora é necessário criar a nossa classe CarroAnfibio que
irá implementar as interfaces propostas e com isso, passará a ser obrigada
a reescrever os métodos propostos nas interfaces. Assim, vamos ao que
interessa. Transcreva o código a seguir e analise sua utilização.
84 t JAVA com Orientação a Objetos
KK ~#ˆ‰"
package estaleiro;
!# ~# implements Carro, Barco {
public void puxarFreioDeMao() {
System.out.println(“Puxou o freio de mão!”);
}
public void navegar() {
System.out.println(“Navegando!”);
}
}
Repare no exemplo que com isso, garantimos o comportamento ambíguo do objeto real com a utilização das interfaces que mapearam o problema.
Acredito que com isso, você tenha conseguido visualizar toda a vantagem de
utilizar interfaces, que consiste na definição de um protocolo que seja comum entre as classes, além de criar uma especificação do que uma classe
deverá oferecer e implementar em termos de métodos, o que resulta numa
forma de abstração.
Então pessoal, chegamos ao final dos principais conceitos do paradigma de programação orientada a objetos, porém isso não significa o final de
nossa jornada. Nos próximos capítulos, iremos conhecer ferramentas interessantes que irão possibilitar a você, caro leitor, ter um melhor aproveitamento da linguagem Java e dos conceitos do paradigma de programação
orientada a objetos. Assim como nos capítulos anteriores, aproveite para refazer todos os exemplos propostos.
Capítulo 5
Ferramentas úteis para o dia a dia
Neste capítulo, iremos considerar diversas ferramentas para um programador Java. Elencar quais seriam as prioridades de um programador em
seu cotidiano não é algo tão simples assim, porém com certeza a manipulação de strings, trabalho com datas e horas, definição de operações matemáticas e manipulação de vetores especiais estão entre as funções que mais se
destacam. Assim como mencionado no início de nosso livro, tudo no Java
consiste em classes, exceto os tipos primitivos. Relembrando nossos tipos
primitivos: int, float, boolean etc. Nossas classes derivam da classe Object,
que tem as definições padrão para qualquer classe. Logo, todas as funcionalidades e operações que são fornecidas pela biblioteca padrão da tecnologia
Java são definidas em classes que operam por meio de seus métodos. Diante
disso, iremos abordar como a linguagem Java possibilita o trabalho com estas importantes ferramentas para o dia a dia.
5.1 Manipulação de strings
Como já vimos desde o início de nossa leitura, a classe String trata-se
de uma das mais utilizadas, ao considerarmos que no cotidiano computacional, em sua maioria, as operações dependem deste tipo de dado em sua entrada. Pois bem, diversas linguagens apresentam formas diferentes de lidar
com as strings, seja no formato de um vetor de caracteres, seja mesmo fornecendo uma operação responsável por desempenhar a função de elemento
agregador destes.
A tecnologia Java fornece a classe String para a realização de operações e a manipulação de palavras e frases. Como você deve já ter percebido,
diferentemente dos tipos primitivos, tais como int, float e assim por diante,
sempre que instanciamos um objeto do tipo String, com ‘S’ maiúsculo em
sua inicial, isso denota, segundo a convenção existente entre os desenvolvedores Java e já conhecida por você, que se trata de uma classe. Por se tratar
de uma classe, ela nos fornece uma gama de serviços que podem ser acionados por meio de seus métodos. São tais métodos que tornam as classes
86 t JAVA com Orientação a Objetos
diferenciadas dos tipos primitivos, como já citado. A tecnologia Java fornece
classes que também auxiliam na manipulação dos principais tipos, como, por
exemplo, Int, Float, classe Boolean, entre outras. Repare que aqui temos o
mesmo padrão: todas as iniciais são maiúsculas, logo, todas são classes que
agregam valores aos tipos de dados por meio de métodos.
Mas iremos apenas nos concentrar aqui na manipulação de strings
com as principais operações possíveis utilizando o Java. Desta forma, conheceremos os principais métodos disponibilizados pela classe String, começando pela utilização de seus métodos construtores.
5.1.1 Construtores da classe String
Assim como em todas as classes Java, é necessária a existência de um
método construtor responsável por garantir a instanciação dos objetos do
tipo String. Pois bem, a classe String fornece dois métodos construtores para
a instanciação de seus objetos, sendo um parametrizado e outro default. Outra forma, no entanto, de realizar a criação de nosso objeto é por meio da passagem de valor explícita, ao qual o objeto fará sua referência. Vamos observar
como pode ser realizada a criação de nossos objetos da classe String.
String s = “Palavra”;
String s1 = new String();
String s2 = new String(“Palavra”);
Repare que tudo que foi apresentado no quadro anterior não é novidade. Vários exemplos já apresentados a você, leitor, nos demais capítulos fizeram menção a alguma das possibilidades de criação ou instanciação de objetos da classe String. É vital para seus programas que o objeto seja construído
com algumas das possibilidades mostradas, garantindo que não ocorra erro
de pontos nulos, sem uma referência de memória.
5.1.2 Outros métodos
Considerando inicializados nossos objetos, vários métodos são úteis
para a manipulação de Strings, como segue na Tabela 5. Consideremos um
objeto ‘s’ do tipo String para a sintaxe.
Capítulo 5 - Ferramentas úteis para o dia a diat87
Tabela 5. Principais métodos da classe String.
Método
Função
Sintaxe
length( )
Retorna o número de caracteres da
string.
Int tamanho = s.lenght( );
charAt(int)
Captura um caractere na posição
especificada na string.
char caractere = s.charAt(3);
equals(String)
Compara duas strings e retorna um
valor booleano (verdadeiro ou falso)
Boolean iguais = s.equals(s1);
equalsIgnoreCase(String)
Compara duas strings ignorando a
diferença entre minúsculas e maiúsculas e retornando valores booleanos.
Boolean iguais =
s.equalsIgnoreCase(s1);
compareTo(String)
Compara duas strings e retorna 0
se ambas forem iguais. Se a string
que chama o método for menor que
a passada como parâmetro, será retornado um número negativo e caso
contrário, um número positivo.
Int result = s.compareTo(s1);
substring(int)
substring(int, int)
Retorna um novo objeto String a
partir do ponto especificado em
valor inteiro ou, então, delimitando a
posição inicial e final.
indexOf(char)
Retorna a posição da primeira ocorrência do caractere passado como
parâmetro na string que invocou o
método.
String nova = s.substring(3);
String nova =
s.substring(3,5);
Int posicao = s.indexOf(‘A’);
88 t JAVA com Orientação a Objetos
toUpperCase( )
Retorna um novo objeto String com
todos os valores em maiúsculo.
String nova = s.toUpperCase(
);
toLowerCase( )
Retorna um novo objeto String com
todos os valores em minúsculo.
String nova =
s2.toLowerCase( );
Amigo leitor, como você deve ter percebido, a classe String fornece diversas possibilidades que podem tornar a vida do programador mais fácil.
Outros métodos da classe String poderiam ser considerados aqui. Assim, fica
como dica que você verifique na documentação oficial da classe String as demais possibilidades para a manipulação das strings.
Porém, nem só de strings vive um programa, outras classes devem ser
consideradas como elementos comuns e interessantes para o cotidiano de
um desenvolvedor Java. Entre tais classes, podemos citar as classes que auxiliam na manipulação de datas e horas, sendo nosso próximo assunto.
5.2 Data e hora
Um dos elementos que mais geram problemas e controvérsias na
programação Java, com certeza consiste na manipulação de datas e horas.
Isso pode tomar proporções ainda maiores quando falamos de aplicações que
irão rodar e depender da Internet e de seus diversos servidores espalhados
pelo mundo.
Considerando tais problemas, a tecnologia Java fornece um conjunto de classes que, conforme já vimos, disponibilizam uma grande variedade
de métodos que possibilitam o trabalho com datas e horas. As principais classes que são utilizadas para a manipulação de datas são: java.util.Date, java.
util.Calendar e java.util.GregorianCalendar. É importante mencionar que a
classe Date está em desuso e, portanto, em processo de depreciação, o que
pode fazer com que nas próximas versões da máquina virtual Java, ela não
seja mais fornecida e os sistemas que a utilizam tenham problemas devido à
sua falta. Mas, ainda assim, é importante apresentá-la ao considerarmos que
diversos sistemas ainda a utilizam. Certo, então, nada melhor que apresentar
sua utilização com um código. Logo, transcreva o código a seguir e execute-o.
Capítulo 5 - Ferramentas úteis para o dia a diat89
// ExemploData.java
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.GregorianCalendar;
public class ExemploData {
public static void main(String[] args) {
Date d = GregorianCalendar.getInstance().
getTime();
SimpleDateFormat format = new SimpleDateFormat();
System.out.println(format.format(d));
}
}
Com a execução do programa, você deve ter percebido outra possibilidade da classe Date, que consiste na manipulação de informações sobre o
tempo, tais como: hora, minutos, segundos e milissegundos. No exemplo
apresentado, note que a data impressa segue o padrão americano e para
isso, é necessária a manipulação de sua formatação, que é feita no método
construtor da classe SimpleDateFormat. Essa classe fornece um conjunto de
caracteres padrão para a formatação do objeto Date. Podemos citar alguns
exemplos de formatações de datas, como a seguir.
dd/MM/yy = 21/08/11
dd/MMM/yyyy = 21/AGO/2011
Para obtermos a formatação no padrão brasileiro, criaremos uma classe alterada no trecho que se refere ao método construtor da classe SimpleDateFormat,
como apresentado a seguir. Execute o código e verifique sua saída.
// Data.java
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.GregorianCalendar;
public class Data {
90 t JAVA com Orientação a Objetos
public static void main(String[] args) {
Date d = GregorianCalendar.getInstance().getTime();
SimpleDateFormat format = new
SimpleDateFormat(“dd/MM/yy”);
System.out.println(format.format(d));
}
}
É importante mencionar aqui que a classe SimpleDateFormat conta
com a utilização de diversos caracteres que auxiliam na formatação para a
manipulação de datas e as mesmas, estão disponíveis em sua documentação
padrão. Outra maneira de obtermos a data pode ser com a utilização das classes DateFormat e Locale.
A classe Locale é importante, pois auxilia na designação do local e no
processo de alteração dos padrões regionais. A mesma já vem com diversas
regiões preconfiguradas como constantes, que apenas necessitam ser determinadas no momento de sua implementação. Já a classe DateFormat, define
alguns padrões que podem ser aplicados a uma data ou hora. Vamos a um
exemplo para sua fixação.
// DataFormatada.java
import java.text.DateFormat;
import java.util.Locale;
import java.util.Date;
public class DataFormatada {
public static void main(String args[]){
Date d = new Date();
Locale local = new Locale («pt»,»BR»);
DateFormat df = DateFormat.getDateInstance(
DateFormat.LONG, local);
System.out.println(“Hoje são: “+ df.format(d));
}
}
Porém, como mencionado, a classe Date está em processo de descontinuação e dessa forma, a tecnologia Java passa a fornecer outras classes para
Capítulo 5 - Ferramentas úteis para o dia a diat91
a manipulação de datas. A principal, com certeza, consiste na Calendar. A
classe Calendar consiste em uma classe abstrata, ou seja, contém as abstrações que definem uma data e hora, e desta forma, disponibiliza atributos e
métodos para a manipulação de datas, uma vez que a data e a hora existem
em qualquer lugar. Mas, o fato de ser uma classe abstrata não permite a instanciação direta por meio do operador new. Assim, para instanciar um objeto Calendar, caro leitor, você deve utilizar o método estático sobrecarregado
getInstance( ), que na maioria das vezes é herdado da classe GregorianCalendar.
A classe Calendar permite que várias operações sejam aplicadas a uma data,
enriquecendo o que já é ofertado pela classe Date. Para entender sua manipulação, vamos a um exemplo.
// ExemploDataCalendar.java
import java.text.DateFormat;
import java.util.Calendar;
import java.util.Date;
public class ExemploDataCalendar {
public static void main(String args[]){
Date d = new Date();
Calendar calendario = Calendar.getInstance();
calendario.setTime(d);
DateFormat df = DateFormat.getDateInstance(
DateFormat.SHORT);
System.out.println( df.format(calendario.getTime()));
}
}
Repare que no exemplo, utilizamos uma instância da classe Date para
definirmos a data atual passada pelo sistema, porém poderíamos também
ter, ao invés de utilizado o objeto do tipo Date, definido por meio de String
os diversos parâmetros de uma data ou mesmo hora. Segue um exemplo para
a visualização.
92 t JAVA com Orientação a Objetos
...
calendario.set(ano,mês,dia);
calendario.set(ano,mês,dia,hora,minuto);
calendario.set(ano,mês,dia,hora,minuto,segundo);
...
Porém, nem sempre é interessante utilizar a classe Date, dada sua atual situação, sendo que a própria classe Calendar com a utilização do método
getInstance( ) já fornece todo o suporte necessário para a aquisição da data.
Além disso, o objeto do tipo Calendar, no nosso caso, calendário, pode ser
utilizado para a manipulação dos elementos vinculados à data por meio de
suas constantes, além de realizar, como já dito, operações como, por exemplo, a soma ou a subtração dos dias em uma data. Como nos demais tópicos
trabalhados até aqui, vamos a mais um exemplo.
// DataCalendario.java
import java.text.DateFormat;
import java.util.Calendar;
import java.util.Locale;
public class DataCalendario {
public static void main(String args[]){
Calendar calendario = Calendar.getInstance();
DateFormat df = DateFormat.getDateInstance(
DateFormat.SHORT, Locale.UK);
System.out.println(df.format
(calendario.getTime()));
System.out.println(Calendar.DAY_OF_MONTH);
System.out.println(Calendar.DAY_OF_WEEK);
System.out.println(Calendar.DAY_OF_YEAR);
calendario.add(Calendar.DAY_OF_YEAR,10);
//adiciona 10 dias à data atual
calendario.add(Calendar.MONTH,5);
//adiciona 5 meses à data atual
calendario.add(Calendar.YEAR,2);
//adiciona 2 anos à data atual
System.out.println(df.format
(calendario.getTime()));
Capítulo 5 - Ferramentas úteis para o dia a diat93
}
}
No exemplo anterior, utilizamos duas classes conhecidas, DateFormat
e Locale. Note que utilizamos a constante “Locale.UK” que define que seguiremos os aspectos regionais da Grã-Bretanha. Logo depois, é feita a impressão da data e algumas operações para, então, termos uma nova impressão da
data com os valores alterados. É claro que existem diversos outros métodos
e constantes que a classe Calendar oferta e, logo, para serem obtidas maiores
explicações, podem ser acessadas na documentação oficial.
5.3 Operações matemáticas
Outra classe importante para o cotidiano de um programador Java
é a java.lang.Math. Ela disponibiliza diversos métodos e constantes para as
operações matemáticas que podem ser acessadas de maneira estática, ou
seja, não é necessário realizar a instanciação de um objeto. Exemplos das
diversas possibilidades são as constantes π (pi) e ln (base dos logaritmos naturais, também chamado de número de Euler ou logaritmo neperiano), sendo respectivamente 3,141592653589793 e 2.718281828459045. Essas duas
constantes podem ser acessadas conforme o exemplo a seguir explicita.
// Matematica.java
public class Matematica {
public static void main(String args[]){
// Cálculo do comprimento de um círculo
‘
double comp = 2 * raio * Math.PI;
System.out.println(comp);
// Logaritmo neperiano
System.out.println(Math.E);
}
}
Foram apresentadas no código as constantes “Math.PI” e “Math.E”
que, conforme já mencionado, simplificam a utilização dos valores para π
94 t JAVA com Orientação a Objetos
e ln. Porém, nem só de constantes vivem as operações matemáticas e para
isso, a classe Math disponibiliza vários métodos que auxiliam nas mais diversas operações. Entre os principais métodos ofertados pela classe, temos a
comparação de valores maiores e menores, bem como operações como potências e raízes para os cálculos de Trigonometria. Considerando isso, vamos à
apresentação de métodos para uma comparação para o conhecimento de sua
sintaxe.
// ComparaValores.java
public class CamparaValores {
public static void main(String[] args) {
"
„ ‘ ’
System.out.println(“O maior valor é “
+ Math.max(valores1[0], valores1[1]));
System.out.println(“O menor valor é “
+ Math.min(valores1[0], valores1[1]));
}
}
Verifique que no exemplo, foi realizada a comparação de valores, sejam eles máximos, sejam mínimos por métodos da classe Math. Porém, a
maioria dos métodos fornecidos pela classe Math auxilia na manipulação dos
valores trigonométricos. Para verificarmos a utilização dos métodos da classe Math para a Trigonometria, nada mais interessante que utilizar o clássico
exemplo da hipotenusa. Logo, transcreva o código e execute-o.
// Hipot.java
public class Hipot{
public static void main(String args[]) {
double p1 = 12;
double p2 = 16;
System.out.println(“A distancia entre os pontos é “
+ Math.hypot(p1, p2));
}
}
Capítulo 5 - Ferramentas úteis para o dia a diat95
Outros métodos, como os que calculam o seno, cosseno e tangente,
além de suas variações, são fornecidos pela classe Math. Finalizamos nossa
análise sobre os métodos da classe Math com um dos mais utilizados e importantes, que é o “Math.random( )”. O método trabalha com valores randômicos que variam de 0.0 até 1.0. Em geral, é necessária a manipulação de tais
valores para se obter sua utilidade para a criação de aplicações do dia a dia,
como podemos perceber no exemplo a seguir.
// NumeroRandomico.java
public class NumeroRandomico {
public static void main(String[] args) {
int valorMin = 0;
int valorMax = 10;
int ranger = valorMax - valorMin;
double valorRandomico;
for (int i = 0; i < 10; i++) {
valorRandomico = Math.random();
System.out.println(“O número entre”
+valorMin+” e “+valorMax+” é:”
+ Math.round(
valorMin + valorRandomico * ranger));
}
}
}
No exemplo, definimos os valores mínimos e máximos que definem
o limite para os valores randômicos que queremos. Logo após, criamos uma
variável que recebe o valor randômico. Finalizando, temos um laço de repetição, no qual realizamos o arredondamento da transformação dos valores
fracionados. É importante mencionar que existem outros métodos da classe
Math que podem ser utilizados para as operações matemáticas que agregam
valor às suas aplicações.
5.4 Trabalho com vetores especiais
Como temos visto desde o início de nosso trabalho, a tecnologia Java
96 t JAVA com Orientação a Objetos
se destaca claramente das outras devido à sua diversidade. Diante isso, outra
ferramenta que deve ser mencionada, entre as diversas que a linguagem Java
disponibiliza, consiste nas classes para o trabalho com vetores.
Em sua maioria, as diversas linguagens de programação disponíveis no
mercado trabalham com vetores convencionais que delimitam um tamanho
fixo. Isto é, impedem que estes sofram alteração de tamanho durante a execução, como, por exemplo, os que foram desenvolvidos em nosso primeiro
capítulo.
A linguagem Java fornece subsídios adicionais para a manipulação dos
vetores, resolvendo problemas como o citado, no caso, a alteração de tamanho durante a execução. Além disso, disponibiliza classes para as operações
de nossos vetores estáticos. Isso tudo obviamente é feito por um conjunto
de classes.
5.4.1 Classe Vector
Entre estas, temos a Vector, que possibilita a criação de uma estrutura
de dados semelhante aos vetores convencionais, porém os objetos instanciados com a utilização dessa classe podem ser redimensionados dinamicamente durante a execução. Logo, a qualquer momento, quando é necessário
mais espaço para o armazenamento, o próprio sistema, por meio da máquina
virtual, encarrega-se de dobrar o tamanho do espaço inicialmente alocado
para o vetor, no caso, um objeto da classe Vector.
Pois bem, outra característica, que deve ser considerada e que torna a
classe Vector diferenciada, está na capacidade de armazenar qualquer tipo
de entidade, independentemente de seu tipo. Isto se dá devido ao fato da
classe Vector fazer o armazenamento de referências para Object. Logo, como
já sabemos, tudo no Java consiste nas classes que herdam de Object e isso,
garante que possamos trabalhar com qualquer que seja o tipo do elemento
a ser armazenado no vetor. Então, nada como a prática para entendermos
melhor os conceitos apresentados. Vamos a eles.
// Classe ExemploVector.java
import java.util.Vector;
public class ExemploVector{
public static void main(String[] args){
Capítulo 5 - Ferramentas úteis para o dia a diat97
Vector vetor = new Vector();
Carro mercedez = new Carro();
vetor.add(mercedez);
Bola bolaFutebol = new Bola();
vetor.add(bolaFutebol);
if(!vetor.isEmpty()){
for (int i = 0; i < vetor.size(); i++) {
Object object = vetor.elementAt(i);
}
System.out.println(vetor.size());
}
}
}
No exemplo anterior, um detalhe a ser observado está no fato de que,
diferentemente dos vetores trabalhados até aqui, não definimos o tamanho de nosso vetor para a variável ‘vetor’. Isso graças à capacidade da classe
Vector que determina o tamanho a ser alocado durante a execução, sendo
que isso é ideal para a manipulação de grandes vetores, bem como estruturas
heterogêneas. Por ser uma classe, Vector disponibiliza para seus objetos, no
caso, vetores especiais, funcionalidades que não são comuns aos vetores tradicionais, tais como, a verificação do tamanho do vetor ou mesmo se eles estão vazios, conforme foi apresentado no exemplo, através dos métodos size( ) e
isEmpty( ).
Entretanto, isto não elimina um dos maiores problemas do trabalho
com vetores, que é sua navegação e busca. Imagine um vetor que cresce e tem
um tamanho de um milhão de posíções. No caso, isso tornaria o processo
de busca computacionalmente inviável, já que para encontrar um elemento,
seria necessário percorrer o vetor. Isto se torna mais crítico, caso o objeto
procurado esteja no final do vetor.
Para solucionar esse problema, a linguagem Java fornece outra classe
para o trabalho com vetores - a HashMap.
98 t JAVA com Orientação a Objetos
5.4.2 Classe HashMap
Seu diferencial está no fato de que ela permite a associação de índices
que identificam e tornam o objeto único dentro do vetor. Tal índice se torna
uma chave para a recuperação do valor de maneira pontual. Isto garante que
possa ser realizada uma consulta de um vetor de um para um. Obviamente,
isto é bem diferente do que ocorria com a definição dos vetores estáticos vistos em nosso primeiro capítulo ou mesmo nos definidos com a classe Vector.
Depois destas considerações, vamos a um exemplo com a classe HashMap.
// Classe NovoVetor.java
import java.util.HashMap;
public class NovoVetor{
public static void main(String[] args) {
HashMap pessoas = new HashMap();
Pessoa newPessoa = new Pessoa();
newPessoa.nome = "Fulano da Silva";
String chave = "Fulano";
pessoas.put(chave, newPessoa);
Pessoa outraPessoa = (Pessoa)pessoas.get(chave);
System.out.println("Nome recuperado: "+
outraPessoa.nome);
}
}
Note que no exemplo anterior, foi instanciado um objeto do tipo
HashMap denominado pessoas, no caso um vetor, e por meio do método
put( ), foi armazenado um objeto da classe Pessoa. Repare que foi necessária
a atribuíção de uma chave para a identificação do objeto, que será utilizado
em uma posterior recuperação. Isto, por sua vez, é feito com a utilização do
método get( ) da classe HashMap, como visto no código anterior.
É importante ainda mencionar que o toolkit Java possui ainda uma
classe que disponibiliza métodos para a realização de operações nos vetores.
Essa classe não é denominada por acaso de Arrays.
Capítulo 5 - Ferramentas úteis para o dia a diat99
5.4.3 Classe Arrays
A classe Arrays fornece suporte para as principais manipulações que
um vetor pode sofrer, no caso a comparação, ordenação ou mesmo pesquisas
binárias, além de outras. Para verificarmos o funcionamento da classe Array,
transcreva o código e execute para verificar seu resultado.
// Classe ManipulandoArrays.java
import java.util.Arrays;
public class ManipulandoArrays {
public static void main(String[] args) {
String[] nomes = new String[3];
nomes[0]= "Fulano";
nomes[1]= "Ciclano";
nomes[2]= "Beltrano";
Arrays.sort(nomes);
for(int i=0;i<nomes.length;i++){
System.out.println(nomes[i]);
}
}
}
A classe Array, como pode ser observado, consiste em uma classe que
manipula vetores estáticos. Além disso, é uma classe abstrata que não precisa
ser instanciada para a utilização, sendo utilizada sua própria referência. Ela,
como já dito, possui uma série de métodos, entre eles, provê o método sort( )
que foi utilizado no exemplo, responsável por ordenar o array passado como
parâmetro, realizando internamente um mecanismo de comparação entre as
strings e ordenando-as.
Note que utilizamos o atributo length para que a impressão ordenada
dos nomes fosse realizada através da estrutura de repetição for. Isto abre
espaço para que seja comentado que mesmo os vetores sendo estruturas estáticas, estes possuem alguns atributos e métodos que auxiliam em sua manipulação, como, por exemplo, o método equals( ).
Com isso, chegamos ao final de mais um capítulo. Nosso próximo compromisso é a necessidade de trabalharmos com o tratamento de exceções no
Java, o que nos abrirá caminho para elementos mais avançados, tais como, o
trabalho com arquivos ou a manipulação de streams.
Capítulo 6
Tratamento de exceções e entrada de dados
! " # $ #%& # # & ' & $
( $ ( $
) ! * $ # # & $ $ # # + ! ( % , $
/ ( # ! # ( + !
& 011 #! $ 0 % ( $ $ 23
$ ( $ $
Como mencionado, diversas classes fornecidas pelo JDK podem
ser utilizadas para fornecer tal suporte para as aplicações desenvolvidas,
diminuindo o tempo de possíveis manutenções que venham a ocorrer e aumentando a capacidade dos programas em Java.
102 t JAVA com Orientação a Objetos
6.1 Tratadores de exceções
A exceção na linguagem Java é uma indicação de que um erro ou um
problema aconteceu durante a execução de uma aplicação. Isto se torna mais
suscetível considerando que a linguagem Java fornece uma grande quantidade
de bibliotecas, no caso, APIs, ou mesmo classes próprias. A ideia nem chega
perto da perfeição, mas, ao menos, tenta garantir à tecnologia, que as classes
de exceção tratem as situações de erros moderados que podem ser encontrados e visualizados em seus programas e, então, recuperados.
Uma maneira interessante de interpretar essas exceções é sempre trabalhar os trechos de código que apresentam um grau maior de possibilidade de
que um erro possa vir a acontecer e geralmente, isto está vinculado à utilização de recursos externos à tecnologia. Isto, em um contexto mais recente e
atualizado, no qual os sistemas são construídos sem critérios, torna a tecnologia Java um elemento diferenciado e um porto seguro para o desenvolvimento de soluções complexas na construção de aplicações críticas, tais como,
sistemas hospitalares, de aviação e até mesmo espaciais e de robótica.
O tratamento de exceções no Java nada mais é que um processo de gatilho, que ao ser disparado um erro como mensagem para o sistema, possibilita que seja trabalhada a exceção. Este processo demonstra ao sistema o erro,
além de relatá-lo para o entendimento e o tratamento da melhor solução para
a recuperação e a continuídade das próximas rotinas do programa.
Assim, o tratamento de exceções geralmente deve ser utilizado ao:
Processar situações excepcionais, nas quais um método seja incapaz de
terminar sua função por razões que fogem ao seu controle;
Processar exceções de componentes que não estão projetados para realizarem tais tarefas diretamente;
Em projetos de grande porte para tratar as exceções de maneira uniforme em todo o projeto.
A manipulação das exceções em Java é feita da mesma maneira como
na linguagem C++, com a utilização das claúsulas try e catch. A cláusula try é
utilizada para indicar o trecho de código que será executado e no qual poderá
ocorrer uma exceção, no caso, um pedaço de código onde a possibilidade de
que possíveis erros ocorram é maior, tal como a abertura de um arquivo ou
mesmo a abertura de uma conexão de rede com outro computador, e como já
mencionado, a conexão com um banco de dados.
Capítulo 6 - Tratamento de exceções e entrada de dadost103
Já na declaração da cláusula catch, define-se o código a ser executado, caso algum erro ou, no caso, uma exceção venha a acontecer durante a
execução da instrução principal, presente na cláusula try. O quadro abaixo
demonstra a organização das cláusulas try/cath que devem ser utilizadas
dentro do escopo de um programa Java.
...
try{
...
// trecho do código a ser executado
}catch(Classe que trata a excecao){
...
// tratamento da exceção
}
...
Um detalhe importante a ser considerado está na possibilidade de existirem quantas cláusulas catch forem necessárias para o melhor desempenho
da aplicação. Isto se deve ao fato de poderem existir vários tipos de possíveis
exceções e obviamente, é interessante que exista um tratamento específico
para cada um desses gargalos do que, por exemplo, apenas um tratamento
generalista que possa vir a comprometer sua recuperação e continuídade na
execução do programa.
Além disso, podem ocorrer situações nas quais é necessária a execução
de alguma tarefa ou instrução, ocorrendo ou não uma falha no trecho de
código principal, ou seja, com o perfeito funcionamento da aplicação ou não.
Aqui, entra em cena a cláusula finally, na qual é definido o bloco de código
que será executado havendo ou não o lançamento de uma exceção. Sua utilização deve ocorrer após a declaração das cláusulas try e catch. Assim, temos
uma nova estrutura para o tratamento das exceções nos Java, como apresentado no trecho a seguir.
...
try{
...
// trecho de código a ser executado
104 t JAVA com Orientação a Objetos
}catch(Classe que trata a excecao){
...
// tratamento da exceção
}catch(Classe que trata a excecao){
...
// tratamento da exceção
’
...
/*código a ser executado com ou sem
exceção */
}
...
Aqui, é necessário que façamos algumas considerações para entendermos o tratamentos de exceções. Conforme você deve ter observado nos quadros, a cláusula catch obriga-nos a adotar uma classe que será responsável por
tratar os possíveis erros que venham a ocorrer. As cláusulas vistas até aqui de
nada serviriam se não existissem classes responsáveis pela identificação dos
erros lançados pela JVM, além de possuir métodos que possibilitem o entendimento do problema e dos elementos para a sua recuperação. Existem algumas classes que devem ser conhecidas para a sua manipulação, em conjunto
com as cláusulas para tratar as exceções mais comuns, sendo apresentadas
na Tabela 6.
Tabela 6. Principais exceções.
Classe
Função
ArithmeticException
Classe utilizada para tratar as exceções
em operações aritméticas e lança uma
exceção quando estas são impossíveis de
ser realizadas.
NullPointerException
Utilizada para o controle de instâncias e
ocorre quando um objeto é instanciado.
Capítulo 6 - Tratamento de exceções e entrada de dadost105
NegativeArraySizeException
Classe utilizada para o controle de
vetores e ocorre quando um valor nulo é
atribuído a uma posição do array.
ArrayIndexOutOfBoundsException
Utilizada para o controle de vetores e
ocorre quando se tenta acessar uma posição do array que não existe.
IOException
Utilizada para tratar as exceções nas operações de entrada e saída de dados.
Exception
Classe geral para o tratamento de exceções, ou seja, qualquer que seja o erro.
Além disso, utilizada pelas demais classes
que tratam os erros por meio da herança.
Bom pessoal, assim como qualquer classe, as de exceção devem ser importadas para a utilização dentro de um programa. Como feito nos demais
capítulos trabalhados até aqui, nada como implementar alguns exemplos
para a fixação dos novos e essenciais conceitos da linguagem Java vistos em
nosso sexto capítulo. Assim, transcreva o código observando a utilização das
cláusulas para o tratamento de exceções, então ao final, execute o programa
para visualizar o resultado.
// Classe Excecao.java
public class Excecao{
public static void main(String[] args){
int a = 20;
int b = 0;
int c = 0;
try{
c = a/b;
}catch(ArithmeticException e){
System.out.println(“Problemas na operação”);
System.out.println(e);
}
106 t JAVA com Orientação a Objetos
$ ˆ ˆ! &“€!YZ ?”•'
}
}
}
No exemplo apresentado, foi criado um programa que contém um
problema matemático, com a divisão por 0. Assim, obrigamos que a cláusula
catch seja executada. Verifique que todas as informações necessárias para
o entendimento do problema são disponibilizadas pela instância da classe
ArithmeticException. Além disso, a cláusula finally é executada ao final do
programa com o lançamento da exceção.
Nos últimos anos, várias tecnologias têm contribuído para o avanço da
tecnologia Java, tornando-a mais interessante em diversos aspectos, incluindo também o tratamento de exceções. Um novo conceito que vem ganhando
espaço é a Programação Orientada a Aspectos. A orientação a aspectos é um
paradigma de programação que vem sendo desenvolvido para somar valor à
POO (Programação Orientada a Objetos) e assim, tornar todo o processo de
programação mais eficiente. Fica a dica de uma leitura complementar sobre
a Programação Orientada a Aspectos, que é algo facilmente encontrado na
Web.
Outro detalhe sobre o tratamento de exceções que deve ser comentado
é que a tecnologia Java permite outra forma de realizar o tratamento. Este
consiste na utilização da cláusula throws.
6.1.1 Throws
Assim como nas cláusulas try/catch, a throws é a responsável por tratar os possíveis erros que venham a acontecer, porém a cláusula throws exige
que sejam listadas as classes que tratam as possíveis exceções que podem ser
disparadas na assinatura de um método. Para ter uma melhor compreensão,
vamos à sintaxe da cláusula throws.
...
tipoDeRetorno nomeDoMetodo() throws ClasseDeExcecao1,
ClasseDeExcecao2, ...{
// corpo do método
}
...
Capítulo 6 - Tratamento de exceções e entrada de dadost107
Aqui, cabe dizer que a única diferença entre as duas sintaxes apresentadas é que as cláusulas try/catch definem seu bloco de código que será tratado, caso ocorra alguma exceção. A cláusula throws, por sua vez, é sempre
definida para um método de uma classe, logo, a qualquer momento dentro do
método ao ocorrer um erro, as classes definidas são executadas para tratá-lo.
Vamos a um exemplo para o conceito apresentado.
// Classe ExcecaoThrows.java
public class ExcecaoThrows{
public static String divisao(int a, int b)throws
ArithmeticException{
return “O valor da divisão é “ + a/b;
}
public static void main(String[] args){
int a = 20;
int b = 0;
int c = 0;
divisao(a,b);
}
}
Bom, conforme mencionado, o método de divisão da classe
ExcecaoThrows.java utiliza a palavra reservada throws para invocar a classe ArithmeticException para realizar o tratamento do erro matemático.
Com o conhecimento adquirido para o tratamento de erros, abrimos espaço
para também apresentarmos uma nova maneira de obter os dados oriundos do teclado, que é por meio da utilização das classes BufferedReader e
InputStream.
6.2 Entrada de dados
Conforme você já deve ter percebido, o método apresentado, e que
você conhece desde o início da leitura de nosso livro, no qual utilizamos objetos da classe Scanner, possui suas limitações,.além de ser instável e inviável
para as grandes quantidades de dados digitados. Pois bem, agora iremos verificar uma nova forma de realizar a manipulação dos dados vindos do teclado.
108 t JAVA com Orientação a Objetos
Como já foi mencionado no início de nosso livro, a classe System é responsável por controlar todas as funções do sistema. E por meio de System.in,
estamos lidando diretamente com as entradas de dados. Até aqui, nenhuma
novidade. Porém, existem outras classes que substituem a classe Scanner na
manipulação dos dados, no caso, InputStream e BufferedReader. A primeira
é reponsável pela leitura do fluxo de entrada. A segunda, pela manutenção no
buffer dos dados que foram digitados até que seja pressionada a tecla Enter.
Pois bem, para que você possa compreender e verificar o funcionamento dessa modalidade de entrada de dados, vamos a um exemplo.
// ManipulacaoDados.java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class ManipulacaoDados {
public static void main(String args[]){
String dados = new String();
BufferedReader entr = new BufferedReader(
new InputStreamReader(System.in));
try {
System.out.println(“Entre com a sua frase:”);
dados = entr.readLine();
System.out.println(“A frase digitada foi:”+dados);
} catch (IOException e){
System.out.println(“Erro ao ler string”);
e.printStackTrace();
}
}
}
Você deve estar perguntando: Mas, por que isso não foi apresentado
antes? No caso, esta metodologia de entrada de dados não foi demonstrada
anterioremente, pois necessitava do tratamento de exceções, uma vez que
estamos lidando com alguns elementos critícos, tais como, a manipulação de
streams, buffer, e isso nos força a lidar com um tratamento prévio dos riscos
que podem ser impostos à nossa aplicação.
Capítulo 6 - Tratamento de exceções e entrada de dadost109
Entendendo o que foi feito em nosso exemplo, verifique que inicialmente foi declarada uma variável de dados do tipo String, que receberá os
valores oriundos do teclado. Para isso, no entanto, foi necessária a utilização dos objetos da classe BufferedReader e InputStream. A primeira, como
mencionado, apenas armazena os dados oriundos do teclado, obtidos por
meio da InputStrem, que é responsável por recuperar os dados de entrada
do sistema, no caso, o teclado, por meio de System.in. O método readLine( ),
do objeto da classe BufferedReader, faz com que o sistema passe a esperar a
entrada de dados e armazená-los na memória durante a execução.
Encerrando este capítulo, vale resaltar que as duas classes citadas nesta seção também são úteis para a manipulação de arquivos, característica
essa que consideraremos em nosso próximo capítulo.
Capítulo 7
Manipulação de arquivos de texto
Bom pessoal, chegamos ao nosso último capítulo e para finalizarmos
os conceitos e as tecnologias introdutórias da tecnologia Java, nada como
aprendermos a criar, realizar a entrada e a leitura de dados nos arquivos texto. Os conceitos trabalhados até aqui nos demais capítulos serão essenciais
para uma melhor compreensão dos conceitos relacionados ao trabalho com
arquivos, principalmente os referentes ao tratamento de exceções, considerados no capítulo anterior.
Estudar a manipulação de arquivos é de extrema importância, pois
geralmente todas as aplicações trabalham com algum tipo de persistência
de dados, uma vez que todos os exemplos realizados e apresentados até aqui
somente fazem uso da memória volátil de seu computador. Ou seja, todos os
dados são perdidos assim que finalizamos nossa aplicação, como você já deve
ter percebido.
Diante de tais fatos, a maneira mais básica e simples de realizar a persistência dos dados é por meio da utilização de arquivos. Isso possibilita que
as informações das aplicações possam ser recuperadas após sua finalização,
abrindo novas possibilidades para seus programas. Obviamente, existem
formas mais profissionais de manter os dados, como, por exemplo, a utilização de Sistemas Gerenciadores de Banco de Dados (SGBD), mas, em diversos
momentos, a manipulação de arquivos é útil e interessante, mantendo, na
maioria das vezes, neutralidade dos dados. Neste primeiro livro, não iremos
abordar a manipulação do banco de dados, uma vez que o objetivo aqui é
fazer com que este livro seja um referencial para as disciplinas introdutórias
de programação.
A tecnologia Java fornece diversas classes e com isso, formas para o
trabalho com arquivos, no caso, a criação, armazenamento e recuperação de
dados. Então, vamos apreciá-las.
7.1 Arquivos
A primeira coisa que devemos tratar sobre a manipulação de arquivos
112 t JAVA com Orientação a Objetos
com Java é a oferta de um pacote destinado a manipulá-los, denominado
java.io. É esse pacote que mantém a maioria das classes que podem ser utilizadas para o trabalho com arquivos.
Um detalhe importante que deve ser mencionado é que, em sua maioria, os programadores tratam a leitura e a escrita, respectivamente, como
operações de entrada e saída de dados em um arquivo. Tal notação é uma
formalização do processo e também é comum em outras linguagens de programação, tais como C e C++. Além disso, a notação apresentada demonstra
uma relação do ponto de vista da aplicação, na qual o arquivo a ser lido passa
ser uma fonte de entrada de dados a serem processados e depois, saindo da
aplicação, eles são escritos e mantidos novamente no arquivo.
O java.io é um dos pacotes mais extensos da tecnologia Java, com mais
de 50 classes que fornecem suporte para o trabalho com arquivos, além de
funcionalidades como, por exemplo, a compactação de arquivos, mas, em
sua maioria, tais classes são classificadas nos dois grandes grupos já mencionados, no caso, a entrada e a saída de dados. É importante mencionar que
as operações irão depender do tratamento de exceções para a realização da
manipulação dos arquivos. Geralmente, para estes casos, é utilizada a classe
IOException que também faz parte do pacote java.io e traz funcionalidades
que auxiliam na recuperação dos erros que possam vir a acontecer.
Outro importante detalhe a ser mencionado sobre o pacote java.io é
o fato de que as classes oferecidas permitem também o trabalho com arquivos especiais como binários, de buffer, como vimos no capítulo anterior, ou
mesmo stream de vídeo e som. Aqui, não iremos considerar tais classes, mas
uma boa gama deste material pode ser encontrada na Web. Diante de tantas
possibilidades, é necessário que façamos uma seleção das principais para que
concentremos nossos esforços. No caso, estaremos trabalhando com FileReader,
Scanner, PrintWriter, FileInputStream, FileOutputStream, RandomAccessFile e
IOException.
7.2 Entradas
Indo ao que realmente interessa, no caso, a leitura de dados em arquivos, que é considerada uma forma de entrada de dados para uma aplicação, a
linguagem Java fornece uma classe que possibilita este processo - a FileReader.
Sua utilização não apresenta nada fora do normal das demais classes consideradas até aqui. Assim, devemos realizar a criação de um objeto da FileReader
Capítulo 7 - Manipulação de arquivos de textot113
que fornece atributos e métodos para manipulação do arquivo.
Entretanto, a classe FileReader necessita da utilização de uma classe
já conhecida para realizar o acesso e a manipulação dos dados vindos do arquivo. Essa classe consiste na Scanner, classe com a qual já trabalhamos no
início de nosso livro. Pois bem, se você lembra, a Scanner é responsável por
auxiliar na manipulação dos dados que vêm do teclado, acessando o atributo
de entrada de dados da System.in. Em nosso caso, só mudaremos a fonte dos
dados, que antigamente consistia no teclado e agora, é o arquivo.
Iremos, agora, utilizar a classe Scanner para realizar operações no objeto da classe FileReader, que faz referência ao local onde se encontra o arquivo
de texto. Nada como um bom exemplo para entender o processo. Transcreva
o código abaixo e execute-o para verificar seu funcionamento.
// LerArquivo.java
import java.io.IOException;
import java.io.FileReader;
import java.util.Scanner;
public class LerArquivo{
public static void main(String[] args) {
FileReader arq = null;
try {
arq = new FileReader(
"c:\\programas\\Arquivo.txt");
} catch (IOException e) {
e.printStackTrace();
}
Scanner leitor = new Scanner(arq);
while(leitor.hasNextLine()){
String linha = leitor.nextLine();
System.out.println(linha);
}
}
}
114 t JAVA com Orientação a Objetos
É bom mencionar que, para nosso programa funcionar, o arquivo ao
qual fizemos a referência com o objeto da classe FileReader deve estar no
mesmo local onde a classe se encontra. Uma forma de resolver este problema
é passando o caminho de referência completo para o local no qual o arquivo
se encontra, no caso, “C:\programas\Arquivo.txt”. É importante citar que foi
necessária a realização do tratamento de exceção que possa vir a acontecer,
como, por exemplo, o fato do arquivo não ser localizado. Isso obviamente foi
realizado com a utilização das cláusulas try/catch, além da classe IOException.
Ainda em nosso exemplo, um objeto da classe Scanner foi instanciado, sendo passada, como parâmetro, a instância da classe FileReader, denominada
arq, que define o caminho do arquivo, como já analisado. Após, é utilizada
a estrutura de repetição while para a leitura linha a linha do arquivo e uma
posterior impressão do conteúdo.
Conforme vimos, a manipulação de arquivos no Java trata-se de uma
rotina simples, sem muitos segredos, isso graças ao suporte que a tecnologia fornece. Entretanto, vale ressaltar que tais funcionalidades dependem da
correta utilização das classes do pacote java.oi. Apresentado o conceito de entrada de dados, ou seja, a leitura de um arquivo, vamos ao processo de criação
e escrita ou, conforme já é de seu conhecimento, à saída de dados utilizando
a classe PrintWriter do pacote java.io.
7.3 Saídas
Após verificarmos como funciona o processo de entrada de dados, iremos trabalhar com a criação e a gravação de dados em um arquivo no formato de texto, utilizando para isso, a classe PrintWriter. Tal classe fornece
atributos e métodos para a manipulação de arquivos, entretanto, aqui também é necessária a utilização da classe IOException para o tratamento das
exceções, assim como é feito para a leitura de dados. Mas aqui, trataremos os
erros que venham a ocorrer no processo de criação e escrita dos dados.
Vamos colocar a mão na massa e escrever mais um exemplo. Neste
caso, um no qual iremos criar um arquivo e escrever uma mensagem dentro
deste.
// EscreverArquivo.java
import java.io.IOException;
import java.io.PrintWriter;
Capítulo 7 - Manipulação de arquivos de textot115
public class EscreverArquivo{
public static void main(String[] args) {
PrintWriter arq = null;
try {
arq=new PrintWriter("dados.txt");
arq.println("Mensagem escrita");
arq.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Vários detalhes devem ser mencionados sobre o processo de criação
e escrita em arquivos. Inicialmente, um importante detalhe a ser descrito, e
que você deve levar em consideração sobre a criação e a escrita, é que se seu
arquivo já existir, ele será recriado e com isso, todos os dados existentes anteriormente serão perdidos. Outro detalhe é que para que todos os dados sejam
persistidos em um arquivo, é necessário que sempre ao final do processo de
escrita, ou seja, ao final da utilização da instância da classe PrintWriter, seja
feita a invocação do método close( ). Tal processo pode ser contemplado no
exemplo anterior. Finalmente, no programa anterior, note que foi utilizado
para a escrita do conteúdo, o método println( ), também da classe PrintWriter.
Então, vamos tornar nosso exemplo mais interessante. Para isso,
iremos, com a utilização da classe Scanner, capturar os dados do teclado e
persisti-los em um arquivo. Este será nomeado com o nome definido pelo
usuário. Então, transcreva o código e execute-o para verificar seu adequado
funcionamento.
// CriandoArquivo.java
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Scanner;
public class CriandoArquivo{
public static void main(String[] args) {
116 t JAVA com Orientação a Objetos
Scanner entrada=new Scanner(System.in);
PrintWriter arq = null;
System.out.println("Nome do arquivo");
String nome = entrada.nextLine();
System.out.println("Entre com os dados");
String dados = entrada.nextLine();
try {
arq = new PrintWriter(nome);
} catch (IOException e) {
e.printStackTrace();
}
arq.println(dados);
arq.close();
}
}
Note que no exemplo, por meio do operador new e o construtor da
classe PrintWriter, foi passado o nome do arquivo a ser criado. Entretanto,
esse arquivo só será efetivamente criado após a execução do método close( ),
ou seja, ao finalizar o programa. Com isso, verificamos o processo de criação
e escrita em arquivos, sendo algo bem mecânico.
7.4 Streams
Outra possibilidade de trabalhar com a manipulação de entrada e saída
de dados consiste na utilização das classes FileInputStream e FileOutputStream.
Essas classes são extremamente úteis, pois nem só de arquivos com texto
natural vivem nossos programas. Estas são úteis para as manipulações de
todos os tipos de arquivos. Assim como nas demais classes vistas até aqui,
a FileInputStream e a FileOutputStream são fornecidas pelo pacote java.io.
Elas são utilizadas para manipular fluxos de bytes, ou seja, possibilita uma
manipulação em um nível mais baixo. Estes dados, na maioria das vezes, se
alterados, comprometem sua estrutura e possível utilização. Ou seja, para
uma manipulação mais segura e da qual dependemos de uma maior segurança, as classes são recomendadas.
Como os próprios nomes sugerem, a FileInputStream é utilizada
para a entrada de dados e a FileOutputStream, para a saída. Um aspecto
Capítulo 7 - Manipulação de arquivos de textot117
importante a ser colocado aqui é o fato de que assim como nas classes
FileReader e PrintWriter, já vistas, para as classes que auxiliam na manipulação
de streams também é necessária a utilização do tratamento de exceção. Para
ter uma melhor compreensão do que foi considerado, veja o exemplo a seguir
e execute-o.
// Classe ExemploStream.java
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class ExemploStream{
public static void main(String[] args) {
FileOutputStream saida = null;
FileInputStream entrada = null;
try {
saida = new FileOutputStream(“dados.txt”);
saida.write(1010);
entrada= new FileInputStream(“dados.txt”);
System.out.print(entrada.read());
} catch (IOException e) {
e.printStackTrace();
}
}
}
No exemplo anterior, foi criado um arquivo e escrita uma linha de
stream com a utilização do método write( ) da classe FileOutputStream. Logo
após, é realizada a leitura dos dados escritos e a impressão destes por meio
do método read( ) da classe FileInputStream. Repare que o processo de criação dos objetos para a manipulação dos arquivos de stream é semelhante aos
realizados até aqui com outras classes pertencentes ao pacote java.io.
118 t JAVA com Orientação a Objetos
7.5 Acessos aleatórios em arquivos
Em todos os exemplos apresentados até aqui, toda a leitura é realizada
de maneira sequencial, ou seja, do início do arquivo até seu fim, sempre nessa
ordem. No entanto, em diversas situações, assim como no caso dos vetores,
isso pode ser caro computacionalmente. Imagine que você esteja buscando
uma informação em um arquivo extenso e essa informação esteja apenas no
final do arquivo. Pois bem, para que você chegue a essa informação, deve
percorrer todo o arquivo. Isso muitas vezes complica todo o processo. No
caso, é muito mais econômico computacionalmente realizar a leitura de uma
posição especifica no arquivo ou mesmo a escrita em determinado ponto.
Para isso, a tecnologia Java fornece a classe RandomAccessFile, que possibilita o
trabalho de acesso aleatório a posições dentro dos arquivos. A RandomAccessFile é
mais uma classe que também faz parte do pacote java.io.
Bom, neste caso, a vantagem de manipular seus arquivos com as instâncias dessa classe é nítida, indo pontualmente no local no qual se deseja escrever ou ler. Para tanto, são utilizados métodos para as operações nos
arquivos, tais como seek( ), length( ), read( ) e write( ), entre os principais.
Aqui, oriento que o exemplo seja transcrito e executado.
// Aleatorio.java
import java.io.IOException;
import java.io.RandomAccessFile;
public class Aleatorio{
public static void main(String[] args) {
try {
~– ~–&
“dados.txt”,”rw”);
ˆ—&˜'
$ ˆ ˆ! &ˆ&''
} catch (IOException e) {
e.printStackTrace();
}
}
}
Capítulo 7 - Manipulação de arquivos de textot119
Alguns aspectos do código anterior devem ter chamado a sua atenção.
No caso, a classe RandomAccessFile trabalha com o conceito de definição do
tipo e acesso, sendo que tal definição é realizada no momento da instanciação do objeto com a utilização de seu construtor. Obviamente, são utilizados
parâmetros no momento da abertura do arquivo para a definição das permissões no arquivo. Os tipos de acesso que podem ser trabalhados consistem,
respectivamente, nos atributos ‘r’ e ‘rw’, que indicam que o arquivo deve ser
aberto somente para a leitura e quando podem ser realizadas operações tanto de leitura quanto de escrita, respectivamente.
Para o posicionamento do cursor no arquivo na posição na qual
se deseja realizar a escrita ou a leitura, é necessária a utilização do método
seek( ). Para a leitura, continuamos a trabalhar com o método read( ), conforme demonstrado no exemplo. Da mesma forma, podemos realizar a escrita, sendo que para isso, utilizamos o método write( ) da própria classe
RandomAccessFile.
Aqui, entretanto, é importante mencionar que existem diversas vertentes do método write permitindo que sejam trabalhados vários tipos de
dados disponibilizados pela tecnologia Java. Existem métodos, tais como
writeInt( ), writeBoolean( ) e writeChars( ), que possibilitam o tratamento de
cada tipo de dado. Isso auxilia o desenvolvedor, pois evita que seja perdido
tempo realizando sua conversão ou mesmo manipulação para persistir os dados. Para visualizar o resultado destas operações, altere o exemplo anterior
conforme é demonstrado a seguir.
// Classe Aleatorio.java
import java.io.IOException;
import java.io.RandomAccessFile;
public class Aleatorio{
public static void main(String[] args) {
try {
~– ~–&
“dados.txt”,”rw”);
ˆ—&˜'
$ ˆ ˆ! &ˆ&''
ˆ ™&š` š'
ˆ&'
120 t JAVA com Orientação a Objetos
} catch (IOException e) {
e.printStackTrace();
}
}
}
No caso em específico, a estrutura continua a mesma e deve ser realizado o tratamento de exceções, já que sua manipulação pode acarretar problemas para a aplicação.
Caro amigo, assim finalizamos nossa leitura. Espero que este manuscrito tenha sido de proveito para que você entenda os detalhes e os conceitos
básicos da programação baseada na tecnologia Java, principalmente os vinculados ao paradigma da programação orientada a objetos. Obviamente que
a tecnologia não se restringe somente aos tópicos mencionados até aqui. Poderíamos citar a criação de aplicações desktop com a utilização da API Swing,
além do acesso ao banco de dados com a JDBC (Java Database Connection)
ou ainda a criação de programas para dispositivos móveis ou Web, tudo baseado no Java.
Mas, obviamente que minha intenção até aqui sempre foi garantir que
fossem apresentados os elementos que possam subsidiar o conhecimento
dessas novas ferramentas que estão à sua disposição. Sinceramente, desejo-lhe sucesso nos seus desafios e espero revê-lo em breve.
Referências
Bibliográficas
BOENTE, Alfredo. Aprendendo a Programar em Java 2: Orientado a
Objetos. Rio de Janeiro: Brasport, 2003.
ANSELMO, Fernando. Aplicando Lógica Orientada a Objetos em Java.
2. ed. Florianópolis: Visual Books, 2005.
CARDOSO, Caíque. Orientação a Objetos na Prática: Aprendendo
Orientação a Objetos com Java. Rio de Janeiro: Ciência Moderna, 2006.
DEITEL, Harvey M. Java: Como Programar. 6ª ed. São Paulo: Pearson
Prentice Hall, 2005.
SANTOS, Rafael. Introdução à Programação Orientada a Objetos
Usando Java. Rio de Janeiro: Campus, 2003.
PUGA, Sandra; RISSETTI, Gerson. Lógica de Programação e Estruturas de Dados: com Aplicações em Java. São Paulo: Pearson Prentice Hall,
2004.
Horstmann, Cay; Cornell, Gary. Core Java, Volume 1 – Fundamentos. 8
Edição. São Paulo: Pearson, 2010.
Block, Joshua. Java Efetivo - 2ª Edição. Rio de Janeiro: Alta Books, 2008.
Furgeri, Sergio. Java 7 - Ensino Didático. São Paulo: Editora Érica, 2010.
Sierra, Kathy; Bates, Bert. Use a Cabeça Java. Rio de Janeiro: Alta Books,
2005.
Apêndice I
Instalação do Sdk e Configuração das Variáveis de
Ambiente (Windows Xp)
Para o download do JDK, siga os seguintes passos:
1. Acesse por meio de um navegador o endereço: http://java.oracle.
com
2. Após acessar o endereço, clique no link Java SE em “Downloads”
ou em “Top downloads”.
3. Logo em seguida, clique no botão “Java Platform” para selecionar
a versão “Standard Edition” da tecnologia.
4. Após isso, aceite o termo e selecione o tipo de Sistema Operacional
no qual a tecnologia será executada. No caso do Windows, selecione conforme apresentado a seguir.
124 t JAVA com Orientação a Objetos
5. Caso tudo tenha ocorrido conforme o esperado, será aberta a janela para o download do arquivo.
6. Depois, basta seguir as orientações do software no momento de
sua instalação.
Para as variáveis de ambiente, siga os seguintes passos:
1. Selecione Iniciar > Painel de Controle > Sistema.
Apêndice It125
2. Selecione Avançado > Variáveis de Ambiente.
3. Clique em Nova.
126 t JAVA com Orientação a Objetos
4. Em “Nome da Variável”, digite JAVA_HOME.
5. Em “Valor da Variável”, digite o caminho onde foi instalado o
Java em seu computador. Copie o caminho literalmente como é
exibido, por exemplo, no Explorer.
6. Clique em OK.
7. Procure a variável PATH, selecione-a e escolha “Editar”.
Apêndice It127
8. Em “Valor da Variável”, acrescente ao valor já existente em seu
final: “;%JAVA_HOME%\bin”.
9. Clique em OK.
10. Selecione Avançado > Variáveis de Ambiente > Nova, assim
como foi feito para a criação da variável JAVA_HOME. Se estiver
em dúvida, observe o processo feito anteriormente.
11. Em “Nome da Variável”, digite CLASSPATH.
12. Em “Valor da Variável”, digite “.;%JAVA_HOME%\lib\tools.jar”.
13. Clique em OK.
14. Clique novamente em OK.
Para que você tenha certeza dos efeitos das mudanças, reinicie o
sistema.
Apêndice II
JAVADOC
1. Inicialmente, é necessário realizar o comentário de documentação
em sua classe definindo e seguindo os elementos definidos pela tecnologia
Java. Existem diversas tags especiais definidas pela tecnologia, que auxiliam
na definição de informações importantes e na formatação padrão, tais como,
@author, @see e @return. Veja o exemplo:
/**
› * @see java.lang.Object
* @author Alex Coelho
*/
public class Carro{
public String cor;
public String marca;
public String modelo;
/**
* Construtor Carro
* @param Carro
* @throws Sem Exceções
*/
public Carro(Carro novo){
this.cor = novo.cor;
this.marca = novo.marca;
this.modelo = novo.modelo;
}
/**
* Método andar que imprime
* o valor String na tela
* @see java.lang.String
130 t JAVA com Orientação a Objetos
*/
protected void andar(){
ligar();
System.out.println(“Carro andando”);
}
/**
* Método parar que imprime
* o valor String na tela
* @see java.lang.String
*/
protected void parar(){
System.out.println(“Carro parado”);
}
/**
* Método ligar que imprime
* o valor String na tela
* @see java.lang.String
*/
private void ligar(){
System.out.println(“Carro ligado”);
}
}
2. Feitos os comentários necessários, digite o comando javadoc em seu
prompt de comando para sua classe, como segue o exemplo:
javadoc NomeDaClasse.java
Veja o funcionamento:
Apêndice IIt131
Se tudo correu conforme o esperado, você deve ter acesso a uma página html na mesma pasta onde se encontra sua classe, como é demonstrado
a seguir.
É importante mencionar que a maioria das IDEs disponíveis no mercado possui algum tipo de suporte para agilizar e garantir a documentação
com Javadoc.
Para obter maiores informações, acesse:
http://www.oracle.com/technetwork/java/javase/documentation/
index.html.
Java na Web
$XWRU$QW¶QLR1HWR
S£JLQDV
lHGL©¥R
)RUPDWR[
,6%1
A complexidade e o crescimento da Internet fez com que surgissem vários
modelos e propostas para a comunicação entre aplicações e que esses fossem
apresentados como opções de computação distribuída.
Este livro aborda as principais tecnologias da plataforma Java 2 Enterprise
! " ! !!#$%&'''%(
vêm se tornando uma das mais bem sucedidas para desenvolvimento em ambiente Web.
) ' '* +, +! / +0/ !1 314 !!5*67
8'(9 !:';*<'!
=>$<sentados.
À venda nas melhores livrarias.
Impressão e acabamento
<=>?@BFBGFHJK=B&HLM@HB0KFN=MB/JFB
Tel: (21) 2201-6662
Download