INTRODUÇÃO AO JAVA WEB
Realização: Lecom
Informações de contato
André Farina – Lecom
Rua Manoel Bento da Cruz, 11-29, Bauru - SP
Fixo: 14 4009.8900
Internet: www.lecom.com.br
Email: [email protected]
Rafael Fantini da Costa – Lecom / MNIX
Rua Manoel Bento da Cruz, 11-29, Bauru - SP
Fixo: 14 4009.8910 / Cel: 14 8104.9339
Internet: www.mnix.com.br
Email: [email protected]
Licença de uso
Este material está licenciado sob a Licença Creative Commons Atribuição-NãoComercial-SemDerivações 4.0 Internacional.
Para ver uma cópia desta licença, visite http://creativecommons.org/licenses/by-nc-nd/4.0/
CONTEÚDO
1. INTRODUÇÃO .......................................................................................................................... 1
1.1 O que é Java? ........................................................................................................................ 1
1.2 Características da Linguagem ................................................................................................ 1
1.3 Plataforma Java ..................................................................................................................... 1
1.4 Java Development Kit (JDK) ................................................................................................. 2
1.5 A Máquina Virtual Java ......................................................................................................... 2
1.6 Garbage Collection ................................................................................................................ 3
1.7 Convenções de Código .......................................................................................................... 3
2. A LINGUAGEM JAVA ............................................................................................................... 4
2.1 Princípios .............................................................................................................................. 4
2.2 Membros de uma Classe ........................................................................................................ 4
2.3 O Método “main” .................................................................................................................. 5
2.4 Exercícios.............................................................................................................................. 5
2.5 Variáveis e Operadores .......................................................................................................... 5
2.6 Palavras Reservadas .............................................................................................................. 6
2.7 Tipos de Dados ...................................................................................................................... 6
2.7.1 Inteiro ............................................................................................................................. 6
2.7.2 Ponto Flutuante ............................................................................................................... 7
2.7.3 Caractere ........................................................................................................................ 7
2.7.4 Lógico ............................................................................................................................ 7
2.7.5 Tipo de Referência .......................................................................................................... 7
2.8 Escopo de Variáveis ............................................................................................................... 8
2.9 Operadores ............................................................................................................................ 8
2.9.1 Precedência de Operadores ............................................................................................. 9
2.10 Constantes ....................................................................................................................... 9
2.11 Controle de Fluxo ................................................................................................................ 9
2.11.1 if – else ......................................................................................................................... 9
2.11.2 switch ......................................................................................................................... 10
2.11.3 while ........................................................................................................................... 10
2.11.4 for ............................................................................................................................... 10
2.11.5 continue – break .......................................................................................................... 11
2.12 Exercícios .......................................................................................................................... 11
3. MÉTODOS ............................................................................................................................... 12
3.1 Modificadores de Acesso ..................................................................................................... 12
3.2 Parâmetros .......................................................................................................................... 12
3.3 Retorno ............................................................................................................................... 12
4. PROGRAMAÇÃO ORIENTADA A OBJETOS ........................................................................ 13
4.1 O Antecessor ....................................................................................................................... 13
4.2 Conceitos ............................................................................................................................ 13
4.2.1 Abstração ...................................................................................................................... 13
4.2.2 Encapsulamento ............................................................................................................ 14
4.2.3 Classe e Objeto ............................................................................................................. 14
4.2.4 Herança ........................................................................................................................ 16
4.2.5 Polimorfismo ................................................................................................................ 17
4.2.6 Modularidade ............................................................................................................... 18
4.3 Benefícios ........................................................................................................................... 18
4.4 Construtores ........................................................................................................................ 19
4.5 this & super ......................................................................................................................... 19
4.6 Exercícios............................................................................................................................ 20
4.7 Membros Estáticos .............................................................................................................. 20
4.7.1 Ocorrências de membros estáticos ................................................................................ 20
4.8 Exercícios............................................................................................................................ 20
5. CLASSES ABSTRATAS E INTERFACES ............................................................................... 22
5.1 Classes Abstratas ................................................................................................................. 22
5.2 Interfaces ............................................................................................................................. 23
5.3 Exercício ............................................................................................................................. 23
6. EXCEÇÕES .............................................................................................................................. 24
6.1 Definição de Exception ....................................................................................................... 24
6.2 Classes de Exceções ............................................................................................................ 24
6.3 Tratamento de Exceções ...................................................................................................... 24
6.3.1 Capturando Exceções .................................................................................................... 25
6.3.2 Deixando uma Exceção Passar ...................................................................................... 26
6.3.3 Lançando uma Exceção ................................................................................................ 26
6.4 Exercícios............................................................................................................................ 27
7. SERVLETS ............................................................................................................................... 27
7.1 O Protocolo HTTP............................................................................................................... 27
7.2 Common Gateway Interface ................................................................................................ 29
7.3 O que são Servlets? ............................................................................................................. 29
7.4 Funcionamento da Servlet ................................................................................................... 30
7.5 Respostas ............................................................................................................................ 31
7.5.1 Exercícios ......................................................................................................................... 32
7.5.2 Status HTTP ................................................................................................................. 32
7.5.3 Exercícios ..................................................................................................................... 33
7.6 Requisições ......................................................................................................................... 33
7.6.1 Parâmetros da requisição .............................................................................................. 33
7.6.2 Exercícios ..................................................................................................................... 33
7.6.3 Cabeçalhos de Requisição ............................................................................................. 33
8. COOKIES ................................................................................................................................. 35
8.1 Definição ............................................................................................................................. 35
8.2 Estrutura .............................................................................................................................. 35
8.3 Adicionando Cookies........................................................................................................... 35
8.4 Recuperando Cookies .......................................................................................................... 36
8.5 Exercícios............................................................................................................................ 36
9. SESSÕES.................................................................................................................................. 37
9.1 Funcionamento .................................................................................................................... 37
9.2 Exercícios............................................................................................................................ 38
9.3 Validade da Sessão .............................................................................................................. 38
9.4 Exercícios............................................................................................................................ 38
10. CICLO DE VIDA DA SERVLET ............................................................................................ 39
10.1 Inicialização ...................................................................................................................... 39
10.2 Atendendo as Requisições ................................................................................................. 39
10.3 Finalização ........................................................................................................................ 40
10.4 Servlet Context .................................................................................................................. 40
10.5 Exercícios .......................................................................................................................... 41
11. VISÃO GERAL SOBRE JSP .................................................................................................. 42
11.1 O que é uma página JSP? ................................................................................................... 42
11.2 Diretivas ............................................................................................................................ 42
11.2.1 Diretiva page .............................................................................................................. 43
11.2.2 Diretiva include .......................................................................................................... 43
11.3 Expressões ......................................................................................................................... 43
11.4 Exercícios .......................................................................................................................... 43
11.5 Scriptlets ........................................................................................................................... 44
11.6 Objetos Implícitos ............................................................................................................. 44
11.7 Declarações ....................................................................................................................... 44
1
1. INTRODUÇÃO
1.1 O que é Java?
Java é uma linguagem de programação orientada a objetos mantida pela Oracle e originalmente
desenvolvida pela Sun Microsystems. Baseada no C++, o Java foi projetado para ser uma
linguagem portável para todas as plataformas.
A portabilidade é alcançada através do uso de um interpretador que traduz os bytecodes (código
resultante da compilação de um programa Java) em instruções de máquina. Isso dispensa a tarefa de
portar o código para diversas plataformas, uma vez que basta instalar a máquina virtual na
plataforma alvo e o programa Java funcionará como na plataforma original.
1.2 Características da Linguagem
Orientada a objetos: Paradigma de programação mais utilizado na atualidade. Entre os
principais benefícios estão o reuso de código e maior facilidade na manutenção dos sistemas
desenvolvidos;
Simples e robusta: Inspirada no C++, o Java apresenta várias melhorias em relação a
linguagem da qual foi originada. Possui várias características que previnem o programador
de cometer erros comuns no C++, além de estruturas que facilitam a programação, tornando-
a mais produtiva;
Gerenciamento automático de memória: O Java abstrai o conceito de referências, livrando
o programador da (árdua) tarefa de lidar com ponteiros. Além disso, tanto alocação quanto a
liberação de memória são geridas pela máquina virtual Java, liberando o programador da
preocupação de gerenciar a memória em baixo nível. A este mecanismo de liberação de
memória damos o nome de Garbage Collection;
Independência de plataforma: Uma das características mais marcantes do Java é a sua
capacidade de executar programas em plataformas diferentes daquela para a qual o
programa foi inicialmente desenvolvido, como evidenciado pelo mote “write once, run
everywhere”. Isso é possível graças à utilização da máquina virtual que interpreta bytecodes;
Multi-threading: A execução de tarefas paralelas é facilitada pelo uso da biblioteca de
threading e pelos recursos de sincronização.
1.3 Plataforma Java
A tecnologia está separada em três edições com propósitos distintos:
Java Standard Edition (Java SE): ferramentas e frameworks básicos para qualquer
aplicação Java (inclusive para as outras duas plataformas). Suficientemente completo para
desenvolvimento de aplicações com interface gráfica, por exemplo;
Java Enterprise Edition (Java EE): ferramentas e frameworks para desenvolvimento de
aplicações distribuídas e sites voltados para o uso corporativo. Engloba tecnologias como
EJB, JMS, RMI, CORBA, etc;
Java Micro Edition (Java ME): ferramentas e frameworks para desenvolvimento de
aplicações executadas em dispositivos móveis e integrados: celulares, set-top boxes,
impressoras, eletrodomésticos, etc.
2
1.4 Java Development Kit (JDK)
O JDK é o kit de desenvolvimento disponibilizado pela Oracle que permite ao desenvolvedor criar
aplicativos para a plataforma Java. A última versão, 1.7, do JDK, pode ser encontrada em
http://www.oracle.com/technetwork/pt/java/javase/downloads
O JDK compreende:
Java Runtime Environment (JRE) utilizado para executar as aplicações
Ferramenta de desenvolvimento: compilador, empacotador JAR, debugger, utilitários (como
o Java2WSDL), etc
Biblioteca de código (framework) com vários recursos prontos para uso: criação de
aplicativos com recursos visuais, manipulação de estruturas de dados, etc
1.5 A Máquina Virtual Java
O JRE provê os requisitos mínimos para a execução Java: a Java Virtual Machine (JVM),
implementação das bibliotecas e arquivos de suporte. Além de ser o coração da JVM, o JRE é a
pedra angular da filosofia “write once, run everywhere”.
Explicando a imagem anterior:
Na compilação, ao invés do código-fonte ser convertido em código-objeto, as instruções são
geradas em bytecode. O bytecode é genérico e independe de arquitetura ou sistema
operacional;
3
Quando um programa Java é executado, o arquivo bytecode é interpretado pela JVM. Cada
sistema operacional / arquitetura possui sua implementação própria de JVM e a mesma
deverá ser instalada na máquina onde o programa será executado. Os browser, por exemplo,
possui uma implementação própria da JVM, utilizada para execução de Applets (como a
applet de segurança do banco).
1.6 Garbage Collection
Comparativamente, tanto no C quanto no C++, o programa desenvolvido é responsável pela
alocação e liberação de memória, sendo responsável por um grande número de erros: memory leak,
crashes, etc. No Java, ao criar um objeto, a plataforma se encarrega de alocar a quantia certa de
memória e manter o registro de utilização do mesmo. Assim, quando não estiver mais sendo usado
(referenciado), o objeto é “coletado” e a memória ocupada por ele é devolvida ao sistema.
Por ser uma operação custosa, o Garbage Collection é acionado automaticamente apenas quando a
aplicação sendo executada excede um limiar de memória em uso. Essa liberação ocorre numa
thread em background para evitar o travamento do programa.
Embora seja possível invocar o Garbage Collector manualmente utilizando a chamada System.gc(),
destruir objetos diretamente não é permitido. Para forçar a liberação, é preciso eliminar todas as
referências a esse objeto e, em seguida, invocar o System.gc().
1.7 Convenções de Código
Convenções de código são importantes para desenvolvedores por um número de razões:
80% da vida útil de um software é gasto em manutenção;
Dificilmente um software é mantido por toda a sua vida pelo autor original;
Convenções de código aumentam a legibilidade do código, permitindo que os
desenvolvedores entendam o código mais rapidamente e corretamente.
Por ser extenso e fora do escopo deste curso, deixo aqui o link para a página que contém uma
definição extensa e completa das convenções de código utilizadas pelos desenvolvedores Java ao
redor do mundo: http://www.oracle.com/technetwork/java/javase/documentation/codeconvtoc-
136057.html
4
2. A LINGUAGEM JAVA
Como o Java é uma linguagem orientada a objetos, o desenvolvimento de programas é feito por
meio de classes. Uma classe java segue a seguinte sintaxe:
<modificador de acesso> class <nome da classe>
{
<Declaração das Variáveis de Instância (Atributos)>
<Declaração de Métodos>
public static void main( String args[] )
{
// corpo principal do programa
}
}
Por exemplo:
public class Exemplo1 { String mensagem = "Minha mensagem"; public void imprimeTexto(String texto) { // Mostra 'texto' no console System.out.println(texto); } public static void main( String args[] ) { imprimeTexto(mensagem); } }
2.1 Princípios
Assim como toda linguagem, o Java possui suas particularidades:
Identificadores (nomes de variável, classe, método) devem começar sempre por letras ou
underscore ( _ )
Comandos são finalizados com um ponto e vírgula ( ; )
Java é case-sensitive, ou seja, diferencia maiúsculas de minúsculas (diferente de Delphi)
Blocos de código são delimitados por um par de chaves { }
Comentários são precedidos por // ou contidos entre /* */
o // Isto é um comentário
o /* Isto também é */
2.2 Membros de uma Classe
As variáveis de um objeto (instância) são chamadas de campos e, juntamente com os métodos,
compõe os elementos básicos para a construção de uma classe. Como tal, são denominados de
membros da classe.
Os campos são blocos de memória reservados para o armazenamento de informações durante a vida
5
útil de um objeto e, portanto, constituem o estado interno do mesmo. Campos são inicializados na
construção do objeto e ficam disponíveis para utilização por todos os seus métodos. Os métodos,
por sua vez, definem as operações (ações) que podem ser realizadas pela classe.
2.3 O Método “main”
Para executar, toda aplicação Java precisa de um ponto de entrada e este ponto é o método main().
A declaração deve conter a seguinte assinatura para que a aplicação consiga ser iniciada com
sucesso: public static void main( String[] args ) { … }
Quando um programa é executado, o interpretador chamará primeiramente o método main da classe.
É ele quem controla o fluxo de execução do programa e executa qualquer outro método necessário
para a funcionalidade da aplicação.
Nem toda classe terá um método main (normalmente apenas uma classe tem um “main”). Uma
classe que não possui um método main() não pode ser “executada” pois não representa um
programa em Java. Ela será sim, utilizada como classe utilitária para a construção de outras classes
ou mesmo de um programa.
2.4 Exercícios
Utilizando a IDE Eclipse:
1. Crie um Java Project que imprima “Hello World”
2. Crie um programa com uma classe que contenha as quatro operações algébricas básicas
(soma, subtração,...) e outra classe que faça uso das mesmas
2.5 Variáveis e Operadores
Variáveis são denominações dadas a porções de memória que armazenam algum dado. Toda
variável válida tem as seguintes características:
É declarada antes de ser inicializada;
Possui um tipo, um identificador e um escopo;
Podem ser locais, quando declaradas em métodos ou são campos, quando declaradas no
corpo da classe;
Podem ser inicializadas na declaração.
Como Java é uma linguagem fortemente tipada, todas as variáveis devem ser declaradas antes de
serem usadas. O tipo de uma variável determina o tipo de informação que pode ser armazenada nela.
Variáveis em Java podem ser declaradas como campos, no corpo da classe, ou podem ser declaradas
localmente em qualquer parte da implementação de um método. Variáveis também possuem um
escopo, o qual determina onde no programa ela estará visível e poderá ser acessada.
Declaração de variáveis é feita como segue:
tipo identificador [= valor] [, identificador [= valor]];
Exemplos:
String texto = “Exemplos de declaração de variáveis”;
int umNumero, UmNumero;
char letra = ‘i’;
6
2.6 Palavras Reservadas
Alguns identificadores reservados para uso da própria linguagem e recebem o nome de palavras
reservadas. São elas:
abstract continue for new switch
assert default goto package synchronized
boolean do if private this
break double implements protected throw
byte else import public throws
case enum instanceof return transient
catch extends int short try
char final interface static void
class finally long strictfp volatile
const float native super while
2.7 Tipos de Dados
No Java, uma variável pode pertencer a quatro tipos distintos: classes, interfaces, arrays (vetores) e
tipos primitivos. Variáveis com classes, interfaces e arrays contém apenas a referência de memória
(ponteiros) par ao objeto real, enquanto tipos primitivos são armazenados em sua totalidade e
possuem tamanho fixo.
O tamanho e as características de cada tipo primitivo são consistentes entre as plataformas, não
havendo variações entre plataformas diferentes, como ocorre no C e C++. Os tipos primitivos são:
Categoria Tipo Tamanho Valor padrão
Inteiro byte 8 bits 0
Inteiro short 16 bits 0
Inteiro* int 32 bits 0
Inteiro long 64 bits 0L
Ponto flutuante float 32 bits 0.0f
Ponto flutuante* double 64 bits 0.0
Caractere char 16 bits '\u0000'
Lógico boolean 8 bits False
Os tipos marcados com um asterisco ( * ) são o padrão para literais definidos no código.
2.7.1 Inteiro
Tipos inteiros armazenam valores numéricos sem casas decimais. Por padrão, literais inteiros são
do tipo int e podem ser especificados como hexadecimais utilizando o prefixo 0x . Para literais do
tipo long, deve-se utilizar o sufixo L . Ex:
int hexa = 0xfe; // OK long outro = 1L; // OK long combo = 0xfeL; // OK, equivale a 254 int errado = 1L; // Não compila
7
Tipo Variação
byte -128 .. +127
short -32768 .. +32767
int -2147483648 .. +2147483647
long -9 quintilhões .. +9 quintilhões
2.7.2 Ponto Flutuante
Variáveis de ponto flutuante armazenam números com casas decimais (conjunto dos números reais).
Deve ser utilizado o ponto ( . ) como separador de casas decimais e, quando necessário, os números
podem ser escritos em notação exponencial utilizando a caractere E . Para literais do tipo float,
deve-se utilizar o sufixo f . Ex:
double expo = 1.1e4; // OK, equivale a 1.1 * 10^4 = 11000.0 float outro = 1.2f; // OK float errado = 1.2; // Não compila
2.7.3 Caractere
Uma variável char armazena apenas 1 caractere em seu interior – o armazenamento de palavras é
reservado a classe String, que não é um tipo primitivo. Internamente, o Java utiliza o padrão
UNICODE para codificação das variáveis do tipo char.
O literal char é identificado pelo uso de aspas simples ( ' ) ao redor da letra. Alguns exemplos:
char letra = 'a'; char linha = '\n'; // Nova linha char simples = '\''; // Aspas simples
char dupla = '\"'; // Aspas dupla char unicode = '\u007F'; // Valor UNICODE char tabula = '\t'; // Tabulação
2.7.4 Lógico
Uma variável boolean pode armazenar apenas os valores true e false . Não é possível utilizar
valores numéricos para testes lógicos, como ocorre no C / C++.
2.7.5 Tipo de Referência
Tipos se separam em primitivos ou referência. Como vimos, os primitivos armazenam valores
numéricos, caracteres e booleanos. Tipos de referência são classes, interfaces e arrays, e
diferentemente dos tipos primitivos, podem ser definidos pelo desenvolvedor.
Uma grande diferença entre os tipos primitivos e de referência é a alocação: tipos primitivos são
alocados na Stack por possuir tamanho fixo e tipos de referência são alocados na Heap, por não
possuírem tamanho definido. Em função disso, tipos primitivos são sempre passados por valor,
enquanto tipos de referência são passados por referência.
Um exemplo prático da diferença: ao alterar um parâmetro inteiro recebido por uma função, você
estará alterando uma cópia da variável que foi passada como parâmetro. Caso fosse um array, a
mudança seria refletida na variável original.
8
2.8 Escopo de Variáveis
O escopo de uma variável é delimitado pelo bloco { } no qual ela está inserida e isso, por sua vez,
determina o ciclo de vida da variável e quem terá acesso sobre ela.
De forma geral, quando uma variável é declarada no corpo de uma classe, chamamos de campo e
quando é declarada no corpo de um método, chamados de local. Uma variável local só pode ser
acessada pelo método que a definiu, enquanto um campo tem o nível de acesso definido pelo seu
modificador de acesso (public, private, protected).
2.9 Operadores
Operadores são funções especiais e intrínsecos da linguagem. No Java, temos:
Operador Função Exemplo Resultado
= Atribuição int var = 2, var1 = -1;
var = var1 + 12;
var1 = var2 = 50;
+ Adição 2 + 2 4
- Subtração 1 - 2 -1
* Multiplicação 1.1 * salario
/ Divisão 100 / 40
100 / 40.0
2
2.5
% Resto da divisão 100 % 40 20
== Igual x == 10
!= Diferente 1 != 1 false
< Menor 1 < 2 true
> Maior 1 > 2 false
>= Maior ou igual 2 <= 2 true
<= Menor ou igual 2 >= 3 false
instanceof Pertence à classe // Mamifero m;
m instanceof Animal
true
&& AND (0 < 1) && (12 > 6) true
|| OR (0 > 1) || (12 < 6) false
! NOT !(2 == 3) true
+= Soma e atribui x += 20 // x = 10 x é 30
-= Subtrai e atribui x -= 20 // x = 10 x é -10
*= Multiplica e atribui x *= 2 // x = 10 x é 20
%= Resto e atribui x %= 3 // x = 10 x é 1
/= Divide e atribui x /= 3 // x = 10 x é 3
++ Incremento x++ // x = 10 x é 11
-- Decremento x-- // x = 10 x é 9
9
2.9.1 Precedência de Operadores
Entende-se por precedência a ordem na qual os operadores são executados em uma expressão.
Operadores com mesma precedência são executados da esquerda para a direita, de acordo com a
associatividade. Por exemplo:
int n = 21 / 3 * 5; // n = 35 int n = 21 / (3 * 5); // n = 1
A tabela abaixo mostra as precedências entre diferentes operadores.
Precedência Operadores
1 ++ -- !
2 * / %
3 + -
4 < > <= >= instanceof
5 == !=
6 && ||
7 = += -= /= %=
2.10 Constantes
Uma constante pode tanto ser definida como um atributo de classe como uma variável local. Uma
constante uma vez que foi declarada e atribuído um valor para a mesma, não é permitido que outra
atribuição seja efetuada, ocasionando um erro de compilação caso isso ocorra. A atribuição do valor
inicial pode ser feita no momento da declaração, ou posteriormente em outra parte do programa.
Para declarar uma variável do tipo constante, devemos utilizar o modificador final dessa forma:
final <tipo> <identificador> [= valor];
2.11 Controle de Fluxo
2.11.1 if – else
Sintaxe Exemplo
if (expr_boolean) { // Corpo 1 } else { // Corpo 2 }
if (idade < 18) { System.out.println("Entrada bloqueada"); } else { System.out.println("Entrada permitida"); }
Se a expressão booleana for verdadeira, o Bloco 1 é executado, caso contrário, o Bloco 2 é
executado.
10
2.11.2 switch
O switch é normalmente utilizado para determinar o fluxo a partir de uma expressão com vários
resultados possíveis. A limitação é que a expressão avaliada pode ser apenas dos tipos char, byte,
short, int, String e enumeradores.
Sintaxe Exemplo
switch (expr) { case const1: // Corpo 1 break; case const2: // Corpo 2 break; ... [default: // Corpo n] }
switch (nome) { case "Fanta": System.out.println("Pessoa"); break; case "Lecom": System.out.println("Empresa"); break; default: System.out.println("Desconhecido"); }
É importante notar que:
O case deve ser seguido por um literal ou uma constante.
Todo bloco deve ser finalizado por um break , exceto o bloco default.
2.11.3 while
Executa um bloco de comandos sucessivas vezes até que a condição seja falsa. A
expressão de comparação é avaliada antes que o laço seja executado.
Sintaxe Exemplo
while (expr_boolean) { // Corpo }
while (i < n) { pot *= x; i++; }
2.11.4 for
Em sua forma mais utilizada, o loop possui um trecho inicial com inicialização das variáveis,
seguida por uma expressão de comparação e depois a parte final com o incremento ou decremento
das variáveis do laço.
Sintaxe Exemplo
for (init; expr_boolean; pos) { // Corpo }
for (int i = 0; i < array.length; i++) { double elemento = array[i]; }
Logo ao iniciar, o for executa o trecho <init> e, em seguida, testa a condição <expr_boolean>. Caso
seja verdadeira, executa os comandos no bloco e, ao final, executa o comando em <pos> seguido
pelo teste de condição em <expr_boolean>, <bloco>, <pos>, <expr_boolean>,… até que
<expr_boolean> seja falsa. O funcionamento se aproxima ao trecho abaixo:
init; while (expr_boolean) { // Corpo pos; }
11
2.11.5 continue – break
O break, quando executado, faz com que o laço mais interno seja interrompido e força a saída do
mesmo, como se a condição de repetição estivesse falsa. Os usos mais comuns são no switch –
usado para sinalizar o fim de um case – e para abortar um laço diante de algum gatilho interno (ex:
encontrou o elemento desejado num array).
O continue funciona de maneira análoga, porém oposta. Ele força que o laço seja interrompido,
mas força uma nova repetição do laço e, consequentemente, uma nova avaliação da expressão
lógica.
2.12 Exercícios
Faça:
Um programa que calcula o fatorial de um número real
Um programa que ordena os valores de um vetor de entrada
Um programa que calcula o perímetro do círculo de raio igual ao parâmetro de entrada
realizada
Um programa que imprime os números primos contidos entre os valores passados por
parâmetro
12
3. MÉTODOS
Num programa orientado a objetos, o estado interno de um programa é determinado pelos campos e
o comportamento é definido pelos métodos. Em Java, um método equivale a uma função / subrotina
/ procedimento encontrado em outras linguagens de programação. É importante notar que não
existem métodos globais – todo método deve, obrigatoriamente, estar contido em uma classe.
Sintaxe: [modificadores] tipo_retorno identificador([argumentos]) { // Corpo } Exemplo: public static final int soma(int a, int b) { return a + b; }
3.1 Modificadores de Acesso
O acesso a campos e métodos é controlado por meio do uso dos modificadores de acesso. A tabela
abaixo relaciona os modificadores de acesso com a acessibilidade do membro em diferentes pontos
do código.
public protected <nenhum> private
Mundo Sim Não Não Não
Subclasse Sim Sim Não Não
Mesmo pacote Sim Sim Sim Não
Própria classe Sim Sim Sim Sim
Nota: Os modificadores de acesso não podem ser aplicados a variáveis locais.
3.2 Parâmetros
Métodos podem ter zero ou mais parâmetros sendo que, mesmo na ausência de parâmetros, é
obrigatório informar os parênteses vazios tanto na definição quanto na chamada do método. Ao
nome do método acrescido dos parâmetros dá-se o nome de assinatura do método.
Na definição de um método, a declaração de parâmetros assemelha-se à declaração de variáveis
locais, utilizando uma vírgula separadora entre os parâmetros, como no método soma acima.
3.3 Retorno
Métodos podem (ou não) retornar valores. Para sinalizar que existe um retorno, precisamos definir
de antemão o tipo do valor retornado e, no corpo da função, utilizar a palava reservada return
seguida pela expressão a ser retornada (veja o exemplo do método soma acima). Caso não haja um
retorno, é necessário definir o tipo de retorno como sendo void na assinatura do método.
Quando chamado, o return faz com que o método encerre sua execução e volte para a posição
anterior na pilha de instruções. Mesmo funções declaradas com retorno void podem lançar mão do
return; como uma ferramenta de controle de fluxo. No exemplo abaixo, curto-circuitamos a
execução do método quando o saldo for insuficiente.
if (saldo < 0) { return; }
13
4. PROGRAMAÇÃO ORIENTADA A OBJETOS
4.1 O Antecessor
Anteriormente à programação orientada a objetos, o paradigma dominante era a programação
estruturada. Neste paradigma, a abordagem é decompor um programa em subprogramas, realizando
o particionamento até que a unidade de subprograma alcançada tenha coesão e inteligibilidade.
Neste paradigma, o programador é forçado a fixar atenção nos procedimentos (verbos do problema)
ao invés dos dados (substantivos do problema).
Essa disparidade tem como resultado um aumento na complexidade do software, uma vez a
modelagem de um problema pensando em suas ações não é uma tarefa trivial – um paradigma que
busca representar um problema baseando-se nos substantivos seria mais recomendado. A partir
dessa ideia foi desenvolvido o paradigma de Programação orientada a objetos.
4.2 Conceitos
Os conceitos básicos que permeiam a programação orientada a objetos são:
Abstração;
Encapsulamento;
Classe e Objeto;
Herança;
Polimorfismo;
Modularidade;
Mensagens e métodos.
4.2.1 Abstração
Abstração consiste em ignorar aspectos não-relevantes de um domínio, concentrando-se apenas nos
assuntos principais do problema. Assim, abstrair consiste basicamente no processo de retirar do
domínio do problema os detalhes relevantes e representá-los não mais na linguagem de domínio, e
sim na linguagem de solução, como Java, C++, etc.
14
4.2.2 Encapsulamento
A propriedade de implementar dados e
procedimentos correlacionados em uma
mesma entidade recebe o nome de
Encapsulamento. A ideia por trás do
encapsulamento é a de que um sistema
orientado a objetos não deve depender
da implementação interna, e sim da
interface de seus componentes.
Vejamos o exemplo de um circuito
integrado (CI). Pode-se notar o
encapsulamento de vários componentes
como transistores, resistores,
capacitores e outros interligados por um
circuito. O usuário que utiliza um CI
não precisa se preocupar com os
mecanismos internos de um CI – apenas com o comportamento da interface (os pinos).
4.2.3 Classe e Objeto
Um Objeto é um elemento que formaliza o modo pelo qual compreendemos algo no domínio do
problema, refletindo a capacidade do sistema de guardar informações sobre tal elemento, interagir
com ele, ou ambas as coisas.
Uma Classe descreve (define) um conjunto de objetos semelhantes que compartilham a mesma
semântica. Um relacionamento análogo ao de Classe-Objeto seria a relação entre uma Receita de
Bolo e um Bolo criado com a receita: a receita pode ser encarada como uma fórmula para criação
bolos, mas ela, em si, não é um bolo.
Considere um programa para um banco, uma entidade extremamente importante para o nosso
sistema é a conta – é uma boa prática generalizarmos as informações da conta relevantes ao
domínio do problema, juntamente com as funcionalidades (ações) que toda conta deve possuir.
15
Entre os dados de uma conta que são importantes, podemos listar: número da conta, nome do cliente,
saldo, limite, etc. Entre as ações que gostaríamos de atribuir a uma conta, temos: sacar uma
quantidade x, depositar uma quantidade y, imprimir o nome do dono da conta, retornar o saldo
atual, transferir uma quantidade x para uma outra conta y, etc.
Começando pelos conteúdos de uma conta, podemos traduzir as informações acima para uma classe
que ficaria como no trecho abaixo:
class Conta { int numero; String nome; double saldo; double limite; }
Agora temos a definição da estrutura de uma conta. Não é possível, no entanto, obter o saldo da
conta, pois o que temos é apenas a definição de uma conta – não é uma conta propriamente dita.
Para essa tarefa, precisamos construir uma conta, sua definição, assim conseguiremos manipular a
conta como gostaríamos. A esta definição damos o nome de classe e aos produtos da construção de
uma classe damos o nome de objeto.
Para podermos efetivamente usar um objeto criado a partir da classe Conta, além de construir o
objeto, precisamos também de um ponto de início no programa a partir do qual o sistema
operacional passa o controle a nossa aplicação. Vamos criar um arquivo Programa.java para isso:
class Programa { public static void main(String[] args) { Conta minhaConta; minhaConta = new Conta(); } }
Para criar (construir, instanciar) uma Conta, basta usar a palavra reservada new seguida pelo nome
da classe (neste caso, Conta) e parênteses.
Através da variável minhaConta agora podemos acessar o objeto recém-criado para obter/alterar
seu nome, saldo, etc
class Programa { public static void main(String[] args) { Conta minhaConta; minhaConta = new Conta(); minhaConta.nome = "Fanta"; minhaConta.saldo = 1.99; } }
É importante fixar que o ponto em “minhaConta.saldo” foi utilizado para acessar o campo saldo no
objeto minhaConta. Agora, minhaConta pertence ao Fanta, e tem saldo de R$ 1,99.
Dentro da classe, também iremos declarar o que cada conta faz, e como é feito – os
comportamentos que cada classe tem, isto é, o que ela faz. Por exemplo, de que maneira que uma
Conta saca dinheiro?
Especificaremos isso dentro da própria classe Conta, e não em um local desatrelado das
informações da própria Conta. Essas “funções” declaradas no interior de uma classe são chamadas
de método e recebem este nome, pois, determina a maneira de fazer uma operação com um objeto.
16
Como queremos a possibilidade de sacar dinheiro da conta, iremos criar um método que saca uma
quantidade determinada e não retorna informações.
void saca(double quantidade) { double novoSaldo = this.saldo - quantidade; this.saldo = novoSaldo; }
A palavra reservada void indica que o método não retorna nenhum dado àquele que o invocou.
Métodos exigem a colocação de parênteses tanto nas declarações quanto nas chamadas para
delimitar os parâmetros fornecidos aos mesmos.
Neste exemplo, podemos ver que o método “saca” possui um parâmetro (ou argumento) –
quantidade – que indica a quantia a ser sacada. Essa variável é uma variável comum, chamada
também de temporária ou local, pois ao final da execução desse método, ela deixa de existir (escopo
local). Dentro do método, também estamos declarando uma nova variável que assim como o
argumento, vai morrer no fim do método, pois este é seu escopo. Para fins de escopo, o parâmetro é
tratado como uma variável declarada no corpo do método.
O próximo passo é mandar uma mensagem ao objeto “minhaConta” e pedir que ele execute o
método “saca()”. Para denominar essa execução, utilizamos o termo “invocação de método”.
public static void main(String[] args) { Conta minhaConta; minhaConta = new Conta(); minhaConta.nome = "Fanta"; minhaConta.saldo = 1.99; minhaConta.saca(1.00); }
Objetos interagem e se comunicam através de mensagens. As mensagens identificam os métodos a
serem executados e, quando enviadas a um objeto, sinalizam ao objeto que uma invocação de
método deve ser realizada. Para enviar uma mensagem, precisamos de:
Identificar o objeto que receberá a mensagem
Identificar o método que o objeto deverá executar
Passar os argumentos requeridos pelo método
4.2.4 Herança
A herança possibilita a hierarquização de classes, onde uma classe mais especializada (classe filha
ou subclasse) pode herdar as propriedades (métodos e atributos) e semântica de uma classe mais
geral (classe pai ou superclasse).
No exemplo a seguir dizemos que a classe Gerente herda todos os atributos e métodos da classe
mãe Funcionario. Sendo mais preciso, ela também herda os atributos e métodos privados, porém
não consegue acessá-los diretamente.
class Funcionario { String nome; String cpf; double salario; }
17
class Gerente extends Funcionario { int senha; public boolean autentica(int senha) { if (this.senha == senha) { System.out.println("Acesso Permitido!"); return true; } else { System.out.println("Acesso Negado!"); return false; } } }
4.2.5 Polimorfismo
O termos polimorfismo é originário do grego e quer dizer “possuidor de várias formas”. Na
programação, está atrelado à capacidade de um método assumir várias implementações diferentes
sob um mesmo nome e à capacidade do programa em discernir, dentre métodos homônimos, aquele
que deve ser executado. Uma das maiores vantagens do uso de polimorfismo está na criação de
programas mais claros e flexíveis, pois, elimina a necessidade de replicação de métodos ao mesmo
tempo em que permite a extensão dos já existentes, como podemos observar no exemplo:
class EmpregadoDaFaculdade { private String nome; private double salario; double getGastos() { return this.salario; } } class ProfessorDaFaculdade extends EmpregadoDaFaculdade { private int horasDeAula; double getGastos() { return super.getGastos() + this.horasDeAula * 10; } }
Polimorfismo também está relacionado à capacidade de uma variável em receber objetos de classes
distintas. Veja:
public class Aluno extends Pessoa { .... } Pessoa p; p = new Pessoa(); // OK p = new Aluno(); // OK p = new Object(); // ERRO
18
4.2.6 Modularidade
Em programas grandes, ocasionalmente surge o problema: quero escrever uma classe com
determinado nome, mas o mesmo já está em uso. Por exemplo: Você está integrando o código de
um outro sistema ao seu e uma colisão de nomes ocorre em classes com funcionamentos diferentes.
Para resolver este problema, recorremos a estrutura de diretórios para organizar as classes. Por sua
vez, no Java, estes diretórios são mapeados para pacotes, estruturas que agrupam classes com
objetivos comuns. Veja o pacote java.util – ele contém classes utilitárias.
A utilização de pacotes não só permite uma organização maior do código como também facilita o
compartilhamento do mesmo, já que existem modificadores de acesso para o nível de pacote,
permitindo que determinado método / campo / classe seja visível apenas para classes pertencentes
ao mesmo pacote.
4.3 Benefícios
Observe a classe abaixo de uma simples aplicação de Zoológico:
public class Zoo { private Animal[] lista = new Animal[8]; public Animal[] getLista(){ return this.lista; } public void adicionarAnimal( Animal p){ for(int i=0; i<lista.length; i++){ if (lista[i] == null){ lista[i] = p; break; } } }
Utilizamos a classe Animal para lançar mão do polimorfismo (Répteis, Mamíferos, etc). Não fosse
por ela, teríamos um grande prejuízo ao criar estruturas distintas para receber cada um dos tipos de
Animal: uma para Reptil, uma para Mamifero, etc. Como a estrutura dessas classes tem muito em
comum, podemos generalizar a definição em uma superclasse a fim de evitar a replicação de
código e facilitar a manutenção.
19
4.4 Construtores
Construtores são métodos especiais, utilizados para personalizar a construção de objetos e possuem
as seguintes características:
É o primeiro método executado pelo objeto
Deve possuir o mesmo nome da classe
Não tem um tipo de retorno (nem mesmo void)
É invocado através do uso do operador new (instanciação)
Tem modificador de acesso, normalmente public
Toda classe deve possuir ao menos um construtor. Se omitido, o compilador gerará um construtor
público, com corpo vazio e sem parâmetros, como no exemplo abaixo:
public Reptil() { }
Por serem os primeiros métodos a serem executados, construtores são utilizados para iniciar o
estado interno de um objeto, configurando-o de acordo com os parâmetros fornecidos:
public Reptil(String especie) { this.especie = especie; } public Reptil() { this("Desconhecido"); }
Nota: Caso alguma exception lançada no corpo do construtor não seja tratada, a criação do objeto
falhará.
O exemplo acima demonstra que, assim como ocorre com os métodos, podemos ter sobrecargas de
um construtor. Inclusive, podemos fazê-los chamarem uns aos outros utilizando o this(). Aplicamos
este recurso na definição de sobrecargas de um construtor que vão de um nível mais complexo
(vários parâmetros) para um nível mais simples.
4.5 this & super
Todos os métodos de uma instância recebem os argumentos implícitos this e base que nada mais
são do que referências para o próprio objeto e para o objeto pai, respectivamente. Embora possa
parecer pouco útil a primeira vista, essas referências são indispensáveis em muitas das situações
encontradas pelo desenvolvedor Java:
Desambiguação: O construtor da classe Reptil recebe um parâmetro que possui o mesmo
nome de um campo. Neste caso, utilizamos o this.especie para dizer ao compilador que
queremos a variável membro e não a local. O mesmo vale para uma classe que define um
campo com nome idêntico a um campo presente na superclasse e que deseja utilizar a
variável da superclasse ao invés da própria;
Construtor: Caso seja necessário que uma sobrecarga do construtor chame outra, utilizamos
o this() para sinalizar a intenção. Usando a base(), conseguimos chamar um construtor da
superclasse – útil para situações onde a assinatura do construtor de sua classe difere da
superclasse;
20
Polimorfismo: Muitas vezes, ao sobrescrever um método da superclasse, queremos apenas
ampliar o comportamento original em vez de substituí-lo completamente. Em casos como esse, é
comum invocar a versão da superclasse utilizando base.metodo().
4.6 Exercícios
Crie uma classe Poligono com os métodos calculaPerimetro() e calculaArea() e crie as
classes Triangulo e Quadrado sobrescrevendo ambos os métodos.
4.7 Membros Estáticos
Membros estáticos são aqueles que não pertencem a uma instância específica, mas sim à própria
classe. Como tal, membros estáticos são compartilhados entre todas as instâncias da classe e não
precisam de instanciação para serem usados. Vejamos o exemplo:
class Usuario { public static int tentativas; ... public void login() { tentativas++; // Restante } }
Usuario.tentativas = 0; Usuario c1 = new Usuario(); Usuario c2 = new Usuario(); c1.login(); // tentativas = 1 c2.login(); // tentativas = 2 c1.tentativas = 0; c2.login(); // tentativas = 1
Para tornar um membro estático, basta acrescentar o modificador static à declaração do mesmo.
Note que é possível utilizar o membro estático através da própria classe (Usuario.tentativas) ou
através das instâncias (c1.tentativas). Independentemente da abordagem, a variável acessada é a
mesma em ambos os casos.
Embora úteis, membros estáticos exigem alguns cuidados especiais no manuseio. Primeiramente,
não é possível utilizar as referências this e base, uma vez que o membro estático não existe em uma
instância. Pela mesma razão, membros estáticos só podem referenciar outros membros estáticos.
4.7.1 Ocorrências de membros estáticos
Nas bibliotecas do Java encontramos inúmeros casos de atributos e métodos estáticos. Nosso
conhecido método main(), por exemplo, é um método estático, que é chamado pela JVM quando
uma aplicação é executada. A classe Math prove métodos de classe que executam várias funções
matemáticas, como funções trigonométricas e logaritmos. Um exemplo é o método Math.sqrt() que
calcula a raiz quadrada de um número não negativo. A classe System prove variáveis de classe para
representar o estado do sistema inteiro. System.out é uma variável de classe que faz referência ao
objeto PrintStream, que representa o fluxo padrão de saída. O método println() é um método de
instância da classe PrintStream.
4.8 Exercícios
Refaça os exercícios dos itens 2.12 e 4.6 utilizando seus novos conhecimentos
Crie um programa de perguntas e respostas com reprocessamento onde o usuário deverá
responder uma série de perguntas múltipla escolha.
Ao término da jogada, será apresentado o número de acertos realizados na jogada
21
Após mostrar o número de acertos, imprimir também as 3 questões que mais tiveram
respondidas erradas de todos os tempos (não apenas da jogada atual)
22
5. CLASSES ABSTRATAS E INTERFACES
Em grandes sistemas, é comum que a hierarquia de classes torne-se complexa, onde as classes mais
acima (raiz e proximidades) costumar ser bastante gerais, favorecendo o reuso. Passa a ser
interessante a possibilidade de definir protótipos de classes que serão usadas para guiar a criação de
outras classes. Elas implementam ou apenas definem comportamentos que são genéricos o
suficiente para serem comuns a várias outras classes.
5.1 Classes Abstratas
Voltando ao exemplo da classe Animal, podemos notar que a definição de animal é muito ampla e
mesmo que animais possuam características compartilhadas, o modo como de operar de cada tipo
de animal é muito diferente: alguns se locomovem pelo ar, outros por terra, etc. Sendo assim, temos
que a classe Animal pode ser considerada um modelo que não deve ser criado diretamente.
Uma das maneiras de criar tais modelos se dá através do uso de classes abstratas. Uma classe
abstrata é uma classe incompleta que não pode ser construída, necessitando de subclasses que
forneçam os detalhes necessários para que a classe esteja completa. Vejamos um exemplo da
modelagem de animais:
abstract class Animal { String especie; int idade; public abstract void locomover(); public abstract void alimentar(); public void nascer() { ... } public morrer() { ... } }
Foi utilizada a palavra reservada abstract para definir uma classe abstrata e também métodos
abstratos. Se tentarmos instanciar uma classe abstrata receberemos um erro de compilação.
Analogamente, métodos abstratos não possuem corpo, e devem ser obrigatoriamente
implementados nas classes filhas. Uma classe abstrata pode conter:
Métodos e atributos como em classes convencionais. As classes derivadas de uma classe
abstrata herdam da mesma forma os métodos e atributos. Estes métodos podem ser
sobrescritos nas subclasses mas isto não é obrigatório;
Métodos abstratos, que obrigatoriamente devem ser sobrescritos pelas subclasses.
A existência de métodos abstratos ocorre apenas em classes abstratas. Como uma classe abstrata
está num nível muito alto de abstração, muito frequentemente não possui detalhes suficientes para
implementar determinados comportamentos que serão comuns a todas as suas subclasses. Ao invés
disso, elas apenas definem quais serão estes comportamentos obrigatórios através da criação de
métodos abstratos.
Como pode ser visto no exemplo, para definir um método abstrato devemos prover apenas a
assinatura do método, isto é, seu nome, argumentos e tipo de retorno. A implementação (corpo)
deste método ficará a cargo de cada subclasse concreta, que poderá dar a sua versão para ele.
O uso de classes e métodos abstratos é uma forma bastante comum e flexível para criarmos e
utilizarmos bibliotecas orientadas a objetos: por meio de herança associamos nossas classes e
implementamos os métodos abstratos. Por meio do polimorfismo, temos a garantia de que as nossas
implementações serão compatíveis com os locais que fazem uso da classe abstrata.
23
5.2 Interfaces
Suponhamos que exista a necessidade de fazer com que um animal bote ovos. Uma ideia seria
acrescentar o método abstrato “botarOvo()” na classe Animal, mas nada garante que um Animal
tenha a capacidade de botar ovos.
Uma outra solução seria criar outra classe com este método, mas precisamos que nossa classe seja
descendente de Animal, uma vez que o Java não suporta herança múltipla, como no C++. Devemos
recorrer a interfaces para solucionar este problema:
interface Oviparo { public static final int MAX_OVOS = 10; public void botarOvo(); } class Mamifero extends Animal implements Oviparo, Comparable { ... public void botarOvo() { ... } }
Existe muita semelhança entre interfaces e classes abstratas, mas interfaces possuem limitações em
sua constituição: elas podem conter apenas constantes e métodos, sendo que nenhum destes
métodos pode conter implementação (abstratos). Para uma classe implementar uma interface,
devemos usar a palavra reservada implements (diferente de extends), como no exemplo anterior.
É obrigatório que toda classe ao implementar uma interface implemente todos os métodos
especificados pela mesma. Assim, interfaces fornecem a ideia de um “contrato” que qualquer
subclasse deve obedecer. Outra característica importante é que uma classe pode derivar de apenas
uma única outra classe (herança simples), mas pode implementar um número indeterminado de
interfaces.
Uma concepção errônea de quem está se iniciando no Java é que escrever interfaces é perda de
tempo, uma vez que o código escrito “não serve pra nada”, uma vez que a classe implementadora é
obrigada a fornecer uma implementação. Essa é uma maneira errada de pensar – O objetivo do uso
de uma interface é deixar seu código mais flexível, e possibilitar a mudança de implementação sem
maiores traumas (manutenibilidade).
Não é apenas um código de prototipação, tampouco um cabeçalho – Trata-se de uma definição de
um novo tipo e já podemos observar suas vantagens.
5.3 Exercício
Crie um programa de agenda com:
o Uma classe abstrata Pessoa (codigo, nome)
o Uma interface DoadorSangue com o método String getTipoSangue()
o Uma classe Funcionário que estenda Pessoa e implemente DoadorSangue
Inclua o atributo int cracha com set e get
Adicione um atributo String tipoSangue cujo valor deverá se atribuído pelo
construtor
o Uma classe Fornecedor, subclasse de Pessoa, com o campo String ramo
24
6. EXCEÇÕES
Olhando os Javadocs (e recomendo que olhem com frequência), podemos encontrar alguns métodos
que possuem a palavra reservada throws em sua assinatura, como o método File.createNewFile():
http://docs.oracle.com/javase/7/docs/api/java/io/File.html#createNewFile()
A palavra reservada throws indica que este método realiza operações que podem retornar um ou
mais tipos de Exceptions. Mas, afinal, o que são exceptions no Java?
6.1 Definição de Exception
O termo exception é uma abreviação para “evento excepcional” – um evento ocorrido durante a
execução do programa que rompe o fluxo normal de instruções.
Quando um erro ocorre dentro de um método (como tentar acessar uma posição não existente em
um array), o método cria um objeto e entrega-o ao Runtime – a este objeto damos o nome de
exception. Esta exception carrega dentro de si informações relacionadas ao erro, incluindo o tipo
do erro e algumas variáveis indicadoras do estado do programa no momento em que o erro ocorreu.
O ato de criar um objeto da classe Exception é passá-lo ao Runtime é chamado “lançar uma
exception”.
6.2 Classes de Exceções
Tanto as exceções quanto os erros derivam da classe Throwlable:
Erros: São classes filhas da classe Error e indicam que ocorreu um erro irrecuperável,
geralmente fatal para a execução do programa;
Exceções unchecked: Estas exceções são derivadas da classe RuntimeException e
geralmente estão associadas com erros inerentes da execução do programa: acessar posições
inexistentes de um array, chamar um método de um objeto inexistente (null), etc. É possível
optar por não tratar essas exceções;
Exceções checked: Exceções que derivam da classe Exception. O desenvolvedor deve,
obrigatoriamente, capturar exceções deste tipo ou um erro de compilação ocorrerá.
6.3 Tratamento de Exceções
Quando um método lança uma exception, o Runtime encerra a execução do método no ponto onde a
exceção foi lançada e inicia a busca por alguém que consiga tratá-la. A lista de candidatos é
composta pelos métodos que foram chamados até o ponto onde o erro ocorreu. A esta lista de
métodos damos o nome de call stack.
A exception continua rompendo o fluxo dos métodos na call stack até que encontre um bloco de
tratamento de exceptions. A ordem em que a busca ocorre é do método mais próximo da
ocorrência do erro até o método main. Caso o Runtime não encontre nenhum bloco de tratamento
adequado, o programa é terminado.
As exceções existem em duas modalidades: checked e unchecked. A primeira obrigatoriamente
precisa ser envolta em um bloco try-catch, do contrário, um erro de compilação ocorrerá. Como
desenvolvedor Java, ao encontrar um método que lança exceptions, você tem alternativas:
Capturar a exceção e tratá-la
Deixá-la seguir para o método superior na call stack
25
Capturar a exceção e disparar uma exceção diferente
6.3.1 Capturando Exceções
Se um bloco de código possui um ou mais métodos que podem disparar exceções, basta cercá-lo
com um bloco try-catch para permitir que seu código esteja “escutando” possíveis exceções. É
possível (e comum) que exista mais de um bloco de catch para um dado trecho de código,
permitindo que cada catch trate uma exceção de um tipo específico. Opcionalmente, pode-se
incluir um bloco finally após todos os blocos catch, o que sempre é executado ao final, mesmo que
nenhuma exceção ocorra. Um exemplo de exceção aritmética:
try { int erro = 1 / 0; // Divisão por zero } catch (ArithmeticException e) { // Tratamento do erro }
Nota: Se houvesse algum código após a divisão, ele nunca seria executado.
Alterando o exemplo anterior, teremos os seguintes valores impressos no console:
Código Console
try { int erro = 1 / 0; // Divisão por zero } catch (ArithmeticException e) { System.out.println("passo 1"); } finally { System.out.println("passo 2"); }
passo 1 passo 2
Podemos ir além e remover o bloco de catch, deixando apenas o finally. Com isso, o programa
imprimirá “passo 2” no console antes da exception ser lançada para o Runtime e forçar o
encerramento do programa:
passo 2 Exception in thread "main" java.lang.ArithmeticException: / by zero at lecom.curso.Programa.main(Programa.java:7)
Voltando ao método File.createNewFile(), podemos observar que ele existem três possibilidades de
exceção: IOException (checked), IllegalArgumentException (unchecked) e SecurityException
(unchecked). Por ser uma exceção checked, devemos tratar a IOException, ficando a critério do
desenvolvedor tratar as outras duas. Uma possível implementação ficaria como segue:
File f = null; try { f = File.createTempFile("tmp", ".txt"); // … Bloco de operações com 'f' } catch (IOException e1) { // Tratamento de e1 } catch (SecurityException e2) { // Tratamento de e2 } finally { if (f != null) { f.deleteOnExit(); // Limpeza }
26
}
É importante notar que o polimorfismo do Java nos permite construir estruturas elaboradas de
tratamento de exceptions. Complementando o exemplo do File.createTempFile():
try { f = File.createTempFile("tmp", ".txt"); // … Bloco de operações com 'f' } catch (IOException e1) { // Tratamento de e1 } catch (SecurityException e2) { // Tratamento de e2 } catch (Exception e3) { // Tratamento de exceções inesperadas } finally { if (f != null) { f.deleteOnExit(); // Limpeza } }
Veja que acrescentamos um novo bloco de catch com uma Exception e3. O efeito prático dessa
estrutura é que todas as exceções não tratadas pelos blocos de catch anteriores serão direcionadas
para a variável e3. Isso ocorre porque todas as exceções são subclasses de Exception.
6.3.2 Deixando uma Exceção Passar
Existem casos onde não queremos tratar uma exceção do tipo checked. Felizmente, podemos livrar
o método da obrigação de tratar a exceção acrescentando um “throws NomeDaException” à
assinatura do mesmo. Desse modo, estamos delegando a responsabilidade de tratar a Exception para
os métodos acima.
public void meuMetodo() throws IOException { File f = File.createTempFile("tmp", ".txt"); // Restante do método }
6.3.3 Lançando uma Exceção
Como desenvolvedor, seria interessante podermos criar nossas próprias exceções e lançá-las como
resposta a um comportamento inesperado. Felizmente, isso é possível no Java.
Para criar sua própria exceção do tipo checked, basta criar uma classe que seja filha de Exception.
Caso deseje uma exceção unchecked, a classe deve ser filha de RuntimeException.
Lançar uma exceção é igualmente simples: basta utilizarmos o comando throw (não confundir com
o throws que acompanha a assinatura de um método) seguido pelo objeto da exceção:
throw new MinhaException("Erro mortal");
Dica: Podemos encarar o throw como se fosse um return de exceções.
27
6.4 Exercícios
Refaça o exercício do item 5.3 fazendo com que os dados venham de um arquivo e tratando
as Exceptions e criando uma Exception própria que deve ser lançada ao tentar inserir um
contato com nome idêntico a um já existente na agenda.
7. SERVLETS
Hoje é comum vincularmos a ideia de Web com dinamicidade, mudança, mas essa nem sempre foi a
realidade: já houve um tempo onde sites eram apenas sítios de hipertexto estático ligados por meio
de hiperlinks. Tecnologias da atualidade nos permitiram alterar esse panorama e transformar a Web
no que vemos hoje: CGI, PHP, .NET, e, principalmente, Java.
Java ganhou destaque como linguagem para a Web por ter várias características que favoreciam o
desenvolvimento de páginas dinâmicas:
Portabilidade (JVM);
Produtividade (POO, Garbage Collection)
Comunidade ativa e numerosa (Apache Foundation e várias outras instituições open-source)
Escalabilidade (permite arquitetura distribuída)
Eficiência (Servlets persistentes entre requisições)
Recompilação automática (páginas JSP)
7.1 O Protocolo HTTP
Antes de tratarmos de Servlets, é importante entendermos deste protocolo sobre o qual os Servlets
estão fundamentados.
O protocolo HTTP é um protocolo da camada de aplicação utilizado para distribuição de conteúdo
hipermídia de maneira distribuída e colaborativa e é utilizado na navegação de páginas na internet.
Os browsers utilizam o HTTP para obter conteúdo de servidores remotos e mostrá-los localmente.
Os clientes obtêm conteúdo por meio de requisições enviadas ao servidor que, por sua vez, processa
a requisição e devolve uma resposta ao cliente (possivelmente com algum conteúdo hipermídia).
Por ser um protocolo stateless de comunicação cliente-servidor, nenhuma informação é mantida
sobre os clientes ou requisições previamente recebidas.
Requisições HTTP são compostas pelos seguintes elementos:
Um comando, também conhecido como método ou verbo
O endereço de um recurso no servidor, também chamado de caminho ou path
A versão do protocolo HTTP sendo utilizado
Juntando todos esses dados, teremos uma requisição que se assemelha ao exemplo abaixo:
GET / HTTP/1.1
Host: www.google.com.br
Existem diversos métodos HTTP disponíveis para uso nas requisições, mas os mais comuns são:
GET – utilizado para obter algum conteúdo do servidor, como uma página ou imagem
POST – utilizado para enviar dados de formulário ao servidor
28
HEAD – similar ao GET, mas envia apenas os cabeçalhos de resposta
Com a introdução da versão 1.1 do HTTP, novos métodos foram acrescentados:
PUT – armazena a entidade enviada no corpo da requisição
DELETE – remove o recurso especificados
OPTIONS – lista os métodos suportados pelo servidor no endereço fornecidos
Uma requisição também pode conter parâmetros adicionais, chamados cabeçalhos ou
headers. Os headers mais comuns são:
User-Agent – contém informações sobre o cliente que iniciou a requisição (browser, versão,
etc).
o Chrome: Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML,
like Gecko) Chrome/33.0.1750.146 Safari/537.36
Accept – indica quais os tipos de recursos aceitos pelo cliente
o Exemplos: */* (tudo), text/html, image/jpeg, etc.
Exemplo atualizado com os cabeçalhos:
Estrutura Exemplo
<Verbo> <Caminho> <Versão HTTP> GET /ExemploServlet/ImprimeIp HTTP/1.1
Host: <Endereço base>
<Nome do cabeçalho 1>: <Valor 1>
...
<Nome do cabeçalho n>: <Valor n>
Host: localhost:8080
User-Agent: curl/7.30.0
Accept: */*
IMPORTANTE: A ordem precisa ser respeitada para que o servidor entenda a requisição
corretamente.
A padronização também está presente na resposta do servidor. Uma resposta bem formada contém:
Versão do protocolo, como na requisição
Código de status indicando sucesso (ou não) ao processar a requisição, acompanhado por
mensagem auxiliar
Cabeçalhos, com informações adicionais com o tipo do conteúdo sendo retornado,
Corpo da resposta, contendo o conteúdo solicitado
Eis um exemplo de reposta completa:
Estrutura Exemplo
<Versão HTTP> <Código> <Mensagem>
<Nome do cabeçalho 1>: <Valor 1>
…
<Nome do cabeçalho n>: <Valor n>
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Cache-Control: no-cache
Pragma: no-cache
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Transfer-Encoding: chunked
29
Date: Sun, 09 Mar 2013 03:43:12 GMT
<Conteúdo> <html> <body>
<h1>Hello World</h1>
</body> </html>
No exemplo anterior, o código de status 200 indica que a requisição do cliente foi bem-sucedida. Os
cabeçalhos trazem informações adicionais sobre o conteúdo retornado, como instruções para que o
browser não faça cache da resposta. O conteúdo retornado é textual e corresponde a uma página
HTML, porém dados binários também podem fazer parte de uma resposta.
A tabela abaixo contempla os códigos mais comuns e seus significados:
Código da resposta Mensagem Significado
200 OK Requisição processada com sucesso
302 Moved Temporarily Conteúdo solicitado está em outar URL
404 Page Not Found Conteúdo solicitado não foi encontrado
500 Internal Server Error Erro inesperado no processamento da requisição
503 Service Unavailable Serviço indisponível no momento
7.2 Common Gateway Interface
O advento do Common Gateway Inteface, mais conhecido por CGI possibilitou o surgimento de
conteúdo dinâmico na Web. Neste modelo, uma aplicação (processo) era executada a cada
requisição HTTP, passando os parâmetros para a aplicação via Variáveis de Ambiente. A aplicação,
por sua vez, retornava o código HTML que deveria ser mostrado para o browser que iniciou a
requisição.
A utilização de Servlets trouxe várias melhorias ao fluxo acima:
Maior desempenho resultante da persistência do Servlet na memória, que não precisa ser
iniciado a cada nova solicitação. Além disso, conexões ociosas ao banco de dados são
mantidas abertas para evitar o overhead de iniciar novas conexões (pool de recursos);
Implantação facilitada pelo uso de uma máquina virtual que abstrai os detalhes do hardware
subjacente aumentando o nível de padronização e portabilidade;
Distribuição facilitada em ambientes heterogêneos em função do uso de uma máquina
virtual.
7.3 O que são Servlets?
Servlets são classes Java usadas para estender as capacidades dos servidores que hospedam
aplicações acessadas por meio de um modelo de requisição-resposta (como o HTTP). Embora
Servlets possam ser usadas para responder qualquer tipo de requisição, o uso mais comum é para
estender a capacidade de aplicações hospedadas por servidores Web. Para aplicações deste tipo, a
tecnologia de Servlets Java oferece classes específicas para uso com HTTP.
Para que um servidor possa executar um Servlet, é preciso que ele implemente um Servlet
Container, também conhecido como Servidor de Aplicações Java. Um Servlet Container é um
30
servidor que suporta as tecnologias JSP, Servlet, JSTL e JSF mas não o Java EE completo. Um dos
mais conhecido é o Apache Tomcat.
No contexto Web, o Servlet é reponsável por receber requisições HTTP feitas ao servidor e, a partir
dos parâmetros recebidos, realizar o processamento relevante para finalmente retornar uma resposta
ao cliente solicitante – seja uma página HTML, uma imagem JPEG, um arquivo JSON, etc.
Os pacotes javax.servlet e javax.servlet.http fornecem as interfaces e classes necessárias para que
possamos iniciar o desenvolvimento de Servlets para Web. Todos os Servlets precisam implementar
a interface Servlet, a qual define os métodos para gestão do ciclo de vida. A classe HttpServlet
oferece métodos como doGet() e doPost() para lidar com requisições HTTP.
7.4 Funcionamento da Servlet
Para ilustrar o modo de operação do Servlet, começaremos com um exemplo simples. O servlet
abaixo faz com que todas as requisições HTTP GET direcionadas para a URL <endereço do
host>/<nome do servlet>/ImprimeIp recebam uma página HTML exibindo o IP do solicitante.
package org.exemplo; import java.io.*; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.*;
@WebServlet("/ImprimeIp") public class ImprimeIp extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { PrintWriter out = response.getWriter();
out.println("<html> <body>"); out.printf("<h1>Seu IP: %s</h1>", request.getRemoteAddr()); out.println("</body> </html>"); out.flush();
}
}
Quando o HttpServlet recebe a requisição, ele verifica o verbo HTTP utilizado (GET, POST,
PUT, ...) e busca o método doXXX() correspondente. Caso exista, o método é chamado com dois
parâmetros:
Um objeto da classe HttpServletRequest, que encapsula a requisição recebida; e
Um objeto da classe HttpServletResponse, que encapsula a resposta do servlet.
Para devolver uma resposta ao cliente que iniciou a requisição, precisamos obter a instância do
PrintWriter associado a resposta. O programa utiliza este PrintWriter para escrever no Stream de
resposta vinculado ao cliente que iniciou a requisição.
A instância do PrintWriter é obtida utilizando o método getWriter() presente no objeto response. A
classe PrintWriter possui métodos que facilitam a escrita de texto num Stream, além de realizar a
bufferização automática do output. No exemplo, utilizamos os métodos println() e printf() para
adicionar Strings o Stream do servlet e, ao final, utilizamos o método flush() para forçar a escrita
dos dados no Stream de saída.
No exemplo, não estamos tratando a IOException lançada pelo getWriter(). No entanto, é
considerada uma boa prática capturar exceções lançadas para a geração de logs no servidor.
try { PrintWriter out = response.getWriter();
out.println("<html> <body>");
31
out.printf("<h1>Seu IP: %s</h1>", request.getRemoteAddr()); out.println("</body> </html>"); out.flush();
} catch (IOException e) { ServletContext context = getServletContext();
context.log("Erro ao obter writer", e); }
7.5 Respostas
Servlets realizam trabalho por trás das cortinas durante o processamento de uma requisição. Embora
não esteja visível no exemplo anterior, o texto que escrevemos no Stream de saída passou por
alguns processamentos até finalmente chegar ao formato final. Antes de chegar ao cliente, a
resposta precisa ser envelopada numa mensagem HTTP válida. O Servlet se encarrega de
acrescentar os cabeçalhos HTTP à nossa resposta. Eis a resposta retornada pelo Servlet escrito no
exemplo anterior:
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Transfer-Encoding: chunked
Date: Sun, 09 Mar 2013 03:01:16 GMT
Cabeçalhos: Populados automaticamente
<html> <body>
<h1>Seu IP: 0:0:0:0:0:0:0:1</h1></body> </html>
Corpo: Populado pelo doGet()
Quando aplicações Web precisam de controle sobre os cabeçalhos retornados, é possível utilizar o
método setHeader() do objeto de response para definir ou modificar cabeçalhos HTTP. Um caso
de uso comum é acrescentar cabeçalhos que instruam o browser do cliente a não fazer cache das
respostas. Existem também as variações setDateHeader() e setIntHeader(), usadas para headers com
valores de data e inteiros, respectivamente.
Voltemos ao exemplo Java, agora com setHeader():
response.setHeader("Cache-Control","no-cache"); // HTTP 1.1 response.setHeader("Pragma","no-cache"); // HTTP 1.0 response.setDateHeader ("Expires", 0); // Proxy response.setDateHeader("Last-Modified", System.currentTimeMillis()); PrintWriter out = response.getWriter();
out.println("<html>"); ...
E os cabeçalhos da nova resposta:
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Cache-Control: no-cache
Pragma: no-cache
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Last-Modified: Sun, 09 Mar 2013 03:43:12 GMT
Transfer-Encoding: chunked
Date: Sun, 09 Mar 2013 03:43:12 GMT
IMPORTANTE: A alteração dos cabeçalhos deve ser feita ANTES de que qualquer conteúdo de
saída seja escrito. A ordem dos elementos na resposta é crítica para obter o resultado desejado.
Primeiro o status (HTTP/1.1 200 OK), depois os cabeçalhos e por fim o corpo da mensagem. Violar
32
esta regra fará com que seja lançada uma IllegalStateException, consequentemente interrompendo
a geração de uma resposta ao cliente.
O header Content-Type é usado para sinalizar ao cliente o tipo de conteúdo sendo retornado e é útil
para auxiliar os browsers a mostrar o conteúdo de maneira inteligente. Entre os possíveis valores
para esse header, temos: text/html para páginas HTML, image/jpeg para imagens no formato JPEG,
application/octet-stream para dados binários, etc. Devido à grande utilização, foi criado o método
setContentType(), próprio para definir valores ao header Content-Type.
Exemplo:
FileInputStream in = null; try { response.setContentType("image/jpeg"); in = new FileInputStream("imagem.jpg"); // Obtem stream de saída binária do Servlet ServletOutputStream out = response.getOutputStream();
byte[] buffer = new byte[1024]; int bytes = 0; // Copia conteúdo binário para saída while ((bytes = in.read(buffer)) >= 0) { // write() pode lançar IOException out.write(buffer);
}
out.flush();
in.close();
}
catch (IOException io) { // Tratamento de erro }
Nota: O trecho acima pode demorar alguns segundos para finalizar a execução, pois, os dados são
enviados gradualmente para o cliente solicitante à medida que o Servlet escreve na
ServletOutputStream. Caso a conexão seja encerrada (ex: browser foi fechado durante a
execução), o método write() lançará uma IOException.
7.5.1 Exercícios
Crie uma Servlet que retorne uma página HTML com uma mensagem de boas-vindas caso o
IP do cliente seja igual ao IP contido em um arquivo.
o No Eclipse, crie um novo Dynamic Web Project
7.5.2 Status HTTP
Como já vimos, toda resposta a uma requisição HTTP começa com o status da resposta. O status
segue o formato <versão do http> <código da resposta> <mensagem auxiliar> e é responsável
por indicar se a requisição foi bem-sucedida (ou não).
Podemos controlar o status das respostas geradas por nossa aplicação assim como fazemos com os
cabeçalhos – basta utilizarmos o método setStatus() do HttpServletResponse. A fim de melhorar a
legibilidade do código, a classe HttpServletResponse define constantes para cada um dos códigos de
status. Caso o setStatus() não seja utilizado, considera-se que houve sucesso no processamento e o
código 200 é retornado.
Código Constante
200 SC_OK
302 SC_MOVED_TEMPORARILY
33
404 SC_NOT_FOUND
500 SC_INTERNAL_SERVER_ERROR
503 SC_SERVICE_UNAVAILABLE
7.5.3 Exercícios
Utilizando o exercício do item 7.5.1 como base, altere o status da resposta para 404 quando
o arquivo não for encontrado
7.6 Requisições
7.6.1 Parâmetros da requisição
Para um Web efetivamente dinâmica, o cliente precisa de meios de enviar dados para que o servidor
consiga produzir conteúdos variados. Formulários HTML são uma das opções, uma vez que é
possível utilizá-los para enviar dados ao servidor por meio do método HTTP Post. Os dados a
serem enviados são incorporados como parâmetros ao corpo da requisição, podendo ser recuperados
posteriormente no servidor.
Outra forma de enviar parâmetros ao servidor é através da concatenação dos mesmos à URL de uma
requisição GET. Para isso, precisamos acrescentar o caractere interrogação ( ? ) ao final da URL
e, em seguida, acrescentar os parâmetros no formato <nome parâmetro>=<valor parâmetro>
separados entre si por um caractere & . Exemplo:
<URL base>/ExemploServlet/?nome=fanta&idade=26
A API de Servlets disponibiliza diversos métodos para capturar os parâmetros de uma requisição:
String HttpServletRequest.getParameter(String paramName) – Retorna o parâmetro
especificado em paramName, ou null caso o parâmetro não exista
Enumeration HttpServletRequest.getParameterNames() – Retorna um enumerador com
os nomes de todos os parâmetros
String HttpServletRequest.getQueryString() – Retorna a parte da URL de requisição que
vem após o caractere interrogação ( ? ). Ex: nome=fanta&idade=26
7.6.2 Exercícios
Crie uma Servlet que receba os parâmetros altura e peso de uma requisição GET. Responda
com uma página contendo o cálculo do Índice de Massa Corporal (IMC). Caso algum dos
parâmetros não esteja presente, responda com o código de status 500 (ver item 7.5.2)
o Cálculo do IMC: peso / (altura * altura)
Crie uma página HTML com um formulário de login e senha que envie uma requisição
POST para uma Servlet. A Servlet retornará uma página de boas vindas em caso de sucesso.
Caso contrário, responda com o código de status 401 (Forbidden).
7.6.3 Cabeçalhos de Requisição
Vimos que uma mensagem de resposta HTTP é composta pelo status, cabeçalhos e corpo.
Requisições possuem uma estrutura similar, como podemos ver no exemplo abaixo:
Estrutura Exemplo
<Verbo> <URL> <Versão HTTP> GET /ExemploServlet/ImprimeIp HTTP/1.1
34
<Nome do cabeçalho 1>: <Valor 1>
<Nome do cabeçalho 2>: <Valor 2>
<Nome do cabeçalho n>: <Valor n>
User-Agent: curl/7.30.0
Host: localhost:8080
Accept: */*
A classe HttpServletRequest fornece métodos para obter mais informações sobre a requisição:
String HttpServletRequest.getHeader(String name) – Retorna o valor cabeçalho
especificado em name, ou null caso o cabeçalho não exista
long HttpServletRequest.getDateHeader(String name) – Retorna o valor do cabeçalho
como um long que representa uma data. O valor retornado equivale ao número de
milissegundos desde 1o de Janeiro de 1970 ou -1, caso não o cabeçalho não exista. Se o
valor do cabeçalho não for uma data, uma IllegalArgumentException é lançada
int HttpServletRequest.getIntHeader(String name) – Retorna o valor do cabeçalho como
um int. Caso o cabeçalho não exista, retorna 01. Se o valor do cabeçalho não puder ser
convertido para um int, uma NumberFormatException é lançada
Enumeration HttpServletRequest.getHeaderNames() – Retorna um enumerador com os
nomes de todos os cabeçalhos presentes na requisição
Um cabeçalho bastante utilizado é o User-Agent, pois nele estão contidas informações sobre o
cliente que iniciou a requisição (software e versão). Estas informações são úteis para que o Servlet
possa gerar uma versão de conteúdo especialmente adaptada para o cliente, como um site que
oferece uma versão para smartphones e outra para desktops.
35
8. COOKIES
Por ser um protocolo stateless, o HTTP não mantém informações sobre requisições passadas e,
portanto, é incapaz de agrupar requisições de um mesmo cliente. Isso representa um problema para
aplicações que dependam de ações executadas anteriormente para poder operar, como é o caso de
sistemas que possuem algum tipo de autenticação (e-commerce, webmail, etc).
Uma das possibilidades é o uso de Cookies para realizar a persistência de informações (estado)
associadas a um cliente.
8.1 Definição
Cookies são pacotes de dados gerados pelo servidor e enviados ao cliente juntamente com a
resposta da requisição. Esses dados são armazenados pelo browser do usuário, que se encarrega de
enviá-los a cada requisição efetuada após o recebimento do cookie. Com isso, o servidor é capaz de
fazer a distinção entre clientes e recuperar outras informações necessárias para a geração de uma
resposta.
8.2 Estrutura
Além do nome, valor e caminho, cookies possuem os seguintes campos:
Comment: Comentário descrevendo o propósito do cookie
MaxAge: Intervalo de tempo (em segundos) no qual o cookie é considerado válido
Domain: Define todos os servidores abaixo de um mesmo domínio
Path: Define os recursos abaixo do diretório virtual do recurso
8.3 Adicionando Cookies
Para a manipulação de cookies, a API de Servlets disponibiliza a classe javax.servlet.http.Cookie
que encapsula dados e operações de um cookie. Os campos de um cookie podem ser recuperados /
alterados utilizando os vários getters e setters disponíveis na classe.
O primeiro passo para utilizar um cookie é criá-lo passando ao construtor o nome e o valor do
mesmo. O próximo passo é definir os campos (Comment, MaxAge, ...) relevantes ao objeto criado.
Por fim, o cookie precisa ser adicionado ao objeto HttpServletResponse para que seja enviado ao
browser do cliente, como mostra o exemplo:
Cookie cookie = new Cookie("Nome", "Valor"); cookie.setMaxAge(120);
cookie.setComment("Comentario sem acento"); response.addCookie(cookie);
Importante: NÃO utilize caracteres acentuados tanto em cookies quanto em cabeçalhos.
O trecho acima cria um cookie com nome “Nome” e valor “Valor”. Ele será válido por 120
segundos a partir do instante em que for recebido e será excluído pelo browser após expirar. A
resposta é acrescida do cabeçalho abaixo:
Set-Cookie: Nome=Valor; Version=1; Comment="Comentario sem acento"; Max-Age=120;
Expires=Sun, 09-Mar-2013 18:30:04 GMT
Caso o valor fornecido para MaxAge seja negativo, o cookie será mantido até que o browser seja
encerrado. Se o valor for 0, o browser removerá o cookie imediatamente.
36
Assim como ocorre com todo cabeçalho, a adição de cookies na resposta do Servlet deve ocorrer
antes que o conteúdo da resposta comece a ser escrito no Stream de saída.
8.4 Recuperando Cookies
Cookies podem ser extraídos das requisições utilizando o método getCookies() da classe
HttpServletRequest. O método retorna um array de objetos da classe Cookie, ou null caso não
haja nenhum. Não existe um método que recupere um cookie específico a partir do nome, ficando
para o desenvolvedor interar pelo array retornado em busca do cookie desejado.
8.5 Exercícios
Utilizando como base o Servlet de login do item 7.6.2, utilize um cookie para persistir as
informações de login, evitando que o usuário precise digitar suas credenciais novamente
37
9. SESSÕES
Além de cookies, podemos utilizar Sessões, ou Sessions, para persistir informações de estado entre
requisições de um mesmo cliente. A API de Servlets possui um módulo de Gerenciamento de
sessões de usuário onde podemos encontrar a classe HttpSession que possui métodos para
manipulação de sessões.
9.1 Funcionamento
É criado, no primeiro acesso, um identificador de sessão, ao qual é associado um objeto da classe
javax.servlet.http.HttpSession na memória do container. O container, por sua vez, encarrega-se de
carregar este identificador para todas as requisições oriundas daquele usuário. Para isso, utiliza-se 1)
Cookies ou 2) URLs reescritas (informações adicionadas ao caminho) caso a opção (1) esteja
disponível (usuário desabilitou os cookies, por exemplo). Com o identificador em mãos, a API
disponibiliza o objeto HttpSession automaticamente para o Servlet.
O Servlet usa o objeto HttpSession em memória para armazenar informações associadas a um
usuário específico que acessa a aplicação. Essas informações estarão disponíveis em todas as
requisições posteriores a todos os Servlets que compartilham o mesmo contexto. Para obter uma
referência ao objeto da classe HttpSession, um Servlet deve utilizar o método getSession() da classe
HttpServletRequest. Olhando a documentação, é possível verificar que existem duas sobrecargas:
HttpSession getSession(boolean create) – Retorna a HttpSession associada a requisição ou,
se não houver uma sessão e create for true, retorna uma nova sessão, caso contrário, retorna
null
HttpSession getSession() – Equivale a getSession(true)
O trecho a seguir obtém o objeto de sessão, se houver, ou cria um novo, caso ainda não exista:
HttpSession session = request.getSession();
if (session.isNew()) { // Acabou de ser criada getServletContext().log("ID: " + session.getId()); // Inicializa session }
else { // Recupera dados da session }
O método isNew() é utilizado para determinar se o identificador de sessão já foi enviado para o
browser cliente. Caso seja uma sessão recém-criada, nós efetuamos a inicialização da mesma com
dados que gostaríamos de recuperar em requisições futuras (usuário, senha, etc). Além deste método,
a classe HttpSession oferece vários outros métodos. Entre eles estão:
void setAttribute(String attributeName, Object attributeValue)
Object getAttribute(String attributeName)
void removeAttribute(String attributeName)
Enumeration getAttributeNames()
Estes métodos permitem que o desenvolvedor controle os objetos vinculados à sessão do usuário de
maneira similar ao funcionamento de um HashMap:
sessao.setAttribute("Contador", new Integer(contador)); ...
Integer contador = (Integer) sessao.getAttribute("Contador");
38
9.2 Exercícios
Substitua o uso de cookies por sessões no exercício do item 8.5
9.3 Validade da Sessão
É possível definir um tempo máximo de inatividade que, se ultrapassado, faz com que a sessão
expire – um recurso bastante utilizado pelos sites de Internet Banking para aumentar a segurança do
usuário. Para que a sessão expire, basta que o cliente não execute nenhuma requisição durante o
tempo limite. Quando isso ocorre, o usuário precisa de uma nova sessão válida, geralmente obtida
por meio de um novo login.
O desenvolvedor também pode revogar a validade de uma sessão manualmente. É este o caso em
lugares onde o usuário efetua logout. Para controlar a validade da sessão, utilizamos os seguintes
métodos da classe HttpSession:
int getMaxInactiveInterval() – obtêm o intervalo máximo de inatividade em segundos
void setMaxInactiveInterval(int interval) – atribui o intervalo máximo de inatividade
invalidate() – invalida sessão manualmente
9.4 Exercícios
Personalize a página de boas-vindas do exercício no item 9.2 mostrando o nome do usuário
autenticado e acrescente um botão de logout
39
10. CICLO DE VIDA DA SERVLET
O ciclo de vida de um Servlet é composto de 3 fases: inicialização, atendimento de requisições e
finalização.
10.1 Inicialização
Processo onde o Servlet é carregado pelo Servlet Container. O instante em que a inicialização
ocorre depende do campo loadOnStartup na annotation @WebServlet: caso o valor seja um
inteiro positivo, o Servlet é inicializado automaticamente pelo Container quando a aplicação é
iniciada, começando pelos menores valores. Caso contrário, a inicialização ocorre quando é
recebida a primeira requisição a ser mapeada para o Servlet. Exemplo de annotation:
@WebServlet(urlPatterns = "/MeuServlet", loadOnStartup = 1)
Na inicialização, o Servlet Container executa o método init() do Servlet, dando chance ao Servlet
de executar quaisquer passos necessários para sua inicialização, tais como:
Leitura de parâmetros de configuração
Inicialização de variáveis de classe (variáveis estáticas)
Inicialização de conexões ao banco de dados, etc
Ao sobrescrever o init() em seu Servlet, utilize o método getServletConfig() para obter uma
instância da classe ServletConfig, a qual guarda os parâmetros de inicialização do Servlet. Em
seguida, invoque o método getInitParameter() do ServletConfig para obter o parâmetro desejado.
public void init() throws ServletException { super.init(); ServletConfig config = getServletConfig();
String smtp = config.getInitParameter("Email.SMTP"); if (smtp != null) { // Logica de inicialização }
}
Também é possível obter um enumerador contendo o nome de todos os parâmetros do Servlet
Config por meio de uma chamada ao método getInitParameterNames().
O Servlet somente poderá receber requisições após a conclusão de seu processo de inicialização.
Para indicar que houve um problema durante a inicialização, o desenvolvedor deve lançar uma
exception da classe ServletException ou UnavailableExcetion. Nestes casos, o Servlet Container
deixará o Servlet em estado inativo, incapaz de receber requisições.
Observando o construtor da UnavailableException, pode-se notar que uma das sobrecargas possui
um parâmetro do tipo int. Este parâmetro recebe a estimativa, em segundos, de quanto tempo o
Servlet deverá ficar inativo.
10.2 Atendendo as Requisições
Após o término bem-sucedido do método init(), o Servlet é marcado como ativo pelo Servlet
Container e está pronto para receber requisições. Na classe GenericServlet, utilizada para Servlets
genéricos e superclasse da HttpServet, o método service() centraliza o processamento de
requisições.
A cada nova requisição recebida, o Servlet Container faz uma chamada ao método service()
40
passando como parâmetros um objeto que encapsula a requisição feita pelo cliente e outro objeto
que encapsula a resposta que deverá ser encaminhada ao cliente. Felizmente, como vimos no item
7.4, a classe HttpServlet já cuida de encaminhar a requisição para o método relevante: doGet(),
doPost(), etc.
10.3 Finalização
O Servlet é finalizado quando o servidor é finalizado ou quando a aplicação se torna inativa pelo
Servlet Container. A imagem abaixo traz uma ilustração do ciclo de vida nos vários estágios de uma
aplicação.
A finalização de um Servlet deve ser tratada através da implementação do método destroy(). No
instante em que o Servlet inicia a sua finalização, o método destroy() é chamado, permitindo a
execução de rotinas de finalização como: Encerramento de conexões com bancos de dados,
Finalização de threads que tenham sido lançados, etc. Exemplo:
public void destroy() { super.destroy(); // Logica de finalização }
10.4 Servlet Context
O objeto ServletContext contém os atributos e informações sobre o contexto em que o Servlet está
sendo executado e contém métodos úteis para Servlets que desejam se comunicar com o Servlet
Container para, por exemplo, escrever num arquivo de log.
Existe um ServletContext por aplicação web por JVM e, portanto, todos os Servlets de uma mesma
aplicação compartilham o mesmo ServletContext. O contexto é retornado a partir da chamada do
método getServletContext() presente nas Servlets.
41
A classe define métodos para recuperar os parâmetros iniciais do contexto definidos no Deployment
Descriptor: getInitParameterNames() e getInitParameter(). É importante notar que existem
diferenças entre os parâmetros iniciais do contexto e os parâmetros iniciais do Servlet.
O objeto ServletContext também possui métodos que podem ser usados para atribuir e recuperar
atributos compartilhados por todos os Servlets do contexto:
Object getAttribute(String name)
Enumeration getAttributeNames()
void removeAttribute(String name)
void setAttribute(String name, Object value)
O funcionamento destes métodos é análogo aos métodos estudados no item 9.1.
Um recurso bastante utilizado do ServletContext e presente em vários exemplos nesta apostila é o
método log(). Ele permite que você adicione mensagens em um arquivo de log do Servidor de
Aplicações. Você poderá utilizar esse método para depurar seus Servlets, gerar alertas de problemas
na sua execução etc.
No Tomcat, as mensagens geradas por meio do método log() são adicionadas a um arquivo de log
chamado localhost.<aaaa-mm-ddd>.log , onde o bloco < > é substituído pela data da geração do
log.
10.5 Exercícios
Implemente os métodos init() e destroy() da Servlet do exercício no item 9.4:
o O init() deverá ler o arquivo que contém os dados do usuário e senha e inseri-los
num array de usuários para evitar operações de leitura de arquivo em toda requisição.
Além disso, também deverá abrir um arquivo para escrita e armazenar a instância
num campo do Servlet.
o O destroy() deverá fechar o arquivo que foi aberto para escrita durante o init()
42
11. VISÃO GERAL SOBRE JSP
A tecnologia Java Server Pages, também conhecida com JSP, facilita a criação de conteúdo web
que contenha partes estáticas e dinâmicas. O JSP disponibiliza todas as capacidades dinâmicas da
tecnologia de Servlets, mas possui uma abordagem mais natural para a criação de conteúdo estático.
A principal motivação para o uso da tecnologia JSP é evitar a formatação de saídas de texto em
Servlets, concentrando no Servlet a lógica de controle e mantendo a responsabilidade sobre
formatação de conteúdo em outro arquivo. Além de facilitar a manutenção, conseguimos paralelizar
o desenvolvimento em duas frentes.
11.1 O que é uma página JSP?
Uma página JSP é um documento texto que mescla dois tipos de texto: dados estáticos, que podem
ser expressos em qualquer formato baseado em texto (como HTML, SVG, XML, etc) e elementos
JSP, utilizados para a geração de conteúdo dinâmico. As páginas JSP podem ser reconhecidas pela
sua extensão “.jsp” e podem ser encontradas na pasta “WebContent” do projeto Java Web.
Os elementos dinâmicos que disponibilizados pelo JSP são:
Elementos Sintaxe
Comentários <%--.. --%>
Declarações <%! ..%>
Diretivas <%@ include .. %>
<%@ page .. %>
<%@ taglib .. %>
Expressões <%= ..%>
Scriptlets <% ..%>
11.2 Diretivas
Diretivas são elementos que encaminham mensagens para o Container JSP e afetam a compilação
da página JSP. As diretivas não aparecem no XML de saída.
Formato Exemplo
<%@ diretiva nomeAtributo1 = “valorAtributo1” nomeAtributo2 = ”valorAtributo2” ... %>
<%@ page language = "java" contentType = "text/html" pageEncoding = "ISO-8859-1" %>
Nota: Cada diretiva possui seu próprio conjunto de atributos específicos.
Existem três diretivas: page, include e taglib (não contemplada nesta apostila)
page – Define propriedades relacionadas ao formato e linguagem da página atual. Este
elemento precisa ser filho direto do nó raiz. Alguns dos atributos são: language,
contentType, pageEncoding, import, errorPage e isErrorPage
include – Utilizando para inserir o conteúdo de outro arquivo (seja texto estático ou outra
página JSP) no documento atual. Este elemento pode ser colocado em qualquer lugar do
documento e seu possui apenas o atributo file, que deve conter o caminho para o arquivo a
ser incluído. O funcionamento é similar ao da diretiva <include> do C / C++
43
11.2.1 Diretiva page
O atributo info é usado para armazenar texto informativo sobre a página e podemos obter o
conteúdo utilizando o método getServletInfo() do Servlet. Exemplo:
<%@ page info="Autor: Fanta" %>
O atributo contentType informa o tipo de conteúdo gerado pelo documento JSP. O propósito é
permitir que o browser consiga interpretar a página corretamente. Exemplo:
<%@ page contentType="text/html" %>
O atributo pageEncoding informa a codificação do texto utilizado na página. Sem esse atributo, o
conteúdo pode ser exibido incorretamente no browser.
<%@ page pageEncoding="ISO-8859-1" %>
O atributo import funciona de maneira análoga ao import do Java. Todos os pacotes utilizados no
documento JSP devem ser declarados utilizando este atributo. Exemplo:
<%@ page import="java.util.*" %>
O atributo errorPage é usado para indicar a página JSP que deverá ser exibida caso ocorra algum
erro durante o processamento da página que contém este elemento. Para habilitar esse
comportamento, a página que fará o tratamento de erros deverá declarar a diretiva page com o
atributo isErrorPage = true.
11.2.2 Diretiva include
A diretiva include nos permite compartilhar o conteúdo comum a várias páginas JSP, seja ele
dinâmico ou estático, facilitando a manutenção das mesmas. Pode-se, por exemplo, construir novas
páginas JSP que incluem os arquivos cabecalho.jsp e rodape.html. Caso seja necessário mudar o
cabeçalho ou rodapé, não precisaremos alterar todas as páginas, apenas estas duas. Exemplo:
<%@ include file=“cabecalho.jsp” %> <!-- Conteúdo -->
<%@ include file=“rodape.html” %>
11.3 Expressões
Expressões utilizam a sintaxe <%= ..%> para imprimir o resultado de um comando Java durante a
geração do conteúdo. Um trecho de código que imprime o IP do cliente que iniciou a requisição
seria:
<p>O seu endereço IP é "<%= request.getRemoteAddr()%>"<p>
Importante: O resultado da expressão deve ser do tipo String. Algumas alternativas são utilizar o
método toString() do objeto ou o método estático String.format()
11.4 Exercícios
Utilize elementos dinâmicos para melhorar o exercício no item 10.5:
44
o Crie uma página JSP de cabeçalho que imprima o nome do usuário autenticado
o Crie uma página JSP de rodapé que imprima a data atual (dd/mm/aaaa)
o Transfira a geração de HTML para uma página JSP
Dica: Na Servlet de autenticação, utilize o método sendRedirect() para
enviar o usuário para a página .JSP de boas-vindas
11.5 Scriptlets
Scriptlets permitem a inserção de um bloco de código Java diretamente no corpo da página JSP e
possuem o seguinte formato:
Formato Exemplo
<!-- HTML --> <% /* Bloco Java */ %> <!-- HTML -->
<p><% String arg = request.getParameter("r"); if (arg != null && arg.equals("42")) { out.println("Found!"); }
else { out.println("Nothing here"); }
out.flush();
%></p>
11.6 Objetos Implícitos
Para facilitar o trabalho do desenvolvedor, alguns objetos são disponibilizados pelo JSP Container
sem que seja necessária a sua declaração (como o objeto request do exemplo anterior). Os mais
comuns são:
Objeto Classe Função
request HttpServletRequest Encapsula a request enviada à página
response HttpServletResponse Encapsula a response que será devolvida ao cliente
out JspWriter Utilizada para escrever na Stream de saída
session HttpSession Encapsula a sessão associada à request
config ServletConfig ServletConfig associado à página
application ServletContext Contexto da Servlet
11.7 Declarações
O elemento de declaração é usado para definir variáveis e métodos que deverão ser acrescentados à
definição do Servlet JSP. Membros declarados num elemento de declaração ficam disponíveis para
uso nas Scriptlets e Expressões, assim como os objetos implícitos.
Formato Exemplo
<%! /* Declaração */ %> <%!
public void erro(String msg) { getServletContext().log("ERRO: " + msg); }
%>
...
<%
45
if (request.getParameter("url") == null) { erro("Parâmetro 'url' não encontrado"); }
%>
As declarações também podem ser usadas para a declaração dos métodos public void jspInit() e
public void jspDestroy(), usados para inicializar a página JSP (ler dados de configuração, por
exemplo) e liberar recursos, respectivamente.
Top Related