Buscar

DESENVOLVIMENTO DE SOFTWARE

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 3, do total de 431 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 6, do total de 431 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 9, do total de 431 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Prévia do material em texto

DESENVOLVIMENTO DE SOFTWARE
Educação a Distância
Você estuda quando e onde quiser, sem avrir mão da qualidade
Atividade estruturada
Prezado(a) Aluno(a),
Esta disciplina possui Atividade(s) Estruturada(s).
As atividades estruturadas compõem a grade curricular dos cursos e têm por objetivo aplicar ou explorar os conhecimentos teóricos da disciplina.
Caso tenha dúvidas, entre em contato com o tutor através do fórum de discussão.
Aula 01: Características gerais e sintaxe básica
Apresentação
A linguagem Java teve origem na década de 1990 e de lá para cá foi se tornando cada vez mais popular, sendo amplamente utilizada em sistemas críticos. 
Como toda linguagem, esta também apresenta peculiaridades, como o uso de padrão de escrita CamelCase, paradigma orientado a objetos e processamento paralelo nativo. 
Precisamos conhecer os tipos utilizados, operadores e controles de fluxo, dando assim os primeiros passos na linguagem, e o uso de um ambiente de desenvolvimento integrado é altamente recomendável para o ganho de produtividade.
Objetivos
· Identificar as características gerais do ambiente Java;
· Explicar a sintaxe básica da linguagem Java;
· Aplicar os elementos da sintaxe Java a diferentes contextos.
Características Gerais do Java
Java é uma linguagem de programação orientada a objetos desenvolvida na década de 1990 por uma equipe de programadores chefiada por James Gosling, na empresa Sun Microsystems. Atualmente, a linguagem está sob o controle da Oracle, após ter comprado a Sun.
Desde a sua concepção, o Java trouxe alguns princípios básicos que norteiam a sua própria evolução, destacando-se o cuidado em executar em múltiplas plataformas e garantir elementos de conectividade.
Em linguagens que geram código nativo para o Sistema Operacional, como C++ e Pascal, o código fonte deve ser compilado, gerando arquivos objeto, e estes arquivos devem ser combinados por linkedição para criar um executável.
Outras linguagens, classificadas como interpretadas, não passam por este processo de compilação, sendo executadas linha a linha por um interpretador, a exemplo da programação shell para Unix e do JavaScript nos navegadores.
Atenção
Aqui cabe uma observação de que Java e JavaScript não podem ser confundidos, tratando de tecnologias totalmente distintas.
A linguagem Java utiliza um artifício que permite a execução de seus programas em qualquer plataforma: uso de máquina virtual. Com esta abordagem, os programas em Java são compilados, não podendo ser classificados como interpretados, mas sem gerar executáveis para o Sistema Operacional, logo não sendo linkeditados.
Cada sistema operacional tem a sua versão da máquina virtual Java, normalmente necessitando da instalação do Java Runtime Environment (JRE), disponível gratuitamente no site da Oracle, e os programas feitos em Java irão executar nestas máquinas virtuais, sem se preocupar com o sistema hospedeiro.  
Isto confere à linguagem a característica de ser conceitualmente multiplataforma. Na prática, os programas executam em apenas uma plataforma, a máquina virtual, que assume o papel de “player” de Java, funcionando como os demais “players” para filmes e músicas.
Nós também temos uma grande facilidade para efetuar a conexão com outros sistemas em rede com o uso de Java, por que traz uma extensa biblioteca de componentes para a criação de Sockets, conexão HTTP e FTP, criação de clientes e servidores de email, entre diversos outros, garantindo a premissa básica de conectividade da linguagem.
Outra característica inovadora no lançamento do Java, e rapidamente adotada por outras linguagens, foi a inclusão de um coletor de lixo (garbage collector), o qual efetua a desalocação de memória de forma automática.
Em termos de programação, devemos estar atentos ao fato de que o Java é fortemente tipado, ou seja, o tipo da variável é definido na sua declaração, além de ser case sensitive, diferenciando letras minúsculas e maiúsculas, e adotar o padrão de nomenclatura CamelCase.
Ambiente de Desenvolvimento
ondemand_videoVídeo.
Podemos desenvolver programas em Java com o simples uso de um editor de texto e os programas de compilação e execução oferecidos a partir do Java Development Kit (JDK), o qual pode ser obtido gratuitamente no site da Oracle (www.oracle.com).
Exemplo
Poderíamos criar um pequeno programa de exemplo no bloco de notas.
Você deve salvar este programa com o nome “Exemplo001.java”, depois ir até o prompt e digitar dois comandos, no mesmo diretório onde o arquivo foi salvo, lembrando que o diretório bin do JDK deve estar no PATH.
javac Exemplo001.java
java Exemplo001
Nós utilizamos o primeiro comando para compilar o arquivo de código-fonte “.java” e gerar o arquivo-objeto “.class”, enquanto o segundo comando executa este “.class”, efetuando a chamada ao método main, ponto inicial de execução, e imprimindo a mensagem “Alô Mundo”.
Note que as linhas de código em Java são terminadas com o uso de ponto e vírgula, e o esquecimento deste terminador é uma fonte de erros muito comum para os programadores da linguagem.  
Apesar de ser possível trabalhar apenas com o JDK e o bloco de notas, precisamos de um ambiente que garanta produtividade nas tarefas que envolvem a programação, e para tal devemos adotar um ambiente integrado de desenvolvimento (IDE).
Várias IDEs estão disponíveis para Java, algumas gratuitas e outras pagas, mas todas elas precisam da instalação do JDK antes que as mesmas sejam instaladas.
Alguns exemplos de IDEs para Java:
1Eclipse
2 NetBeans
3 IntelliJ IDEA
4 BlueJ
5 JCreator
Nós iremos adotar o NetBeans, por ser mais didático e já trazer uma configuração completa, sem a necessidade de adicionar servidores ou bancos de dados, o que trará mais conforto para todos na aprendizagem da linguagem.
A interface do NetBeans é bastante complexa, e devemos entender seus componentes principais para melhor utilização da ferramenta.
De acordo com a numeração utilizada na figura, temos os seguintes componentes:
1 Menu Principal – Controle global das diversas opções da IDE, ativação e desativação de painéis internos, instalação de plataformas, entre outras funcionalidades. Este é o controle de mais alto nível do NetBeans.
2 Toolbar – Acesso rápido, de forma gráfica, às opções mais utilizadas, como criar arquivos e projetos, salvar arquivos e executar o projeto.
3 Painel de Controle – Aceita várias configurações, mas no padrão normal apresenta uma divisão com a visão lógica dos projetos (Projetos), uma com a visão física (Arquivos) e outra com acesso ao banco de dados e aos servidores (Serviços).
4 Navegador – Permite o acompanhamento das características de qualquer elemento selecionado da IDE, como classes em meio ao código.
5 Editor de Código – Voltado para a edição do código, com diversos auxílios visuais e ferramentais de complementação.
6 Saída – Simula o prompt do sistema, permitindo observar a saída da execução, bem como efetuar entrada de dados via teclado.
O NetBeans pode ser obtido em netbeans.org/downloads, devendo ser escolhida a versão completa, pois já traz todas as plataformas de programação configuradas, além dos servidores GlassFish e Tomcat embutidos.
 
Após instalar o JDK e o NetBeans (verão completa), podemos executar esta IDE e criar o nosso primeiro programa já no ambiente de desenvolvimento completo.
Para criar um novo projeto devemos seguir os passos:
1. No menu principal do NetBeans escolha a opção Arquivo..Novo Projeto, ou Ctrl+Shift+N;
2. Na janela que se abrirá, escolha o tipo de projeto como Java..Aplicação Java e clique em Próximo;
3. Dê um nome para o projeto (Exemplo001) e diretório para armazenar seus arquivos (C:\MeusTestes) e clique em Finalizar.
Ao final destes passos, o projeto estará criado e sua estrutura poderá ser observada no Painel de Controle, bem como o código padrão estará presente no Editor de Código. Diversos comentários são adicionados a esse código, aparecendo normalmente na coloração cinza, e eles podem ser mantidos ou removidos sem qualquer interferência sobre a funcionalidade do projeto.O que precisamos fazer agora é complementar esse código com a linha que falta no main, conforme pode ser observado a seguir.
 
 
package exemplo001;
public class Exemplo001 {
  public static void main(String[] args) {
    System.out.println("APENAS UM EXEMPLO");
  } }
	
Após acrescentar a linha com o comando de impressão, basta executar o projeto com uso de F6, menu Executar..Executar Projeto, ou botão “play” da ToolBar ( )
O projeto será compilado e executado, e a frase “APENAS UM EXEMPLO” poderá ser observada na divisão de Saída do NetBeans.
Atenção
Para todos os projetos de exemplo que criaremos a partir daqui teremos de seguir esses passos. Ocorrerão alterações apenas para outros tipos de projeto, como aplicativos web, e essas mudanças serão apresentadas no momento oportuno.
Tipos Nativos e Operadores
Quase tudo em Java é baseado em objetos. Basicamente, apenas os tipos nativos são considerados de forma diferente, mas para cada tipo nativo existe uma ou mais classes Wrapper.
Sempre devemos lembrar que um tipo nativo corresponde a uma simples posição de memória, enquanto uma classe Wrapper, além de comportar o valor correspondente ao tipo, oferece métodos específicos para tratamento do mesmo.
Podemos observar, no quadro seguinte, os principais tipos nativos do Java.
	Tipo Nativo
	Wrapper
	Descrição do Tipo
	byte
	Byte
	Inteiro de 1 byte
	short
	Short
	Inteiro de 2 bytes
	int
	Integer
	Inteiro de 4 bytes
	long
	Long
	Inteiro de 8 bytes
	char
	Character
	Caracteres ASCII
	float
	Float
	Real de 4 bytes
	double
	Double
	Real de 8 bytes
	boolean
	Boolean
	Valores booleanos true ou false
As variáveis aceitam diferentes formas de declaração e inicialização, como podemos observar a seguir:
 
 
int a, b, c;
boolean x = true; // Variável declarada e inicializada
char letra = 'W';
String frase = "Teste";
double f = 2.5, g = 3.7;
	
Para programas simples não há necessidade do uso extensivo de classes Wrapper, mas é aconselhável utilizá-las na criação de sistemas que usem tecnologias de Web Service, ou frameworks de persistência, entre outros.
Atenção
Nas versões antigas do Java as classes Wrapper deveriam ser alocadas com uso de new, mas atualmente podem ser associadas direto ao valor armazenado pelas classes. Exceção feita à classe String, que sempre permitiu a inicialização direta.
Lembre-se sempre de comentar seu código para que você e outros programadores possam revisá-lo com mais facilidade em adaptações posteriores.
O Java aceita dois tipos de comentários:
Comentário de linha, com uso de //.
Comentário longo, iniciado com /* e finalizado com */.
Tendo definidas as nossas variáveis, podemos efetuar as mais diversas operações sobre elas com o uso de operadores. Os principais operadores são divididos em aritméticos, relacionais e lógicos.
Os operadores aritméticos utilizados no Java são:
	Operador
	Operação
	+
	Soma (concatenação para texto)
	-
	Subtração
	*
	Multiplicação
	/
	Divisão
	%
	Resto da divisão inteira
	++
	Incremento de 1
	--
	Decremento de 1
	&
	And (E) binário
	|
	Or (ou) binário
	^
	Xor (ou-exclusivo) binário
	Operador
	Operação
Vamos observar, a seguir, um pequeno programa de exemplo com o uso de operadores.
 
 
package exemplo002;
public class Exemplo002 {
  public static void main(String[] args) {
    int a = 5, b = 32, c = 7;
    System.out.printf("A: %d\t B: %d\t C:%d\n",a,b,c);
    b = b - c;
    b /= a;
    System.out.printf("A: %d\t B: %d\t C:%d\n",a,b,c);
    b = a ^ c;
    System.out.printf("A: %d\t B: %d\t C:%d\n",a,b,c);
    b++;
    System.out.printf("A: %d\t B: %d\t C:%d\n",a,b,c);
  }
}
	
Antes de descrever a saída e as operações utilizadas, vamos observar o comando printf.
Este comando permite uma saída formatada, onde cada %d receberá um valor inteiro, \n é utilizado para pular de linha e \t para representar uma tabulação. No caso, como existem três ocorrências de %d na expressão, ela receberá as três variáveis inteiras logo após a vírgula, preenchendo as lacunas %d com os valores dessas variáveis na ordem oferecida.
Observe agora a saída do programa.
A:5     B:32     C:7
A:5     B:5     C:7
A:5     B:2     C:7
A:5     B:3     C:7
Na primeira impressão temos os valores originais, declarados na inicialização, mas em seguida são feitas duas operações, onde b recebe seu valor decrementado do valor de c, sendo dividido por a em seguida.
A expressão b /= a é equivalente a b = b / a, e isto pode ser feito com qualquer operador quando a variável sofre uma operação sobre ela mesma. Igualmente, poderíamos ter escrito b - = c na primeira operação.
Fazendo as contas teremos os valores da segunda linha que foi impressa.
Em seguida temos uma operação binária entre a e c, com o valor armazenado em b. É uma operação de ou-exclusivo, onde o valor será 1 apenas se os dois bits comparados tiverem valores distintos.
	A
	0
	1
	0
	1
	C
	0
	1
	1
	1
	a ^ c
	0
	0
	1
	0
Com esta operação justificamos na terceira impressão o valor de 2 para b, o qual é acrescido de 1 com o operador ++, apresentando o valor 3 na quarta impressão.
Os operadores relacionais permitem a comparação entre valores, tendo como resultado um valor verdadeiro (true) ou falso (false), o que pode ser armazenado em uma variável booleana.
Podemos observá-los no quadro seguinte.
	Operador
	Operação
	==
	Compara a igualdade entre os termos
	!=
	Compara a diferença entre os termos
	>
	Compara se o primeiro é maior que o segundo
	<
	Compara se o primeiro é menor que o segundo
	>=
	Compara se o valor é maior ou igual
	<=
	Compara se o valor é menor ou igual
Estes operadores serão de grande importância para as estruturas de decisão e de repetição que utilizaremos em nossos programas, pois elas tratam diretamente com valores booleanos para o controle do fluxo de execução.
Nós também podemos combinar valores booleanos com o uso de operadores lógicos, os quais são expressos pela tabela-verdade a seguir:
	A (Booleano 1)
	B (Booleano 2)
	A && B - AND
	A || B - OR
	! A - NOT
	false
	false
	false
	false
	true
	false
	true
	false
	true
	true
	true
	false
	false
	true
	false
	true
	true
	true
	true
	false
No exemplo seguinte podemos observar a utilização de operadores relacionais e lógicos.
 
 
package exemplo003;
public class Exemplo003 {
  public static void main(String[] args) {
    int a = 5, b = 32, c = 7;
    boolean x;
    x = a < b;
    System.out.println("PASSO 1: "+x);
    x = (b > a) && (c > b);
    System.out.println("PASSO 2: "+x);
    b /= a -= 1;
    c++;
    x = (b==c);
    System.out.println("PASSO 3: "+x);
  }
}
	
No primeiro passo temos x recebendo a comparação a < b, que é verdadeira.
No segundo passo, a comparação b > a é verdadeira, mas c > b é falsa, e com o operador lógico aplicado teremos o resultado final como falso.
Em seguida são feitas algumas operações aritméticas de forma concatenada, e que poderiam ser expressas como:
 
 
a = a – 1; // a passa a valer 4
b = b / a; // b é divido por a (4) e fica com 8
c = c + 1; // c passa a valer 8
	
Após estas operações, os valores de b e c são iguais, e a condição se torna verdadeira para o terceiro passo.
Podemos observar a saída resultante a seguir.
PASSO 1: true
PASSO 2: false
PASSO 3: true
Uma observação final é a de que na linguagem Java a divisão entre inteiros retorna um valor inteiro, enquanto para variáveis do tipo ponto flutuante será um valor real, ou seja, se efetuarmos a operação 5 / 2 teremos 2, enquanto 5.0 / 2 dará 2.5.
Estruturas de Decisão
ondemand_videoVídeo.
Após definidas as variáveis e os métodos de saída, o nosso próximo passo será a compreensão das estruturas de decisão existentes na sintaxe do Java.
O fluxo de execução sequencial indica que as diversas instruções serão executadas na ordem em que são apresentadas no código, mas existem formas de redirecionar o fluxo de execução para partes específicas.
É comum encontrarmos situações onde efetuamos determinadas ações apenas perante certas condições. Para isso, em termos de algoritmos, contamos com as estruturas de decisão.
Basicamente, contamos comduas estruturas de decisão:
if..else
switch..case
Estrutura if..else
A sintaxe da estrutura if..else é muito simples, conforme podemos observar a seguir.
if ( <<condição>> ) {
   // instruções para o caso verdadeiro
} else {
   // instruções para o caso falso
}
Se a condição especificada entre parênteses tiver valor verdadeiro, o primeiro bloco de comandos será executado, e se for falsa quem será executado é o segundo bloco.
O uso de chaves é necessário apenas quando temos blocos constituídos de mais de um comando, e o uso de else é opcional.
Vamos observar um pequeno exemplo de uso da estrutura if..else:
 
 
package exemplo004;
import java.util.Scanner;
public class Exemplo004 {
  public static void main(String[] args) {
    Scanner s1 =new Scanner(System.in);
    System.out.println("DIGITE UM NÚMERO:");
    int x = s1.nextInt();
    if(x%2==0)
      System.out.println("O NÚMERO É PAR");
    else
      System.out.println("O NÚMERO É ÍMPAR");
  }
}
	
Neste exemplo foi utilizado um objeto do tipo Scanner, inicializado com a entrada de dados padrão via teclado (System.in) para obter um número inteiro digitado pelo usuário. Isto pode ser observado na chamada s1.nextInt( ).
Após o usuário entrar com o número pelo teclado, ocorre o teste do if para saber se o resto da divisão por dois resulta em zero, imprimindo a mensagem “O NÚMERO É PAR” e, se o resto não for zero, imprimir “O NÚMERO É ÍMPAR” a partir do else.
Como os blocos tinham apenas uma instrução cada, o uso de chaves não foi necessário em nenhum dos dois.
Podemos observar uma possível saída para a execução desse código a seguir.
DIGITE UM NÚMERO:
5
O NÚMERO É ÍMPAR
Estrutura switch..case
Utilizamos a estrutura switch..case quando desejamos efetuar ações específicas para cada valor de uma variável. Um exemplo simples de utilização seria a construção de um menu de opções para um sistema. 
A sintaxe básica utilizada por essa estrutura seria:
 
 
switch (<<variável>>) {
  case valor1:
    // instruções para o valor1
    break;
  case valor2:
    // instruções para o valor2
    break;
  default:
    // instruções para valores que não foram previstos
}
	
O comando switch irá desviar o fluxo de execução para os comandos case, de acordo com o valor da variável, sendo que o comando default será executado caso o valor não esteja entre aqueles que foram previstos.
Devemos observar que cada seção case deve ser terminada com o comando break, pois sua ausência irá causar a continuidade da execução, “invadindo” a seção seguinte. 
Vamos observar um exemplo de utilização da estrutura switch..case a seguir:
 
 
package exemplo005;
import java.util.GregorianCalendar;
import java.util.Scanner;
public class Exemplo005 {
    public static void main(String[] args) {
       Scanner s1 =new Scanner(System.in);
       System.out.println("DIGITE O DIA ATUAL:");
       int d = s1.nextInt();
       System.out.println("DIGITE O MÊS ATUAL:");
       int m = s1.nextInt();
       System.out.println("DIGITE O ANO ATUAL:");
       int a = s1.nextInt();
       GregorianCalendar g1 = new GregorianCalendar(a,m-1,d);
       switch(g1.get(GregorianCalendar.DAY_OF_WEEK)){
           case 1:
              System.out.println("DOMINGO! FERIADO! :)");
              break;
           case 2:
           case 3:
           case 4:
           case 5:
           case 6:
              System.out.println("DIA ÚTIL! TRABALHANDO. :(");
              break;
           case 7:
              System.out.println("SÁBADO! FERIADO! :)");
              break;
           default :
              System.out.println("ALGO ESTÁ ERRADO....");
              break;
       }
    }
}
	
Aqui utilizamos, além do Scanner, um objeto do tipo GregorianCalendar. Esse objeto permitirá tratar elementos de data e hora, sendo inicializado com ano, mês (0 a 11) e dia.
Após a inicialização, utilizamos get(GregorianCalendar.DAY_OF_WEEK) para verificar o dia da semana, o qual é obtido como um valor inteiro de 1 a 7, correspondendo aos dias de domingo a sábado.
Em nosso exemplo, a estrutura switch utiliza esse valor para imprimir a frase equivalente a cada dia da semana, sendo a opção default executada apenas para valores fora da faixa de 1 a 7, algo que não ocorrerá devido ao uso de GregorianCalendar.
Podemos observar uma possível saída para a execução desse código a seguir.
DIGITE O DIA ATUAL:
17
DIGITE O MÊS ATUAL:
11
DIGITE UM NÚMERO:DIGITE O DIA ATUAL:DIGITE O ANO ATUAL:
2018
SÁBADO! FERIADO! :)
Estruturas de Repetição
ondemand_videoVídeo.
Outra forma de redirecionar o fluxo de execução é através do uso de estruturas de repetição. Elas servem para repetir a execução de um comando ou bloco de comandos enquanto determinada condição for verdadeira.
A linguagem Java permite a utilização das seguintes estruturas de repetição:
· for;
· while;
· do..while.
Estrutura for
A implementação de estruturas do tipo PARA..FAÇA utiliza a seguinte sintaxe:
for ( <<inicialização>>; <<condição>>; <<incremento>> )
· Inicialização
· Condição
· Incremento
Inicialização
Definição dos valores iniciais das variáveis de controle.
Exemplo
Por exemplo, um loop de 1 a 5 seria for( int i=1 ; i<=5 ; i++ ).
Estruturas deste tipo são utilizadas quando trabalhamos com uma regra de execução com início e fim bem determinados, como no caso de passeios em vetores, ou quando somamos os valores de uma sequência. Vamos observar o exemplo a seguir.
 
 
public class Exemplo006 {
 public static void main(String[] args) {
   // Calculo do valor médio da sequencia y = f(x) = x * x
  // Media = Somatorio dos valores / quantidade
  // Limites 1 a 5
  double soma = 0.0;
  for(int x=1; x<=5; x++)
    soma += Math.pow(x, 2);
    // eleva x a potência 2 e acumula
  System.out.println(soma/5);
 }
}
	
Neste exemplo temos o cálculo do valor médio de uma sequência entre os limites de 1 a 5, considerando a função y = x2. Foi utilizado o método Math.pow, que eleva um número a uma potência qualquer, para calcular o quadrado de x.
Ao executar o programa teremos a impressão do valor 11, o que corresponde exatamente ao valor médio, dado por (1 + 4 + 9 + 16 + 25) / 5.
Estruturas while e do..while
A implementação de estruturas do tipo ENQUANTO..FAÇA utiliza a seguinte sintaxe:
while ( <<condição1>> ) {
   // Bloco de Comandos
}
Com relação às estruturas do tipo FAÇA..ENQUANTO, a sintaxe utilizada seria:
Do {
   // Bloco de Comandos
} while ( <<condição2>> );
No exemplo seguinte podemos observar a utilização do comando while.
 
 
public class Exemplo007 {
 public static void main(String[] args) {
   int[] x1 = {21, 32, 15, 27, 33, 17};
   int posicao = 0;
   int soma = 0;
   while(posicao< x1.length){
     // Enquanto for menor que o tamanho do vetor
     soma += x1[posicao];
     posicao++;
   }
   System.out.println(soma);
 }
}
	
Neste exemplo é criado um vetor inicializado na criação, assumindo 6 posições, ou seja, terá os índices de 0 a 5.
	x1[0] = 21
	x1[0] = 21
	x1[2] = 15
	x1[3] = 27
	x1[4] = 33
	x1[5] = 17
Em seguida procedemos ao somatório desses valores com o uso de um comando while, tendo como condição posicao< x1.length. Como o length neste caso é 6, a posição varia de 0 a 5.
A cada rodada, o valor do vetor na posição equivalente é adicionado à variável soma, e não podemos esquecer de incrementar a posição, pois caso contrário teríamos uma repetição infinita.
O uso de length permite que seja alterada a quantidade de elementos de x1 na inicialização sem a necessidade de modificações no restante do código.
Ao final teremos impresso o valor 145, que corresponde à soma 21+32+15+27+33+17.
O comando do..while tem funcionamento similar, mas o teste é feito ao final, o que garante pelo menos uma execução.
Podemos observar um exemplo de utilização do comando do..while a seguir.
 
 
public class Exemplo008{
import java.util.Scanner;
public class Exemplo008 {
 public static void main(String[] args) {
   Scanner teclado = new Scanner(System.in);
   int soma = 0;
   do{
     System.out.print("Digite um numero (0 para sair):");
     valor = teclado.nextInt();
     soma+= valor;
   } while(valor!=0);
   System.out.printf("\nA soma dos numeros "+
                     "digitados e: %d\n",soma);
 }
}
	
Neste nosso exemplo serão solicitados os números ao usuário até que ele digite 0, sendo apresentado a seguir o somatório dos números digitados por ele.
Podemos verificar uma possível saída desse programa a seguir.
Digite um numero (0 para sair):13
Digite um numero (0 para sair):32
Digite um numero (0 para sair):21
Digite um numero (0 para sair):7
Digite um numero (0 para sair):0
A soma dos numeros digitados e: 73
Atividade
1. Uma das características do Java é a possibilidade de execução em plataformas distintas sem a necessidade de recompilar o código fonte. Qual componente permite isso?
a) Garbage Collector
b) JVM
c) JDK
d) NetBeans
e) Eclipse
Gabarito
2. Observando a sintaxe do comando for, e considerando que todas as variáveis utilizadas foram previamente declaradas, qual das opções seguintes irá necessariamente gerar um erro de compilação?
a) for(a=1,b=20; a < b; a++,b+=2)
b) for( ; a++ < 10;)
c) for(a=1; a=10; a++)
d) for( ; ; )
e) for(a=200; a>1; a/=2)
Gabarito
3. Implemente um programa em Java para imprimir os 10 primeiros números primos que ocorrem após o número 1. Lembrando que um número primo é aquele divisível apenas por ele mesmo e por 1.
public class Gabarito13 {
 public static void main(String[] args) {
    int contagem = 0;
    int atual = 1;
    while(contagem< 10){
       atual++;
       boolean ePrimo = true;
       for(int i=2; i< atual; i++)
          if(atual%i==0)
             ePrimo = false;
       if(ePrimo){
          contagem++;
          System.out.println(atual);
       }
    }
 }
}
Aula 02: Orientação de Objetos
Apresentação
A programação estruturada fornece um meio bastante direto de expressar processos, adequando-se muito bem à implementação de algoritmos, mas com a evolução dos sistemas se torna necessário adotar metodologias de desenvolvimento mais organizadas, o que popularizou muito as técnicas da Orientação a Objetos.  
Para a criação de sistemas comerciais devemos compreender alguns pilares que definem essa metodologia, como abstração, herança, polimorfismo e encapsulamento, e aprender a sintaxe específica para a implementação desses conceitos. 
Também é necessário que saibamos diferenciar a aplicação de elementos concretos e abstratos na concepção de sistemas Java.
Objetivos
· Identificar os diversos conceitos da Orientação a Objetos;
· Explicar a sintaxe Java para Orientação a Objetos;
· Aplicar a sintaxe Java na criação de elementos concretos e abstratos.
Histórico e Características Gerais
Com a criação de sistemas cada vez maiores e com grande apelo visual, as técnicas tradicionais de modelagem e programação estruturada começaram a entrar em colapso.
Complexos trechos de código inter-relacionados, junto com documentação escassa e diversas replicações de processos já existentes, acabam tornando a manutenção dos sistemas extremamente difícil, aumentando o custo e diminuindo as possibilidades evolutivas desses sistemas.
A orientação a objetos surge neste contexto, trazendo uma forma mais organizada de trabalho, onde a modelagem e a implementação mostram uma proximidade muito maior que nas técnicas ditas tradicionais.
Para podermos adotar essa nova filosofia, devemos deixar de pensar em termos de processos e funções, pois isto é a metodologia estruturada, focada em funcionalidades pontuais e a organização delas.
Agora precisamos pensar de uma forma diferente, em termos de personagens, os quais deverão apresentar características físicas e ações ou verbos.
Exemplo
Na programação estruturada diríamos que o projétil partiu no ângulo de 55 graus, sofrendo a ação da gravidade, atingindo o prédio na altura do quarto andar, pois estamos definindo um processo. Em termos de orientação a objetos, estaríamos considerando que temos um personagem chamado tanque de guerra, e que ele é capaz de atirar um projétil.
A mudança de foco é muito grande, e tem como objetivo:
· Aumento do reuso de código;
· Facilidade de manutenção;
· Documentação automatizada.
Começamos a utilizar de forma mais ampla a programação orientada a objetos (POO1) com o advento das interfaces gráficas, pois ficou muito evidente que a programação estruturada não era a melhor opção para construir ambientes constituídos de janelas.
Antigamente tínhamos que utilizar as APIs do sistema operacional para a construção de cada janela, de forma independente, mas com a POO podemos definir um personagem denominado “Janela”, o qual terá atributos como “posição”, “largura” e “altura”, e será capaz de efetuar ações como “abrir”, “minimizar” e “maximizar”, e, a partir daí, colocarmos a quantidade necessária de personagens deste tipo para implementar as interfaces de nossos sistemas.
Abstração
ondemand_videoVídeo.
Um dos pilares da POO é o conceito de abstração, que se refere à definição de um modelo simplificado de algo maior.
Quando abstraímos algo, estamos preocupados apenas com os detalhes que sejam relevantes para o problema de interesse, e essas abstrações serão representadas como classes na POO, as quais trazem a definição dos atributos e métodos suportados pelos personagens:
Atributos
Definem características físicas, como cor, idade, endereço etc.
Métodos
As ações ou os verbos que podem ser praticados, tais como comer, andar ou dormir.
Neste ponto podemos abandonar a palavra “personagem” e passar a tratar dos dois níveis de programação que utilizaremos:
Classe
funciona como um molde (tipo ou domínio), para definir como serão os objetos criados a partir da mesma. Elas definem apenas os tipos destes atributos, sem assumir valores.
Objetos
São como variáveis ou elementos físicos criados a partir do molde que é a classe. Cada objeto irá assumir valores específicos para os atributos definidos na classe.
Vamos entender melhor.
Poderíamos considerar uma classe “Pessoa”, que apresenta como atributos o nome e o telefone.
Mas qual nome e telefone?
Cada objeto dessa classe irá assumir valores específicos, como p1 se chama João e seu número de telefone é 1111-1111, enquanto p2 se chama Maria e seu número de telefone é 2222-2222.
Vamos observar estes conceitos iniciais a seguir. Para esse exemplo deveremos criar dois arquivos, um para Pessoa e outro para Exemplo010.
 
 
package exemplo010;
public class Pessoa {
  String nome;
  String telefone;
  void exibir(){
    System.out.println(nome+"::"+telefone);
  } }
	
 
 
package exemplo010;
public class exemplo010 {
  public static void main(String[] args) {
    // Instanciando os objetos p1 e p2
    Pessoa p1 = new Pessoa();
    Pessoa p2 = new Pessoa();
    // Preenchimento dos atributos dos objetos p1 e p2
    p1.nome = "João";
    p1.telefone = "1111-1111";
    p2.nome = "Maria";
    p2.telefone = "2222-2222";
    // Chamada ao método exibir em p1 e p2
    p1.exibir();
    p2.exibir();
  }
}
	
Conforme podemos observar, o primeiro arquivo (Pessoa.java) contém a classe Pessoa, a qual define os atributos nome e telefone e o método exibir.
No outro arquivo (Exemplo010.java) teremos a classe chamadora e seu ponto inicial de execução main, onde inicialmente são instanciados os objetos p1 e p2 com o uso do operador new, responsável por alocar esses objetos na memória.
Acompanhando a sequência de comandos, teremos o preenchimento dos atributos nome e telefone para cada um dos objetos, e a chamada ao método exibir de cada um deles.
Teremos como resultado final a impressão do nome e o telefone de cada objeto.
João::1111-1111
Maria::2222-2222
Existe também um método especial chamado de construtor, responsável pela inicialização de um objeto recém-criado. Note que ele não aloca o objeto, sendo esta responsabilidade do operador new, mas permite inicializar atributos do objeto em seu primeiro momento de existência.
Em algumas linguagens temos também o método destrutor, mas o Java não necessita deste método, particularmente por trazer o garbage collector, tecnologia que remove da memória qualquer objeto que não possa ser acessado e não tenha maisutilidade para o programa.
Vamos refazer o exemplo anterior...
 
 
package exemplo010;
public class Pessoa {
  String nome;
  String telefone;
  Pessoa(String nome, String telefone){
    this.nome = nome;
    this.telefone = telefone;
  }
  void exibir(){
    System.out.println(nome+"::"+telefone);
  }
}
	
 
 
package exemplo010;
public class exemplo010 {
  public static void main(String[] args) {
    // Instanciando e inicializando os objetos p1 e p2
    Pessoa p1 = new Pessoa("João","1111-1111");
    Pessoa p2 = new Pessoa("Maria","2222-2222");
    // Chamada ao método exibir em p1 e p2
    p1.exibir();
    p2.exibir();
  }
}
	
Essa modificação do código não irá alterar a saída, mas podemos observar como o uso de um construtor simplificou muito a inicialização dos objetos no main.
Todo construtor é definido pela criação de um método com o mesmo nome da classe, e seria possível utilizar parâmetros com nomes diferentes dos atributos que serão inicializados, mas ocorreria uma grande perda de semântica. 
O problema agora é como iremos diferenciar os parâmetros e atributos, já que eles têm os mesmos nomes. É neste ponto que surge um elemento da orientação a objetos chamado de ponteiro de autorreferência, expresso em Java como this, e que sempre aponta para os atributos e métodos do objeto corrente.
Observe a linha de código em destaque:
this.nome = nome;
A forma mais fácil de ler essa linha seria “o nome deste objeto receberá o nome passado como parâmetro”.
Algumas classes apresentam vários construtores diferentes, cada um deles com um conjunto de parâmetros distintos, de forma a se adaptarem a contextos diversos, e a aplicação dessa técnica é chamada de sobrecarga.
Embora nós utilizemos muito a sobrecarga na orientação a objetos, principalmente na criação de métodos construtores, esta é uma técnica antiga, já existente na programação estruturada, e amplamente utilizada na linguagem C.
Pacotes e Bibliotecas
Quando fazemos um sistema orientado a objetos é comum ocorrer um grande número de classes, e precisamos de uma maneira organizada para classificar e agrupar essas classes de acordo com suas afinidades a determinado contexto.
O primeiro nível de organização são os pacotes (package), que seria equivalente ao diretório no qual a classe se encontra, segundo um caminho relativo, tomando como base o diretório raiz do projeto.
Exemplo
O diretório “\java\math” equivaleria ao pacote “java.math”.
Se observarmos os códigos de exemplo anteriores, veremos a presença da palavra package, imediatamente no início dos mesmos. Todo arquivo Java deve ter a assinatura de pacote correspondente ao diretório onde se encontra.
Quando utilizamos classes pertencentes ao mesmo pacote não há necessidade de importar elementos, mas ao tratar de pacotes diferentes, será necessário importar elementos de outro pacote com o uso de import.
Podemos importar classes dos pacotes do projeto ou de bibliotecas externas.
As bibliotecas são conjuntos de pacotes Java, e suas respectivas classes, compactados para um arquivo com extensão “.zip” ou “.jar”. Essas bibliotecas podem ser referenciadas pelo projeto, ou adicionadas ao CLASSPATH, no qual estão relacionadas as bibliotecas padrão do ambiente.
Exemplo
Se quisermos acessar um banco de dados MySQL, podemos adicionar o arquivo mysql-connector.jar ou similar às bibliotecas do projeto. Depois é só utilizar as importações corretas.
O comando import pode ser usado de duas formas:
Incluindo apenas uma classe, como import java.util.ArrayList
ou
Incluindo todas as classes do pacote (sem recursividade), como import java.util.*
Herança
O segundo pilar da orientação a objetos é a herança, que nos permite criar uma nova classe a partir de outra já existente, configurando um reaproveitamento estrutural.
A árvore de família de animais que nos é apresentada nas aulas de Biologia.
Ela segue o mesmo princípio da herança em termos de programação, pois ao definir um cachorro ou coelho aproveitamos o conceito já existente de mamífero.
Para derivar uma classe de outra utilizaremos a palavra reservada extends, e assim como podemos acessar os atributos e métodos internos com this, utilizamos a palavra super para ter acesso aos atributos e métodos da classe original.
Vamos observar o exemplo seguinte.
 
 
package exemplo010;
public class Profissional extends Pessoa{
  String profissao;
  Profissional(String nome, String telefone, String profissao) {
    super(nome, telefone);
    this.profissao = profissao;
  }
}
	
Aqui nós criamos uma classe Profissional descendente de Pessoa com o uso de extends. Esta nova classe teve o atributo “profissao” adicionado.
Dica
Não é permitido utilizar acentuação em nomes de atributos, e também um construtor com três parâmetros.
A nova classe apresenta os atributos nome, telefone e profissao, sendo os dois primeiros herdados de Pessoa, e a palavra super foi utilizada para chamar o construtor de Pessoa a partir do construtor de Profissional.
Encapsulamento
Quando consideramos uma estrutura fechada qualquer, observamos que alguns elementos são visíveis, enquanto outros não.
Exemplo
Ao observarmos uma pessoa nós podemos ver sua face, seu cabelo, seus braços, mas não temos acesso ao seu estômago nem pulmão.
Na orientação a objetos temos esta mesma característica, pois classes definem estruturas fechadas, nas quais o acesso a seus atributos e métodos deve ser controlado, e para tal iremos contar com três níveis de acesso:
· Público (public)
· Privado (private)
· Protegido (protected)
Público (public)
Permite que qualquer um acesse o atributo ou método.
Quando não definimos nada em termos de nível de acesso, o padrão é o de pacote, ou seja, todas as classes do mesmo pacote podem acessar livremente, mas se ocorrer a importação desse pacote por uma classe pertencente a outro, essa classe externa não terá acesso aos atributos e métodos com nível de pacote. 
Mais uma pequena alteração em nossa classe Pessoa... 
 
 
package exemplo010;
public class Pessoa{
  private String nome;
  private String telefone;
  public Pessoa(String nome, String telefone){
    this.nome = nome;
    this.telefone = telefone;
  }
  public void exibir(){
    System.out.println(nome+"::"+telefone);
  }
}
	
Essa alteração não impactará na classe chamadora, pois o construtor e o método exibir estão definidos como públicos, mas se tentarmos acessar os atributos de p1 ou p2 a partir do main, como na primeira versão, ocorrerá um erro de compilação. 
p1.nome = "João"; // Esta linha irá gerar um erro de compilação
É muito comum que precisemos controlar o acesso a determinado atributo sem, no entanto, impedir esse acesso, e nesse ponto devemos utilizar os getters e setters. 
Esse controle é chamado de encapsulamento, que trata de mais um dos pilares da orientação a objetos e, em termos formais, visa ocultar detalhes da implementação interna da classe, fornecendo apenas uma interface com métodos de negócio e métodos de acesso.
O método getter serve para obter o valor de um atributo interno, enquanto o setter permite escrever um valor nesse atributo. O conjunto dos dois define uma propriedade da classe.
Podemos implementar muito facilmente a técnica de encapsulamento em nossa classe Pessoa modificada.
 
 
package exemplo010;
public class Pessoa{
  public String getNome(){
    return nome;
  }
  public void setNome(String nome){
    this.nome = nome;
  }
  public String getTelefone(){
    return telefone;
  }
  public void setTelefone(String telefone){
    this.telefone = telefone;
  }
  private String nome;
  private String telefone;
  public Pessoa(String nome, String telefone){
    this.nome = nome;
    this.telefone = telefone;
  }
  public void exibir(){
    System.out.println(getNome()+"::"+getTelefone());
  }
}
	
Note que foram apenas acrescentados os métodos de leitura (getNome e getTelefone) e os métodos de escrita (setNome e setTelefone). Para não ocorrer perda de semântica devido a alteração de nomes, o uso de this é necessário nos setters.
Polimorfismo
ondemand_videoVídeo.
O último dos pilares daorientação a objetos é o polimorfismo, consistindo na possibilidade de uma classe descendente efetuar alterações sobre métodos herdados, de forma a adaptar as funcionalidades a seu próprio contexto.
Esta talvez seja a característica mais poderosa da orientação a objetos, proporcionando grande flexibilidade a qualquer sistema, e devemos compreendê-la corretamente para tirar o melhor proveito da metodologia.
Como já é de nosso conhecimento, por meio do processo de herança, as classes descendentes herdam todas as caraterísticas existentes nas ancestrais.
Mas e se algumas ações não forem exatamente o que se espera?
É nesse ponto que precisamos do polimorfismo2.
Observe o diagrama seguinte.
Este é um diagrama de classes da UML (Unified Modeling Language) representando a classe Profissional e descendentes diretos e indiretos da mesma.
Tomando como base Profissional, podemos dizer que todo objeto deste tipo irá trabalhar, assim como os objetos das classes descendentes, mas certamente irão trabalhar de forma diferente.
· se for um engenheiro pode estar indo fazer o projeto de uma casa ou de um carro;
· no caso do médico talvez esteja indo operar alguém;
· e um advogado estará acompanhando algum processo.
No entanto, a frase seria a mesma: “Estou indo trabalhar”.
O uso de polimorfismo permitirá alterar a funcionalidade do método trabalhar para as diversas classes envolvidas.
Vamos observar um código com uso de polimorfismo, com a alteração da nossa classe Profissional de exemplo. Essa alteração é necessária para que o método exibir passe a mostrar também a profissão, e não apenas o nome e telefone.
 
 
public class Profissional extends Pessoa{
  private String profissao;
  public Profissional(String nome, String telefone, String profissao) {}
    super(nome, telefone);
    this.profissao = profissao;
  }
  @Override
  public void exibir() {
    super.exibir();
      // Chama o exibir de Pessoa, imprimindo nome e telefone
    System.out.println("\tTrabalha como "+profissao);
      // Complementa a informação acerca da profissão
  }
}
	
Note que o novo método exibir chama o original através do uso de super.
Observe também a presença da anotação @Override, que deve ser colocada nos métodos polimórficos e significa sobrescrito, não devendo nunca ser confundido com a sobrecarga, cujo nome em inglês seria overload.
A seguir podemos observar um pequeno teste com chamadas ao método exibir.
 
 
package exemplo010;
public class Exemplo010a {
  public static void main(String[] args) {
    Pessoa[] pessoas = {new Pessoa("Joao","1111-1111"),
      new Pessoa("Maria","2222-2222"),
      new Profissional("Luiz","3333-3333","Advogado")};
    for(int i=0; i< 3; i++)
      pessoas[i].exibir();
  }
}
	
Note que foi criado um vetor de Pessoa, aceitando tanto Pessoa quanto Profissional. Isto se deve a uma lei da POO que define o seguinte: “qualquer descendente pode ser utilizado no lugar da classe”.
Se observarmos o mundo real, esta lei também pode ser aplicada, pois um profissional é uma pessoa, embora nem sempre uma pessoa seja um profissional.
A saída desse programa será a seguinte:
Joao::1111-1111
Maria::2222-2222
Luiz::3333-3333
Trabalha como Advogado
Note que mesmo definido o vetor como do tipo Pessoa, o exibir correto foi chamado para o Profissional existente neste vetor, e isso se deve ao polimorfismo.
Classes Abstratas e Interfaces
ondemand_videoVídeo.
Normalmente confundimos muito as palavras abstração e abstrato, mas elas apresentam sentidos muito distintos:
Uma abstração é um modelo simplificado de algo. 
Um elemento abstrato é algo intangível, que não é palpável ou concreto.
Existem dois tipos de elementos abstratos em Java: classes abstratas e interfaces.
As classes abstratas são incompletas, não podem gerar objetos, mas servem de base para impor determinada característica funcional a seus descendentes, que serão obrigados a implementar as funcionalidades abstratas para gerar objetos.
Exemplo
Um elemento abstrato da vida real seria mamífero, enquanto cachorro e gato são elementos concretos. Mesmo não podendo gerar objetos, o elemento mamífero define uma regra para seus descendentes, ou seja, para descender de mamífero precisa saber mamar.
Vamos observar um exemplo de classe abstrata em Java envolvendo a criação de quatro classes.
 
 
public abstract class Mamifero {
  protected String familia;
  public abstract void mamar();
  public String getFamilia(){
    return familia;
  }
}
	
 
 
public class Cachorro extends Mamifero{
  public Cachorro() {
    familia = "Canidae";
  }
  @Override
  public void mamar() {
    System.out.println("Cachorro mamando...");
  }
}
	
 
 
public class Gato extends Mamifero{
  public Gato() {
    familia = "Felidae";
  }
  @Override
  public void mamar() {
    System.out.println("Gato mamando...");
  }
}
	
 
 
public class Exemplo011 {
  public static void main(String[] args) {
    Mamifero m = new Gato();
    m.mamar();
  }
}
	
Nós criamos inicialmente a classe abstrata Mamifero, com uso da palavra abstract em termos da definição da classe e do método abstrato interno.  
Atenção
Essa classe não pode gerar instâncias, afinal falta uma parte dela que é o método mamar( ), mas permite herança e obriga a definição do método faltante. Isso pode ser observado nas classes Cachorro e Gato, descendentes de Mamifero, e que por serem concretas podem ser instanciadas. 
Ao final, podemos ver uma classe de teste, onde é instanciado um Gato no lugar de Mamifero, sendo chamado em seguida o método mamar( ) implementado na classe descendente. Como esperado, teremos a impressão da frase “Gato mamando...”. 
Outro tipo de elemento abstrato é a interface, e esta não deve ser confundida com interface visual, que trata da interação com o usuário.
Aqui estamos preocupados com a interface programacional, que se preocupa em definir quais métodos estarão disponíveis para uso de determinado tipo de componente.
Em termos práticos, uma interface é um conjunto de assinaturas de métodos abstratos, os quais devem ser implementados pelas classes que se proponham a tal. Ao implementar esses métodos, as classes passam a ser reconhecidas por algum ferramental que os utiliza, promovendo funcionalidades específicas.
As interfaces são de grande utilização no Java, e podemos observar um exemplo a seguir, complementando o exemplo anterior.
 
 
public interface Voo {
  void voar();
}
	
 
 
public class Morcego extends Mamifero implements Voo{
  public Morcego() {
    familia = "Phyllostomidae";
  }
  @Override
  public void mamar() {
    System.out.println("Morcego mamando...");
  }
  @Override
  public void voar() {
    System.out.println("Morcego voando...");
  }
}
	
 
 
public class Exemplo011a {
  public static void main(String[] args) {
    Object[] objetos = {new Gato(), new Morcego(),
                       new Cachorro()};
    for(int i=0; i< 3; i++) {
      if(objetos[i] instanceof Voo)
        ((Voo)objetos[i]).voar();
          // Conversão de tipo (type cast) necessária
    }
  }
}
	
Neste exemplo temos a definição de uma interface, com o uso da palavra interface no lugar de class. Uma interface é naturalmente abstrata, logo seus métodos são considerados por padrão públicos e abstratos.
Uma observação é a de que as interfaces não aceitam atributos, e podem ser consideradas como conjuntos de assinaturas de métodos.
Podemos observar que a classe Morcego deriva de Mamifero e implementa a interface Voo com o uso da palavra implements. Ao implementar a interface, ela se torna obrigada a implementar o método voar( ).
Ao final temos um pequeno teste, onde é percorrido um vetor de objetos, e perguntado a cada um deles se implementa Voo ou não com o uso de instanceof, e caso implemente, ocorre a conversão (type cast) para o tipo da interface e a subsequente chamada de voar ( ).
Executando o exemplo teremos apenas a frase “Morcego voando...” sendo impressa.
Dica
Note que implementar é muito diferente de herdar. Nesse caso fica claro que Morcego é um Mamifero, e que ele implementa Voo, mas nãoherda do mesmo.
Embora muitos confundam as interfaces com classes abstratas, as diferenças em termos metodológicos são muito grandes:
As classes abstratas definem uma regra para sua família de classes.
clear
As interfaces podem ser implementadas por qualquer classe que necessite participar de algum processo específico.
Atividade
1. Qual das características da Orientação a Objetos permite a alteração funcional de um método herdado, permitindo grande flexibilidade e adaptabilidade ao ambiente?
a) Sobrecarga
b) Construtores
c) Encapsulamento
d) Polimorfismo
e) Destrutores
Gabarito
2. Segundo a sintaxe do Java, qual a palavra utilizada para indicar que uma classe é descendente de outra?
a) extends
b) implements
c) super
d) this
e) private
Gabarito
3. Você criou um novo sistema de finanças e, como estratégia de desenvolvimento, resolveu criar um núcleo de cálculo adaptável, e as classes que desejarem utilizar as funcionalidades desse núcleo deverão implementar um conjunto de métodos abstratos, podendo pertencer a qualquer família de classes. Qual elemento de programação você deverá utilizar para viabilizar essa funcionalidade?
a) Interface Programacional
b) Classe Abstrata
c) Classe Concreta
d) Mecanismo de Herança
e) Sobrecarga
Aula 3: Exceções e elementos comportamentais
Apresentação
Algo que devemos estar atentos na criação de um sistema é a possibilidade de ocorrência de erros durante a execução. Precisamos trata-los, e o Java traz uma estrutura organizada para esse tratamento baseada no uso de classes de exceção e instruções próprias. 
No entanto, ao observarmos, conseguimos diferençar qual o tipo e modelo da arma de fogo longo? Diversas opiniões surgem para identificar a arma de fogo. Fuzil? Rifle? Espingarda? Carabina?
Estes são assuntos de relevância para qualquer programador que pretenda construir sistemas comerciais mais robustos.
Objetivos
· Explicar a sintaxe Java para o Tratamento de Exceções;
· Demonstrar a Modelagem Comportamental no Java;
· Aplicar a biblioteca de Coleções Genéricas do Java.
Exceções
Muitas vezes temos que preparar nossos sistemas para a possibilidade de ocorrência de erros durante a execução, podendo ocorrer pela indisponibilidade de algum recurso, como rede ou espaço em disco, pela entrada de dados em formato incorreto, entre diversas outras situações.
 Outro assunto muito relevante para os sistemas atuais é a utilização de modelagem comportamental, aumentando o reuso de processos e estruturas, o que pode ser feito no ambiente Java por meio de classes genéricas ou pelo uso de anotações. Inclusive, uma importante biblioteca Java para o manuseio de coleções apresenta uma implementação atual baseada em classes genéricas.
Nos ambientes orientados a objetos, os diversos erros de execução são encapsulados em classes especiais denominadas exceções.
Temos a classe básica Exception, que define apenas um código de erro e uma mensagem, voltada para erros genéricos.
A partir desta classe, e utilizando o mecanismo de herança, são definidas exceções mais específicas, com maior gama de informações acerca do erro.
Para tratarmos uma exceção devemos utilizar uma estrutura de fluxo específica, baseada no comando try (tentar), o qual utiliza a seguinte sintaxe:
try {
  // Bloco de comandos protegido
} catch (IOException e1) {
  // Tratamento de erro de IO
} catch (Exception e2) {
  // Tratamento de erro genérico
} finally {
  // Execução obrigatória, independente da ocorrência de erros
}
Inicialmente é executado o bloco protegido pelo comando try, e se ocorre uma exceção do tipo IOException, ela é capturada em e1 com o uso de catch(IOExeption e1), sendo executado o tratamento de erro de IO e seguindo para o bloco do finally.
Para qualquer outro tipo de exceção, será executado o bloco para tratamento de erro genérico, com a captura do erro em e2, e seguindo para finally ao término. Lembrando que, segundo a orientação a objetos, qualquer descendente pode ser utilizado no lugar da classe original, o que viabiliza essa funcionalidade.
Se invertermos a ordem dos blocos catch ocorrerá um erro de compilação, pois o fato de Exception ser mais genérico impediria a captura de quaisquer outras exceções.
Quanto ao bloco finally, mesmo sem ocorrência de erros, ele será executado.
Um exemplo prático no qual utilizaríamos essas instruções seria na inserção de dados em um banco, onde devemos abri-lo, inserir os registros e fechar o banco. Ocorrendo ou não erros durante a inserção, o banco deve ser fechado.
ABRIR_BANCO_DE_DADOS( );
try {
   INSERIR( );
   INSERIR( );
   // Diversos comandos de inserção
} catch (Exception e) {
    // Tratamento de erro genérico
} finally {
FECHAR_BANCO_DE_DADOS( );
}
Para qualquer exceção não silenciosa, o compilador considerará como erro caso a possibilidade de ocorrência da mesma não seja tratada. Nesses casos precisamos utilizar a estrutura try..catch, ou avisar ao compilador que não será tratado, mas sim ecoado para o chamador do método, por meio da assinatura com throws.
Também podemos criar nossas próprias exceções, de forma a utilizá-las como sinalizações de erros específicos de nossos sistemas.
 
 
public class ErroCalc extends Exception{
  public ErroCalc(int a, int b){
    super("Erro com os numeros "+a+" e "+b);
  }
}
	
Em nossa exceção personalizada (ErroCalc) temos um construtor que recebe dois valores inteiros, e chama o construtor de Exception, com o uso de super, passando a mensagem correta.
Esta exceção poderia ser utilizada em uma classe de nosso sistema.
 
 
public class Calculadora {
  public int somar(int a, int b){
    return a+b;
  }
  public int dividir(int a, int b) throws ErroCalc {
    if(b==0)
      throw new ErroCalc(a, b);
    return a/b;
  }
}
	
No caso da classe Calculadora, temos os métodos somar, onde não ocorre exceção, e dividir, que gera ErroCalc quando o parâmetro b tem valor zero. Note que não basta alocar o objeto ErroCalc com new, sendo necessário o uso do comando throw para lançar a exceção.
  Caso não ocorra tratamento com try..cath, ao lançar ErroCalc a execução de dividir é imediatamente interrompida, retornando ao chamador, sendo também necessário informar ao compilador que esse método pode gerar uma exceção desse tipo com o uso do comando throws em sua assinatura.
Podemos testar facilmente nossa exceção com o uso de um objeto do tipo Calculadora.
 
 
public class TesteCalc {
  public static void main(String[] args) {
    Calculadora c1 = new Calculadora();
    try {
       System.out.println(c1.somar(2, 3));
       System.out.println(c1.dividir(6, 3));
       System.out.println(c1.dividir(6, 0));
    } catch (ErroCalc e)
       System.out.println(e.getMessage());
    }
  }
}
	
Neste exemplo, a condição que irá gerar a exceção será a chamada c1.dividir(6,0), mas mesmo que não colocasse o valor zero, o compilador exigiria o tratamento, já que dividir assinala uma possibilidade de exceção com throws ErroCalc.
A saída que obteremos será a seguinte:
O que é Modelagem Comportamental?
Em muitas situações não conseguimos expressar a realidade por meio de simples objetos.
Exemplo
Somos capazes de modelar um carro, e a partir daí utilizarmos dois, três, ou até algumas dezenas de objetos desse tipo, mas como faríamos para modelar um engarrafamento no trânsito?
Para uma situação desse tipo, não importa qual carro esteja entrando ou saindo, mas sim que o trecho esteja engarrafado. Logo, não são os objetos que definem a abstração principal, mas sim o comportamento.
Poderíamos dizer o mesmo sobre uma fila. Você pode ter uma sequência de pessoas, elefantes, carros, ou qualquer outra coisa, e todas essas sequencias organizadas seriam filas, independente dos objetos que as constituem.
Photo by Slava Bowman on Unsplash
Essa generalização também pode ser feita para uma pilha de pratos ou de livros, pois não importa de qual tipo de objeto se trate, e sim que estejam empilhados.
Na programação utilizamos diversos comportamentos, normalmente definidos por estruturas de dados, como as próprias Filas e Pilhas, por exemplo.
Em termosde orientação a objetos, existem duas ferramentas que permitem a implementação de modelos comportamentais: Classes Genéricas e Anotações.
Classes Genéricas
As classes genéricas, também chamadas de classes template, já tinham sido idealizadas no início da orientação a objetos, e eram comuns em linguagens como o C++, mas veio para o Java em versões um pouco mais recentes por intermédio dos Generics.
Quando criamos uma classe genérica estamos modelando um comportamento com lacunas bem definidas ao qual serão atribuídas classes para preencher tais lacunas e completar a funcionalidade abstrata previamente definida.
Vamos observar um exemplo.
 
 
package exemplo020;
  public class Pilha‹;K›; {
   class NoPilha‹;K›; {
      K dado;
      NoPilha‹;K›; proximo;
   }
   private NoPilha‹;K›; topo = null;
   public void empilhar(K dado){
      NoPilha‹;K›; novo = new NoPilha<>();
      novo.dado = dado;
      novo.proximo = topo;
      topo = novo;
   }
   public K desempilhar(){
      if(topo==null)
         return null;
      NoPilha‹;K›; antigo = topo;
      topo = topo.proximo;
      return antigo.dado;
   }
}
	
Neste exemplo foi definida uma classe genérica de Pilha, com os métodos para empilhar e desempilhar (push/pop), podendo ser observada a presença da lacuna definida pela letra K. Na verdade poderia ser qualquer letra, devendo apenas ser colocada junto ao nome da classe.
public class Pilha‹K›
A letra K representa algo genérico e será substituída em todas as ocorrências posteriores quando definirmos o tipo de objeto a ser utilizado.
Por exemplo, se utilizarmos String, o método empilhar pode ser lido da seguinte forma:
public void empilhar(String dado){
     NoPilha ‹String› novo = new NoPilha‹›();
     novo.dado = dado;
     novo.proximo = topo;
     topo = novo;
}
É interessante observar também a definição de uma inner class chamada NoPilha, ou seja, uma classe utilitária interna, que só pode ser utilizada na programação da classe Pilha. Na prática ela auxilia na construção de uma estrutura de pilha típica, onde um valor é colocado no topo, e deve apontar para o elemento logo abaixo na pilha (proximo).
Fonte: https://www.tutorialspoint.com/data_structures_algorithms/stack_algorithm.htm
Sempre devemos lembrar que a pilha trata de uma estrutura LIFO (Last In First Out), o que significa que o último a entrar é o primeiro a sair. Isto faz sentido se fizermos uma analogia com uma pilha de pratos, já que não é possível pegar o prato de baixo sem que os demais caiam.
Agora podemos criar um programa para testar nossa classe de Pilha.
 
 
package exemplo020;
public class Exemplo020 {
   public static void main(String[] args) {
      Pilha pilha1 = new Pilha<>();
      pilha1.empilhar(5);
      pilha1.empilhar(7);
      pilha1.empilhar(9);
      Integer x;
      while((x=pilha1.desempilhar())!=null)
      System.out.println(x);
   }
}
	
Esse programa inicialmente cria uma Pilha de elementos Integer (classe wrapper para ı), e em seguida empilha os valores 5, 7 e 9.
Observando o que ocorre a seguir, vemos a declaração de uma variável x que receberá os valores desempilhados, enquanto esse valor for diferente de nulo, imprimindo x a cada rodada.
Por se tratar de uma estrutura LIFO, é natural que os números sejam impressos na ordem inversa da entrada, ou seja, serão impressos os valores 9, 7 e 5.
Coleções
As coleções são de grande importância para o Java, sendo organizadas pela Oracle em um grupo denominado JCF (Java Collections Framework), o qual foi todo implementado com o uso de classes genéricas.
Ao contrário de vetores, que apresentam um número de elementos fixo, as coleções funcionam internamente como listas encadeadas, e permitem que objetos sejam adicionados ou removidos a qualquer momento.
Nós podemos verificar a relevância dessa família de classes pelo simples fato de ter sido criada uma nova funcionalidade para a estrutura for baseada em coleções, e que posteriormente foi expandida para vetores.
Em outras linguagens existem estruturas foreach, com funcionamento similar, e a leitura que devemos fazer é “para cada elemento pertencente à coleção”.
A sintaxe básica seria a seguinte:
for( Classe obj: Coleção ‹Classe› ) {
     // Bloco de instruções
}
Uma Collection é uma classe abstrata e que, portanto, não pode ser utilizada diretamente, mas nós podemos instanciar os seus diversos descendentes com funcionalidades específicas.
Podemos observar, a seguir, um exemplo de uso de ArrayList, descendente de Collection.
 
 
package exemplo021;
import java.util.ArrayList;
public class Exemplo021 {
   public static void main(String[] args) {
      ArrayList‹String› lista = new ArrayList<>();
      lista.add("Primeiro");
      lista.add("Segundo");
      lista.add("Terceiro");
      for(String x: lista){
         System.out.println(x);
      }
   }
}
	
Neste exemplo é criada uma coleção do tipo ArrayList, com elementos do tipo String, e em seguida adicionamos três elementos nessa coleção com o uso do método add.
Depois de adicionados os elementos, podemos percorrer a lista com uso do for, e a variável x receberá o primeiro valor da lista na primeira rodada, em seguida, o segundo e finalmente o terceiro, imprimindo o valor recebido a cada rodada.
 Com isso teremos a seguinte saída:
Nas versões atuais do Java é possível também utilizar operadores lambda, segundo o paradigma funcional, e o trecho voltado para a impressão dos valores poderia ser escrito da seguinte forma:
lista.forEach((x) -> {
     System.out.println(x);
     });
Os principais métodos do ArrayList‹E› são apresentados no quadro abaixo:
	Método
	Retorno
	boolean add(E e)
	Adiciona o elemento ‹strong›e‹/strong› ao final da lista.
	void clear( )
	limpa a lista.
	boolean contains(Object o)
	Retorna ‹strong›true‹/strong› caso o objeto se encontre na lista.
	E get(int index)
	Retorna o elemento na posição especificada por ‹strong›index.‹/strong›
	boolean remove(Object o)
	Remove o objeto da lista se ele estiver lá.
	E remove(int index)
	Remove o elemento na posição ‹strong›index‹/strong› e retorna o mesmo.
	int size( )
	Retorna o tamanho da lista.
Dentre as diversas outras classes do JCF, além do ArrayList outra de grande utilização é HashMap. Com o uso desta classe é possível estabelecer relações do tipo chave-valor com muita facilidade.
Exemplo
Se quisermos fazer um controle no qual temos o código de nossos produtos no formato inteiro e os nomes no formato texto, poderemos declarar o HashMap da seguinte forma:
HashMap ‹ Integer, String > produtos = new HashMap‹›( );
Observando a declaração do HashMap, vemos que devem ser colocadas na tipificação dele a classe da chave e a classe do dado (valor).
 
 
package exemplo022;
import java.util.HashMap;
import java.util.Scanner;
public class Exemplo022 {
   public static void main(String[] args) {
      Scanner teclado = new Scanner(System.in);
      HashMap‹Integer,String› produtos = new HashMap<>();
      int opcao;
      do{
         System.out.println("Digite 1 para incluir, "+
                           "2 para consultar, 0 para sair");
         opcao = teclado.nextInt();
         switch(opcao){
            case 1:
               System.out.println("Código do novo produto:");
               int codigoN = teclado.nextInt();
               System.out.println("Nome do novo produto:");
               String nomeN = teclado.next();
               produtos.put(codigoN, nomeN);
               break;
            case 2:
               System.out.println("Digite o código:");
               int codigo = teclado.nextInt();
               String nome = produtos.get(codigo);
               if(nome!=null)
                  System.out.println(nome);
               break;
         }
      } while(opcao!=0);
   }
}
	
Nesse programa utilizamos uma estrutura de repetição do tipo do..while, onde é solicitada ao usuário qual ação ele deseja efetuar, sendo 1 referente à inclusão de um produto, 2 para a consulta e 0 para encerrar a execução.
Ao escolher a opção 1, será executado o primeiro bloco do switch..case,onde iremos solicitar o código e o nome do novo produto, procedendo a inclusão dele no HashMap com o uso do método put.
Se a opção escolhida for 2, solicitaremos o código do produto e buscaremos o nome dele no HashMap por meio do método get, o qual poderá retornar o nome, ou nulo no caso em que a chave não estiver presente nesse HashMap.
Podemos observar, a seguir, uma possível saída para esse programa.
Os principais métodos do HashMap ‹K,V› são apresentados no quadro seguinte:
	Método
	Retorno
	void clear( )
	Limpa o mapa.
	boolean containsKey(Object key)
	Verifica se o mapa contém a chave especificada.
	boolean containsValue(Object value)
	Verifica se o mapa contém o valor especificado.
	V get(Object key)
	Retorna o valor correspondente à chave.
	V put(K key, V value)
	Associa o valor à chave no mapa.
	V remove(Object key)
	Remove o mapeamento para a chave especificada.
	int size( )
	Retorna a quantidade de mapeamentos.
	Collection ‹V› values( )
	Retorna uma coleção com os valores mapeados.
Nós vimos aqui apenas os dois principais elementos do JCF, mas existem muitas outras classes disponíveis nessa biblioteca.
É interessante observar também que, como estruturas de dados, tais elementos podem ser combinados para criar, por exemplo, listas de mapas de listas.
ArrayList‹ HashMap‹ Integer, ArrayList‹String› > >
Esta estrutura de exemplo, um pouco mais complexa, permitiria guardar coleções de tabelas de mapeamento, onde cada mapeamento se refere a uma coleção.
Exemplo
Poderíamos ter uma coleção dos setores da empresa, onde cada setor seria um mapeamento de departamentos e coleções de pessoas que trabalham nesses departamentos.
Reflexividade Computacional
Podemos definir reflexividade computacional como a habilidade de um objeto conhecer a si próprio, sua estrutura e a utilização de seus métodos.
Na linguagem Java os objetos derivam direta ou indiretamente da classe Object, que seria a classe base de toda a plataforma. No entanto, existe outra classe chamada Class, que permite observar a estrutura de qualquer objeto por meio da reflexividade.
Atenção
Não confundir a palavra reservada class (minúsculo) para definição de classes com a classe Class (maiúsculo) para gerência de classes.
Por meio de Class podemos obter informações acerca dos atributos da classe em objetos do tipo Field e de seus métodos por meio de objetos Method e Parameter.
Além de consultar as informações, podemos mudar o valor de atributos e invocar métodos com o uso destas classes.
Vamos observar um pequeno exemplo, com uma classe Pessoa definida de forma simples.
 
 
package exemplo023;
public class Pessoa {
     public String nome;
     public String telefone;
     public void exibir(int quantidade){
          for(int i=0; i ‹ quantidade; i++)
               System.out.println(nome+"::"+telefone);
     }
}
	
 
 
package exemplo023;
import java.lang.reflect.Field;
public class Exemplo023 {
     public static void main(String[] args) throws Exception {
          Object objeto =
               Class.forName("exemplo023.Pessoa").newInstance();
          Class classe = objeto.getClass();
          // Reconhecendo os atributos do objeto...
          for(Field f: classe.getFields())
              System.out.println(f.getName()+"::"+f.getType());
          // Alterando os valores e invocando o método...
          classe.getField("nome").set(objeto,"João");
          classe.getField("telefone").set(objeto,"1111-1111");
          classe.getMethod("exibir", int.class).invoke(objeto, 2);
     }
}
	
Note que no exemplo o objeto da classe Pessoa é criado a partir de seu nome completo, incluindo o pacote, no formato texto, e que conseguimos extrair os atributos com objetos Field. Qualquer nome de classe que fosse utilizado iria funcionar e mostraria os atributos definidos no âmbito da classe.
Uma observação a ser feita é a de que o método main acrescenta throws Exception em sua assinatura. Isto é necessário porque o método forName pode gerar uma exceção caso não encontre o nome da classe passada como parâmetro no CLASSPATH.
Além de obter os dados acerca dos atributos, também alteramos os valores dos campos com o comando set. Abaixo, podemos observar uma linha com método set desdobrada para melhor compreensão.
Field f = classe.getField("nome");
     // Captura o atributo nome da classe no Field f
f.set(objeto, "João");
     // Utiliza o método set para alterar o valor em objeto.
Finalmente, invocamos um método passando parâmetros para o mesmo com o uso do invoke. Novamente vamos desdobrar a linha para facilitar a compreensão.
Method m = classe.getMethod("exibir", int.class);
     // Captura a versão do método exibir que recebe um valor
     // inteiro como parâmetro
m.invoke(objeto,2);
     // Invoca este método passando o valor 2 para o parâmetro.
Podemos observar, a seguir, a saída esperada para a execução desse código:
Anotações
Por que falamos acerca de reflexividade computacional?
Certamente é um assunto complexo, mas necessário para compreender os fundamentos da criação e utilização de anotações.
As anotações são metadados anexados às classes que podem ser reconhecidos por ferramentas externas para as mais diversas finalidades. Atualmente, é comum seu uso em diversos frameworks, principalmente os de persistência de dados.
Sem a reflexividade computacional essas ferramentas não conseguiriam reconhecer tais anotações em meio ao código da classe.
 
Essa é uma modelagem comportamental que segue o caminho inverso das classes genéricas, pois nessas primeiras as classes são aplicadas ao comportamento, enquanto nas anotações iremos aplicar o comportamento às classes.  
 
 
package exemplo024;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(value=RetentionPolicy.RUNTIME)
@Target(value=ElementType.TYPE)
public @interface Autoria {
    String autor();
    int ano();
    String empresa() default "UNESA";
}
	
Para definir uma anotação precisamos utilizar o comando @interface, além de definir a utilização dela com @Target, e neste exemplo indica que a anotação será voltada para classes, mas que poderiam ser diversos outros tipos, como métodos e atributos, bem como o escopo de utilização com @Retention, colocado aqui como em tempo de execução.
Para essa anotação criamos três campos, sendo que dois são de preenchimento obrigatório e um opcional, no caso empresa, já que é definido um valor padrão com default.
Após criar nossa interface de anotação, podemos aplicá-la a uma classe.
 
 
package exemplo024;
@Autoria(autor = "Denis", ano = 2018)
public class Carro {
      // O código interno da classe não será criado
}
	
Note que o uso da aplicação é bastante simples, assemelhando-se muito a um comentário, mas necessitando sempre do preenchimento de campos obrigatórios.
O passo final é o mais complexo, justamente por necessitar de elementos de reflexividade computacional, e trata do reconhecimento das anotações pelas ferramentas externas.
O processo demonstrado a seguir é o mesmo utilizado pelos frameworks na automatização de diversas atividades.
 
 
package exemplo024;
  public class Exemplo024 {
     public static void main(String[] args) {
      Object[] objetos = {new Carro(), "XPTO" };
      for(Object obj: objetos){
         Class c1 = obj.getClass();
         if(c1.isAnnotationPresent(Autoria.class)){
            Autoria a1 =
                 (Autoria)c1.getAnnotation(Autoria.class);
            System.out.println("Classe " + c1.getName() +
                 " escrita por " + a1.autor() + " em " +
                 a1.ano());
         } else {
            System.out.println("Classe " + c1.getName() +
               " sem autoria definida");
         }
      }
   }
}
	
Incialmente definimos um vetor de objetos, recebendo um Carro e uma String, e nesse caso apenas o Carro traz a anotação de Autoria.
Ao percorrer o vetor, para cada Object do mesmo iremos obter o Class, e em seguida perguntar se a anotação de Autoriaestá presente.
if(c1.isAnnotationPresent(Autoria.class))
Estando presente iremos capturar a anotação encontrada, sendo necessário uma conversão de tipo, já que o método retorna uma anotação de forma genérica.
Autoria a1 = (Autoria)c1.getAnnotation(Autoria.class); // Conversão de tipo com (Autoria)
Com isso conseguimos imprimir os valores digitados para autor e ano, ou a mensagem de que a autoria não está definida quando a anotação não está presente.
Podemos observar a saída do programa a seguir:
Classe exemplo024.Carro escrita por Denis em 2018
Classe java.lang.String sem autoria definida
Atividade
1. Em muitas situações, mas em particular quando estamos criando uma biblioteca de componentes, não queremos necessariamente tratar uma exceção ocorrida, apenas ecoá-la ao processo chamador para que este sim efetue o tratamento. Qual a palavra reservada utilizada na assinatura de um método qualquer para ecoar a exceção?  
a) catch
b) throw
c) finally
d) try
e) throws
Gabarito comentado
2. Você desenvolveu um sistema e descobriu que as atividades de inserção, exclusão e consulta aos dados era extremamente repetitiva em termos de programação. Com isso resolveu generalizar a solução, ao invés de criar dezenas de classes similares. Baseado no esqueleto de uma dessas classes, e sabendo que todas as chaves são inteiras, faça a implementação inicial da classe genérica que daria suporte às mesmas.
public class PessoaDAO {
   public void inserir(Pessoa entidade){ }
   public void excluir(Integer chave){ }
   public Pessoa buscar(Integer chave){ }
   public ArrayList‹Pessoa› buscarTodos( ){ }
}
Gabarito
Sugestão de implementação:,
 
public class GenericDAO‹K,E› {
   public void inserir(E entidade){ }
   public void excluir(K chave){ }
   public E buscar(K chave){ }
   public ArrayList‹E› buscarTodos( ){ }
}
Sugestão para a classe PessoaDAO como objeto direto:
GenericDAO%8Integer,Pessoa> pessoaDAO = new GenericDAO‹›();
Sugestão para utilização de herança:
public class PessoaDAO extends GenericDAO{}
3. Ao definir um novo sistema você se encontra perante uma situação na qual deve modelar a estrutura para representar os dados de funcionários e dependentes, segundo uma relação de 1 para n, sendo que as classes Funcionario e Dependente já estão implementadas. Qual a definição de estrutura mais adequada, com uso do JCF?
a) HashMap‹Funcionario, ArrayList‹Dependente>>
b) ArrayList‹HashMap‹Funcionario, Dependente>>
c) HashMap‹Funcionario, Dependente>
d) HashMap‹ArrayList‹Funcionario›, ArrayList‹Dependente>>
e) HashMap‹ArrayList‹Funcionario›, Dependente>
Aula 04: Interfaces Visuais para Desktop
Apresentação
O conceito de GUI está relacionado à utilização de elementos padronizados de interatividade na forma gráfica, visando aumentar a usabilidade de um sistema e proporcionando grande ganho ergonômico ao usuário.  
Interfaces consistentes permitem que o usuário compreenda mais rapidamente as funcionalidades de novos aplicativos. Nós, como programadores, devemos ser capazes de lidar com estas interfaces gráficas, e a linguagem Java nos fornece algumas bibliotecas capazes de facilitar bastante nosso trabalho, como awt e swing. 
Saber utilizar os componentes das bibliotecas, e controlar a funcionalidade do sistema por meio de eventos, irá viabilizar a construção de aplicativos gráficos para desktop de forma consistente e produtiva.
Objetivos
· Identificar os conceitos relacionados às interfaces gráficas;
· Explicar o uso de componentes visuais e eventos em sistemas desktop;
· Usar os componentes swing para a criação de Interfaces Gráficas.
GUI (Graphical User Interface)
Os primeiros computadores pessoais trabalhavam no modo texto, com comandos simples de entrada e saída para interação com vídeo e teclado e até mesmo jogos eram feitos com caracteres ASCII.
Os pesquisadores da Xerox foram pioneiros na criação de interfaces gráficas, definindo conceitos que persistem até os dias atuais, como janelas, menus, caixas de opção, caixas de seleção e ícones.
As empresas Apple e Microsoft seguiram as ideias definidas pela Xerox na criação de suas interfaces gráficas, sendo o Macintosh lançado em 1984, o qual se tornou o primeiro produto de sucesso a utilizar uma interface gráfica.
No caso da Microsoft, as interfaces gráficas inicialmente eram chamadas a partir do MS-DOS, recebendo o nome de Windows, e foi somente a partir de 1990, com a versão 3.0, que este sistema de janelas se popularizou.
Outras plataformas também passaram a disponibilizar ambientes baseados em janelas, como XWindow no UNIX e Linux, Workbench no Commodore Amiga, e GEM nos computadores Atari.
Qualquer que seja a GUI, ela deve utilizar um conjunto de tecnologias visando à criação de uma plataforma de interação com o usuário.
Em computadores pessoais, o conjunto padrão é denominado WIMP (window, icon, menu, pointer), ou seja, é um ambiente composto de janelas, ícones, menus e ponteiros. Nesse sistema, utiliza-se um dispositivo de ponteiro como o mouse para controlar a posição do cursor e apresentar informação organizada em janelas e representada por meio de ícones.
Em ambientes WIMP, os gerenciadores de janelas facilitam a interação entre as representações gráficas, aplicações e o sistema de gerenciamento de hardware. A sensação proporcionada por estes gerenciadores de janelas, incluindo a interação entre janelas e outros elementos gráficos, produz um ambiente denominado desktop.
Bibliotecas gráficas do Java
O ambiente Java traz, por padrão, duas bibliotecas que objetivam a criação de sistemas baseados em janelas:
awt (Abstract Window Toolkit),
trata da API original do Java para GUI.
swing, parte da JFC
(Java Foundation Classes).
Existe também uma terceira biblioteca denominada swt (Standard Widget Toolkit), criada pela IBM e mantida pela Eclipse Foundation.
Nós utilizaremos a biblioteca swing, por ser padrão do ambiente Java e por definir o aspecto para os componentes visuais de forma independente, ao contrário do awt, que exibe os componentes de acordo com o aspecto proporcionado pela plataforma ou pelo sistema operacional.
 
Nas bibliotecas swing e awt temos componentes visuais como botões, campos de texto e listas, voltados para a interação com o usuário, e componentes chamados de contêineres, responsáveis por agrupar os componentes iniciais.
O principal contêiner é a janela, e dentro dela podemos ter botões e outros componentes visuais simples, bem como painéis, que podem agrupar outros componentes visuais e painéis de forma recursiva.
Para ajustar a forma como estes componentes serão dispostos sobre o contêiner, devemos utilizar os componentes de layout.
Vamos ilustrar este processo no exemplo seguinte.
 
 
import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.FlowLayout;
import java.awt.HeadlessException;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class MinhaJanela extends JFrame{
public MinhaJanela() throws HeadlessException {
super("Apenas um teste");
setLayout(new BorderLayout());
setBounds(10, 10, 300, 200);
JPanel jp = new JPanel(new FlowLayout());
jp.add(new Button("OK"));
jp.add(new Button("Cancela"));
add(jp,"South");
}
public static void main(String[] args) {
new MinhaJanela().setVisible(true);
}
}
	
Neste exemplo nós criamos uma classe descendente de JFrame, que na prática seria uma janela, e efetuamos algumas configurações.
Inicialmente chamamos o construtor do pai com a passagem do título da janela, definimos o layout como BorderLayout (dividido em Norte, Sul, Leste, Oeste e Centro), e definimos a posição, altura e largura com uso de setBounds.
Em seguida foi criado um painel com layout sequencial (FLowLayout), sendo adicionados dois botões a este painel, o qual é finalmente adicionado à janela na posição Sul.
O resultado você confere na imagem ao lado.
Podemos verificar alguns layouts disponíveis na tabela seguinte.
	Layout
	
	Descrição
	BorderLayout
	
	Aceita até cinco componentes, os quais devem ser posicionados em North, South, East, West e Center.
	BoxLayout
	
	Coloca os componentes em uma linha

Outros materiais