pacote

Propaganda
Programação por Objectos
Java
Parte 10: Pacotes, excepções e asserções
LEEC@IST
Java – 1/54
Pacotes – revisão (1)
• Um pacote é um mecanismo de agrupamento de
informação:
– Os pacotes podem conter outros pacotes, classes,
interfaces e objectos.
– O pacote forma um espaço de nomes (namespace), logo
os seus membros têm de ter identificadores únicos (por
exemplo, num pacote não pode haver duas classes com o
mesmo nome).
– O identificador dum pacote pode consistir num nome
simples ou num nome qualificado. O nome qualificado
corresponde ao nome simples prefixado pelo nome do
pacote onde este reside, se existir. É usual usar-se :: para
separar os nomes simples.
LEEC@IST
Java – 2/54
Pacotes – revisão (2)
– A importação adiciona o conteúdo do pacote importado ao
espaço de nomes do pacote importador, de tal forma que
passa a ser desnecessário usar o seu nome qualificado.
– A importação não é transitiva.
•
•
Se o pacote B importar o pacote A, e o pacote C importar o
pacote B, no pacote C não são importados os elementos do
pacote A.
Caso o pacote C queira importar igualmente os elementos
do pacote A, devem ser inseridas duas directivas de
importação, uma para o pacote A e outra para o pacote B.
• O conjunto de métodos de um pacote é referido por
API (Application Programmer Interface).
LEEC@IST
Java – 3/54
Pacotes (1)
• Os pacotes são úteis por várias razões:
– Agrupam interfaces e classes relacionadas num mesmo
pacote (que depois pode ser disponibilizado num ficheiro
jar, juntamente com o MANIFEST.MF descrevendo o
pacote).
– Criam um espaço de nomes que evita conflito de nomes
entre tipos definidos fora do pacote (possível uso de
nomes populares, por exemplo, List).
– Oferecem um domínio protegido para o desenvolvimento
de aplicações (o código dentro do pacote pode cooperar,
usando o acesso que lhe é oferecido aos membros das
classes e interfaces do pacote, acesso esse que
normalmente é mais restrito para código externo).
LEEC@IST
Java – 4/54
Pacotes (2)
• Uma classe é inserida num pacote pela directiva
package IdPacote;
• Uma declaração package tem de ser a primeira
coisa a aparecer num ficheiro fonte, antes da
declaração de qualquer classe ou interface (e
mesmo antes de qualquer directiva de import).
• Só pode existir uma declaração package por código
fonte.
• O nome do pacote é implicitamente prefixado ao
nome de cada tipo contido no pacote.
LEEC@IST
Java – 5/54
Pacotes (3)
• Se um tipo não é declarado como pertencendo a um
pacote, é colocado num pacote sem nome (facilita a
implementação de pequenos programas).
LEEC@IST
Java – 6/54
Pacotes (4)
• A importação das classes é feita pela directiva:
import IdPacote[.IdSubPacote]*.(*|IdTipo);
• Importação a pedido (on demand):
import java.util.*;
O * importa as definições de todos os tipos públicos
do pacote.
• Importação simples: import java.util.Set;
LEEC@IST
Java – 7/54
Pacotes (5)
• A directiva de import deve ser usada depois da
declaração package, mas antes da declaração de
qualquer tipo.
• O Java importa automaticamente o pacote
java.lang (subpacote lang do pacote java).
– O separador “.” no Java corresponde ao “/” do Unix e “\” do
Windows.
LEEC@IST
Java – 8/54
Pacotes (6)
• Código escrito fora dum pacote, e que precisa de
tipos definidos nesse pacote, tem duas opções:
– Utilizar o nome qualificado do tipo.
– Importar parte ou todo o pacote.
package xpto;
public class Xpto {
java.util.Set<String> strings;
//...
package xpto;
}
import java.util.Set;
public class Xpto {
Set<String> strings;
//...
}
LEEC@IST
Java – 9/54
Pacotes (7)
•
•
As directivas de import apenas dizem ao compilador como
determinar o nome qualificado dos tipos que são usados na
aplicação e que não são encontradas localmente.
O compilador procura pelo tipo na ordem:
–
–
–
–
–
•
O tipo corrente, incluindo tipos herdados.
Tipos aninhados no tipo corrente.
Tipos importados explicitamente através de importações simples.
Outros tipos declarados no mesmo pacote.
Tipos importados implicitamente através de importações a
pedido.
Se depois de todos estes passos o tipo ainda não é encontrado,
dá um erro de compilação.
LEEC@IST
Java – 10/54
Pacotes (8)
•
Quando dois pacotes contém um tipo com o mesmo nome o
programador pode:
– Referir-se a ambos através do nome qualificado, i.e.,
pacote1.IdTipo e pacote2.IdTipo.
– Importar apenas pacote1.IdTipo, ou pacote1.*, e usar
apenas IdTipo para se referir a pacote1.IdTipo, e usar
sempre o nome qualificado para pacote2.IdTipo.
– Fazer o contrário, i.e., importar apenas pacote2.IdTipo, ou
pacote2.*, e usar apenas IdTipo para se referir a
pacote2.IdTipo, e usar sempre o nome qualificado para
pacote1.IdTipo.
– Importar tudo de ambos os pacotes pacote1.* e pacote2.*, e
usar o nome qualificado pacote1.IdTipo e pacote2.IdTipo
no código (se um tipo com o mesmo identificador existe numa
importação a pedido, não é possível usar o nome simples em
ambos os tipos).
LEEC@IST
Java – 11/54
Pacotes (9)
• Existem apenas duas opções de visibilidade para
classes e interfaces (não aninhadas) num pacote:
pacote e public.
– Uma classe ou interface pública é acessível em código fora
do pacote.
– Por omissão do qualificador de visibilidade, uma classe ou
interface é acessível apenas no código dentro do mesmo
pacote.
• Os tipos são escondidos para fora do pacote.
• Os tipos são escondidos para subpacotes.
LEEC@IST
Java – 12/54
Pacotes (10)
• Por omissão do qualificador de visibilidade, um
membro de uma classe é visível dentro do
correspondente pacote, e apenas dentro deste.
• Membros de uma classe não declarados private
num pacote são visíveis em todo o pacote.
• Todos os membros de uma interface são
implicitamente public.
LEEC@IST
Java – 13/54
Pacotes (11)
• Um método só pode ser redefinido numa subclasse
se for acessível na superclasse.
• Quando um método é chamado, em tempo de
execução o sistema tem de considerar a
acessibilidade do método para decidir qual a
implementação a usar...
LEEC@IST
Java – 14/54
Pacotes (12)
package p1;
public abstract class SuperClasseAbstracta {
private void pri() {print(“SuperClasseAbstracta.pri()”);}
void pac() {print(“SuperClasseAbstracta.pac()”);}
protected void pro() {print(“SuperClasseAbstracta.pro()”);}
public void pub() {print(“SuperClasseAbstracta.pub()”);}
public final void imprimir() {
pri();
pac();
pro();
pub();
}
}
LEEC@IST
Java – 15/54
Pacotes (13)
package p2;
import p1.SuperClasseAbstracta;
public class SubClasseConcreta1 extends SuperClasseAbstracta {
public void pri() {print(“SubClasseConcreta1.pri()”);}
public void pac() {print(“SubClasseConcreta1.pac()”);}
public void pro() {print(“SubClasseConcreta1.pro()”);}
public void pub() {print(“SubClasseConcreta1.pub()”);}
}
A chamada de
new SubClasseConcreta1().imprimir();
imprime no terminal
SuperClasseAbstracta.pri()
SuperClasseAbstracta.pac()
SubClasseConcreta1.pro()
SubClasseConcreta1.pub()
LEEC@IST
Java – 16/54
Pacotes (14)
package p1;
import p2.SubClasseConcreta1;
public class SubClasseConcreta2 extends SubClasseConcreta1 {
public void pri() {print(“SubClasseConcreta2.pri()”);}
public void pac() {print(“SubClasseConcreta2.pac()”);}
public void pro() {print(“SubClasseConcreta2.pro()”);}
public void pub() {print(“SubClasseConcreta2.pub()”);}
}
A chamada de
new SubClasseConcreta2().imprimir();
imprime no terminal
SuperClasseAbstracta.pri()
SubClasseConcreta2.pac()
SubClasseConcreta2.pro()
SubClasseConcreta2.pub()
LEEC@IST
Java – 17/54
Pacotes (15)
package p3;
import p1.SubClasseConcreta2;
public class SubClasseConcreta3 extends SubClasseConcreta2 {
public void pri() {print(“SubClasseConcreta3.pri()”);}
public void pac() {print(“SubClasseConcreta3.pac()”);}
public void pro() {print(“SubClasseConcreta3.pro()”);}
public void pub() {print(“SubClasseConcreta3.pub()”);}
}
A chamada de
new SubClasseConcreta3().imprimir();
imprime no terminal
SuperClasseAbstracta.pri()
SubClasseConcreta3.pac()
SubClasseConcreta3.pro()
SubClasseConcreta3.pub()
LEEC@IST
Java – 18/54
Excepções – revisão (1)
• Frequentemente as aplicações informáticas são
sujeitas a situações anómalas:
– Erros matemáticos (por exemplo, divisões por 0).
– Dados indicados em formato inválido (por exemplo, inteiro
inserido com caracteres inválidos).
– Tentativa de acesso a uma referência nula.
– Abertura para leitura de ficheiro inexistente.
– …
LEEC@IST
Java – 19/54
Excepções – revisão (2)
• Uma excepção é um sinal lançado ao ser identificada
uma condição que impede a execução normal do
programa.
• As excepções podem ser tratadas de formas
diversas:
– Termina o programa com mensagem de aviso e impressão
do estado (inaceitável para sistemas críticos).
– Geridas em locais específicos, designados por
manipuladores (handlers).
LEEC@IST
Java – 20/54
Tratamento de excepções (1)
• No Java, uma excepção é um objecto da classe
Exception, derivada da classe Throwable.
• A gestão de excepções é feita nos seguintes passos:
1. Delimitar código onde podem ser geradas excepções (numa
cláusula try).
2. Se for detectada situação anómala, lançar uma excepção
(numa cláusula throw).
3. Inserir manipulador para as excepções lançadas (numa
cláusula catch).
LEEC@IST
Java – 21/54
Tratamento de excepções (2)
• Formato típico dos métodos:
try {
instruções com testes tipo:
if (teste) throw new IdExcepção(String);
} catch(IdExcepção1 e) {
instruções
} catch (IdExcepção2 e) {
instruções
//... tantos catches quantos necessários
} finally {
instruções
}
LEEC@IST
Java – 22/54
Tratamento de excepções (3)
• Uma cláusula try tem de ter pelo menos uma
cláusula catch, ou finally.
• Podem estar associadas a uma cláusula try
qualquer número de cláusulas catch, incluindo zero,
desde que cada cláusula apanhe diferentes tipos de
excepções.
• O corpo do try é executado até que uma
excepção é lançada. Caso nenhuma excepção seja
lançada o corpo do try termina com sucesso.
LEEC@IST
Java – 23/54
Tratamento de excepções (4)
• Se uma excepção é lançada, cada cláusula catch
é examinada, da primeira para a última, até que
se encontre uma cujo tipo da excepção tratada
seja compatível com a excepção lançada.
– Se a cláusula catch é encontrada, é executado o seu
corpo. Mais nenhuma cláusula catch é executada.
– Se nenhuma cláusula catch é encontrada no
respectivo método, a excepção passa de método para
método na pilha de execução, até que uma cláusula
try trate a excepção lançada.
•
LEEC@IST
Caso esta cláusula nunca seja encontrada o programa
termina abruptamente.
Java – 24/54
Tratamento de excepções (5)
• Não é possível definir numa cláusula catch para
apanhar excepções duma superclasse, antes
duma cláusula catch para apanhar excepções
duma subclasse.
– A primeira cláusula catch apanharia sempre a excepção,
e a segunda cláusula catch nunca seria atingida.
– Por prevenção, existe um erro de compilação nestas
situações.
• Se o try tem uma cláusula finally, o seu
código é executado após todo o processamento
no try estar completo (independentemente de o
ter feito com sucesso, através do lançamento de uma
excepção, ou com instruções return ou break).
LEEC@IST
Java – 25/54
Tratamento de excepções (6)
• Apenas uma excepção é tratada numa cláusula
try. Se uma cláusula catch ou finally
lançarem outra excepção, as cláusulas catch da
cláusula try não são reexaminadas.
– As cláusulas catch e finally encontram-se fora da
protecção da correspondente cláusula try.
– Tais excepções são passadas de método para método na
pilha de execução e poderão, contudo, vir a ser tratadas
noutra cláusula try.
LEEC@IST
Java – 26/54
Tratamento de excepções (7)
• Tipicamente, no tratamento de excepções são
executadas as seguintes acções:
1. Registar mensagem de erro.
2. Recuperar o estado do objecto.
3. Chamar novamente o método de onde foi gerada a
excepção.
LEEC@IST
Java – 27/54
Tratamento de excepções (8)
• Se o programa gerar uma excepção, e no código
não se indicar o bloco catch para tratamento da
excepção, o JVM:
1. Aborta execução do programa.
2. Imprime no System.err a excepção gerada e a pilha de
execução.
LEEC@IST
Java – 28/54
Classes de excepções
<<abstract>>
Object
<<abstract>>
Throwable
Error
LinkageError
LEEC@IST
Exception
...
RuntimeException
...
Java – 29/54
Classe Throwable (1)
• Superclasse de todos os erros e excepções.
• Apenas objectos desta classe, ou subclasses,
podem ser lançados na instrução throw e usados
como argumentos da clásula catch .
LEEC@IST
Java – 30/54
Classe Throwable (2)
• Construtores:
Throwable()
Constrói um novo Throwable sem mensagem detalhada.
Throwable(String message)
Constrói um novo Throwable com a mensagem detalhada recebida.
Throwable(String message, Throwable cause)
Constrói um novo Throwable com a mensagem detalhada e causa
recebidas.
Throwable(Throwable cause)
Constrói um novo Throwable com a causa recebida e mensagem
detalhada (cause==null ? null : cause.toString()) (que
tipicamente contém a classe e a mensagem detalhada da causa).
LEEC@IST
Java – 31/54
Classe Throwable (3)
• Alguns métodos:
Throwable initCause(Throwable cause)
Inicializa a causa deste Throwable com a causa recebida como
parâmetro. Este método pode ser chamado apenas uma vez,
normalmente é chamado directamente no construtor, ou imediatamente
após a sua construção. Se este Throwable foi criado com
Throwable(Throwable) ou Throwable(String,Throwable) este
método nunca pode ser chamado.
Throwable getCause()
Retorna a causa deste Throwable, ou null.
String getMessage()
Retorna a messagem detalhada associada.
void printStackTrace()
Imprime no System.err a pilha de chamada dos métodos.
LEEC@IST
Java – 32/54
Classe Error
• Usada para lançar excepções por falha do JVM.
• Normalmente não são tratadas pelo programador.
LEEC@IST
Java – 33/54
Classe Exception (1)
• J2SE dispõe 55 subclasses:
– ClassNotFoundException
– IOException (contém 21 subclasses)
– ...
• Excepções do programador são subclasses de
Exception.
LEEC@IST
Java – 34/54
Classe Exception (2)
• Construtores:
Exception()
Constrói uma nova Exception sem mensagem detalhada.
Exception(String message)
Constrói uma nova Exception com a mensagem detalhada recebida.
Exception(String message, Throwable cause)
Constrói uma nova Exception com a mensagem detalhada e causa
recebidas.
Exception(Throwable cause)
Constrói uma nova Exception com a causa recebida e mensagem
detalhada (cause==null ? null : cause.toString()) (que
tipicamente contém a classe e a mensagem detalhada da causa).
• A classe Exception não introduz novos métodos.
LEEC@IST
Java – 35/54
Exemplo (1)
• Exemplo de excepção do utilizador (divisão por zero):
public class ExDivisãoPorZero extends Exception {
public ExDivisãoPorZero() {
super(“Divisão por zero”);
}
public ExDivisãoPorZero(String message) {
super(message);
}
}
LEEC@IST
Java – 36/54
Exemplo (2)
public class Divide {
protected int op1;
public Divide() { op1=20; }
public int divide(int op2) {
try {
if (op2==0) throw new ExDivisãoPorZero();
return op1/op2;
} catch (ExDivisãoPorZero e) {
System.out.println(e);
return –1;
}
}
}
LEEC@IST
Java – 37/54
Exemplo (3)
public static void main (String args[]){
if (args.length!=1) {
System.out.println(“Um numero apenas!”);
System.out.exit(0);
}
Divide d = new Divide();
int result = d.divide(Integer.parseInt(args[0],10));
if (result==-1) System.exit(1);
else {
System.out.println(d.val()+“/”+args[0]+”=“+result);
System.exit(0);
}
}
LEEC@IST
Java – 38/54
Transferência de excepções (1)
• Frequentemente é de interesse a excepção ser
tratada pelo objecto que chama o método
(normalmente a recuperação depende do objecto
que o chama).
• O método, onde a excepção pode ser gerada, deve
indicar no cabeçalho
throws IdExcepção
• No exemplo da divisão por zero, o método divide
seria alterado para:
public int divide(int op2) throws ExDivisãoPorZero {
if (op2==0) throw new ExDivisãoPorZero();
return op1/op2;
}
LEEC@IST
Java – 39/54
Transferência de excepções (2)
public static void main(String args[]) {
if (args.length!=1) {
System.out.println(“Um numero apenas!”);
System.out.exit(0);
}
int result;
Divide d = new Divide();
try {
result = d.divide(Integer.parseInt(args[0],10));
System.out.println(d.op1()+”/”+args[0]+”=“+result);
System.exit(0);
} catch (ExDivisãoPorZero e) {
System.out.println(e);
System.exit(1);
}
}
LEEC@IST
Java – 40/54
Outro exemplo (1)
public void lerFicheiroDados(String nomeFicheiro)
throws FicheiroDadosInválido
{
String ficheiro = nomeFicheiro + “.fasta”;
FileInputStream in = null;
try {
in = new FileInputStream(ficheiro);
parsarFicheiroDados(in);
} catch (IOException e) {
throw new FicheiroDadosInválido();
} finally {
try {
if (in!=null) in.close();
} catch (IOException e) {
//ignorar: ou lemos os dados correctamente
//
ou lançamos FicheiroDadosInválido
}
}
}
LEEC@IST
Java – 41/54
Outro exemplo (2)
•
•
No exemplo anterior tanto o construtor FileInputStream,
como o método parsarFicheiroDados podem lançar
excepções do tipo IOException.
Quando é criada a excepção FicheiroDadosInválido é
perdida informação da excepção que foi lançada.
– Criar novas excepções em substituição de outras é um
mecanismos importante para subir o nível de abstracção.
– Mas como fazê-lo sem perder informação que pode ser
necessária para se proceder à recuperação da excepção?
•
Há duas soluções:
1. Usar o método initCause da classe Throwable.
2. Definir construtores da nova excepção que aceite um
Throwable.
LEEC@IST
Java – 42/54
Outro exemplo (3)
} catch (IOException e) {
FicheiroDadosInválido fdi = new FicheiroDadosInválido();
fdi.initCause(e);
throw fdi;
}
} catch (IOException e) {
throw (FicheiroDadosInválido)
new FicheiroDadosInválido().initCause(e);
}
LEEC@IST
Java – 43/54
Outro exemplo (4)
public class FicheiroDadosInválido extends Exception {
public FicheiroDadosInválido() {}
public FicheiroDadosInválido(String msg) {
super(msg);
}
public FicheiroDadosInválido(Throwable causa) {
super(causa);
}
public FicheiroDadosInválido(String msg, Throwable causa) {
super(msg, causa);
}
}
} catch (IOException e) {
throw new FicheiroDadosInválido(e);
}
LEEC@IST
Java – 44/54
Asserções – definição
• Uma asserção serve para verificar um invariante.
• Um invariante é uma condição que deve ser sempre
verdadeira.
LEEC@IST
Java – 45/54
Asserções (1)
Sintaxe:
assert expr1 [: expr2]
• expr1 é uma expressão boolean ou Boolean.
– Quando é encontrado um assert no código:
• A expr1 é avaliada:
– Se expr1 for true, a asserção passa.
– Se expr1 for false, a asserção falha, e um
AssertionError é construído e lançado.
LEEC@IST
Java – 46/54
Asserções (2)
• expr2 é uma expressão opcional que será passada
ao construtor do AssertionError para descrever o
problema encontrado.
– Se expr2 for um Throwable será o valor retornado pelo
método getCause do correspondente AssertionError.
– Caso contrário, expr2 é convertida numa String e será a
mensagem de detalhe do correspondente
AssertionError.
LEEC@IST
Java – 47/54
Asserções (3)
• Não devem ser usadas asserções para testar maus
funcionamentos que às vezes acontecem
(IOException, NullPointerException, ...).
• As asserções devem ser usadas apenas para
testar condições que nunca devem falhar num
programa correcto. Por exemplo:
– Testar se o estado corrente do objecto é correcto.
– Verificar o fluxo do código.
LEEC@IST
Java – 48/54
Asserções (4)
•
Testar se o estado corrente do objecto é correcto: ao remover um
objecto de um conjunto de objectos, se o objecto foi encontrado e
removido é suposto o conjunto ter menos 1 elemento.
public boolean remover(Object objecto) {
assert numElementos >= 0;
if (objecto==null)
throw NullPointerException(“remover: objecto null”);
int aux = numElementos;
boolean objEncontrado = false;
try {
//remover objecto do conjunto (se existir)
return objEncontrado;
} finally {
assert ((objEncontrado==false && numElementos==aux) ||
(objEncontrado==true && numElementos==aux-1);
}
}
LEEC@IST
Java – 49/54
Asserções (5)
•
Verificar o fluxo do código: neste caso, quer-se proceder à
substituição do valor antigo da tabela valores pelo valor
novo, assegurando-nos que o valor antigo está sempre
presente na tabela valores.
private void substituir(int antigo, int novo) {
for (int i=0; i<valores.length; i++) {
if (valores [i ] == antigo) {
valores [i ] = novo;
return;
}
}
assert false : “substituir: valor ”+antigo+”não encontrado”;
}
LEEC@IST
Java – 50/54
Asserções (6)
• Por omissão, as asserções não são avaliadas.
– É possível ligar e desligar as asserções (para pacotes e
classes).
– Quando as asserções estão desligadas não são avaliadas,
por isso as expressões das asserções não devem produzir
efeitos colaterias.
assert ++i < max;
LEEC@IST
i++;
assert i < max;
Java – 51/54
Asserções (7)
• Para ligar e desligar as asserções:
– Correr a aplicação na linha de comando com as seguintes
opções
(no NetBeans Properties do projecto > Run > VM Options):
• -ea[:IdPacote ][.IdSubPacotes ]*[.IdClasse ]
Liga as asserções para os pacotes/classes dadas. Se não
forem dados pacotes/classes as asserções são ligadas para
todas as classes (excepto classes de sistema).
• -da[:IdPacote ][.IdSubPacotes ]*[.IdClasse ]
Desliga as asserções para os pacotes/classes dadas. Se não
forem dados pacotes/classes as asserções são desligadas
para todas as classes.
– No caso dos pacotes pode usar-se IdPacote... para aplicar a
mesma opção a todos os subpacotes.
LEEC@IST
Java – 52/54
Asserções (8)
• Remover por completo as asserções:
private static final asserçõesLigadas = false;
if (asserçõesLigadas)
assert false : “asserções não estão desligadas”;
LEEC@IST
Java – 53/54
Asserções (9)
• Tornando as asserções obrigatórias (uso propositado
de efeitos colaterais na asserção):
static {
boolean asserçõesLigadas = false;
assert asserçõesLigadas = true;
if (!asserçõesLigadas)
throw new IllegalStateException(“Asserções necessárias”);
}
LEEC@IST
Java – 54/54
Download