apontamentos sobre java 5 · apontamentos sobre java 5.0 luis mendes 31-07-2007 31-07-2007 1/96....

96
Apontamentos sobre Java 5.0 Luís Mendes Apontamentos sobre Java 5.0 Luis Mendes 31-07-2007 31-07-2007 1/96

Upload: ledat

Post on 31-Aug-2018

220 views

Category:

Documents


0 download

TRANSCRIPT

Apontamentos sobre Java 5.0 Luís Mendes

Apontamentos sobre Java 5.0

Luis Mendes

31-07-2007

31-07-2007 1/96

Apontamentos sobre Java 5.0 Luís Mendes

ConteúdoApontamentos sobre Java 5.0........................................................................................1Introdução....................................................................................................................4Capitulo 1 – Conceitos fundamentais..............................................................................5

Estrutura dos ficheiro com o código fonte...................................................................5Identificadores..........................................................................................................5Tipos da linguagem ..................................................................................................6Literais.....................................................................................................................6Importing (definição do namespace)..........................................................................9ClassPath................................................................................................................10As variáveis em Java e a sua inicialização.................................................................11Copia dos argumentos dos métodos.........................................................................12Gargage Collector....................................................................................................12Declaração de variáveis...........................................................................................13

Capítulo 2 – Operadores e atribuições..........................................................................14Operadores da linguagem Java................................................................................14Operadores unários.................................................................................................15Operadores aritméticos............................................................................................15Operadores de comparação.....................................................................................17

Capítulo 3 – Modificadores...........................................................................................20Modificadores de acesso..........................................................................................20Modificadores de acesso e a re-escrita de métodos...................................................26Modificador final......................................................................................................26Modificador Abstract................................................................................................27Modificador Static....................................................................................................27Modificador Native...................................................................................................28Modificador Transient..............................................................................................28Modificador Volatile.................................................................................................28Modificador Synchronized........................................................................................28

Capitulo 4- Conversões e casting..................................................................................29Conversões entre tipos primitivos.........................................................................29Conversões nas operações aritméticas..................................................................29Casting em tipos primitivos..................................................................................30Conversões entre referências...............................................................................30Casting entre referências.....................................................................................30

Capítulo 5 – Controlo do fluxo de execução, Asserções e excepções...............................32Controlo do fluxo de execução.................................................................................32Excepções ..............................................................................................................33Asserções...............................................................................................................39

Capítulo 6 – Objectos e classes....................................................................................41Classes...................................................................................................................41

Construtores.......................................................................................................41Métodos.............................................................................................................44

31-07-2007 2/96

Apontamentos sobre Java 5.0 Luís Mendes

Classes aninhadas...............................................................................................49Classes de métodos.............................................................................................52Classes anónimas................................................................................................53Enums................................................................................................................54

Capítulo 7 – Threads...................................................................................................57O que é?.................................................................................................................57Criação de uma thread............................................................................................57Ciclo de vida de uma thread.....................................................................................57Métodos mais importantes da Classe Thread ............................................................59Os objectos são monitores.......................................................................................61

Sincronização entre threads ................................................................................63Padrões de uso do Wait() e notify()......................................................................63Deadloacks.........................................................................................................65Observações.......................................................................................................65

Capítulo 8 – Package Java.lang e java.util.....................................................................67Object....................................................................................................................67Math......................................................................................................................69String, StringBuffer, StringBuilder.............................................................................69As classes Wrapper.................................................................................................72Autoboxing e autounboxing.....................................................................................75Colecções (Java Collection Framework).....................................................................76

Collection............................................................................................................77List ....................................................................................................................78Set ....................................................................................................................81Map....................................................................................................................83

Processamento de texto..........................................................................................86Formatação de texto............................................................................................88Formatação de texto para datas...........................................................................89Formatação de números .....................................................................................91

Capitulo 9 – I/O e Streams..........................................................................................92A Classe File...........................................................................................................92A classe RandomAccessFile......................................................................................92Streams, Readers e Writers......................................................................................93

Streams..............................................................................................................93Readers e writers................................................................................................94Serialização de objectos.......................................................................................95

31-07-2007 3/96

Apontamentos sobre Java 5.0 Luís Mendes

Introdução

Estes apontamentos são o resultado da preparação para o exame Sun Certified Java Programmer (SCJP) Java Release 5.0 que teve lugar no final do mês de Julho de 2007.

A preparação para o exame teve como base a leitura do livro “Complete Java 2 Certification Study Guide Fifth Edition” de Phil Heller e Simon Roberts, Sybex e as experiências feitas com o Eclipse para entender do ponto de vista prático as funcionalidades da linguagem Java versão 5.0.

Nos próximos capítulos são apresentados as funcionalidades mais importantes da linguagem Java, sem contudo, pretender faze-lo de uma forma exaustiva e acompanhando sempre que possível com exemplos de código de modo a concretizar melhor os conceitos expostos.

31-07-2007 4/96

Apontamentos sobre Java 5.0 Luís Mendes

Capitulo 1 – Conceitos fundamentais

Estrutura dos ficheiro com o código fonte

O ficheiro com o código fonte tem de ter a extensão .java

O nome do ficheiro com o código fonte tem de ter o mesmo nome que a classe pública do ficheiro. Um ficheiro pode conter qualquer número de classes não-públicas.

Um ficheiro código fonte têm a seguinte estrutura

package collecções.list; ==> nome separado por pontos que corresponde ao caminho de directorias onde o ficheiro com o código fonte está presente, neste caso será collecções\list (é opcional se a classe não estiver em nenhuma package)

import java.util.Iterator; ==> indica as classes usadas na definição desta import java.util.LinkedList; classe. Pode-se importar todas as classes de import java.util.List; uma package usando * em vez no nome da classe.import java.util.ListIterator; (é opcional se forem usados nomes completos

nas classes) a package java.lang.* é importada automaticamente

public class Lista { // definição das classe, enums ou interfaces

}

Identificadores

Lista de palavras reservadas da linguagem Java é,

abstract class extends implements null strictfp trueassert const false import package super tryboolean continue final instanceof private switch voidbreak default finally int protected syncronized volatilebyte do float interface public this whilecase double for long return throw whilecatch else goto native short throwschar enum if new static transient

Os identificadores, nomes das variáveis, classes, métodos ou labels podem começar por: ● uma letra● $● _

31-07-2007 5/96

Apontamentos sobre Java 5.0 Luís Mendes

São case-sensitive, não podem conter espaço e não podem pertencer à lista das palavras reservadas.

Tipos da linguagem

A linguagem Java disponibiliza duas categorias de tipos:● Tipos primitivos: tipos que guardam um único valor e que não são objectos.● Referências para objectos: “apontadores” para objectos descendente da classe

Object.

Os tipos primitivos existentes são definidos independentemente da arquitectura do PC que executa os programas e estão definidos como,

Tipo dimensão Valor mínimo valor máximo

boolean não é conhecida true false

char 16 bits (unicode) 0 216-1 = 65,535

byte 8 bits - 27 =-128 +27-1 =127

short 16 bits - 215 = -32,768 +215-1 =32,767

int 32 bits - 231 = -2,147,483,648 +231-1 = 2,147,483,647

long 64 bits - 263 = -9,223,372,036,854,775,808

+263-1 = 9,223,372,036,854,775,807

float 32 bits +/-1.402398462-45 +/-3.4028234738

double 64 bits +/-4.94065-324 +/-1.797693308

Os tipos double e float podem também ter os seguintes valores:● Float.NaN (not a number)● Float.NEGATIVE_INFINITY● Float.POSITIVE_INFINITY● Double.NaN (not a number)● Double.NEGATIVE_INFINITY● Double.POSITIVE_INFINITY

Literais

Literais são valores constantes que o programador usa por exemplo para inicializar uma variável.

31-07-2007 6/96

Apontamentos sobre Java 5.0 Luís Mendes

Tipo de expressão forma de escrita dos valores exemplos

boolean Só pode ser: true ou false true

char entre plicas e não pode ser mais do que um carácter, o valor hexadecimal de um carácter unicode ou um carácter pré-definido.

'w', '\u4567' (código unicode), '\n', '\r', '\''

short, int, byte números decimais em notação decimal, octal – começa com o dígito 0 - ou hexadecimal – números com o prefixo 0x

28 (decimal), 034 (octal), 0x1c (hexa)

long o mesmo que para os tipos short, int, byte mas tem de terminar com o sufixo l/L

28L, 034l, 0x1cL

float número com um ponto decimal (.) e com o expoente indicado por e ou E e finalizado com a letra f/F.

789.9F

double o mesmo que para o tipo float só que deve terminar com o d ou D. Um número decimal sem o sufixo f/F ou d/D é considerado como sendo um double.

12.23E3d

String caracteres entre aspas “ola mundo”

Exemplo,

boolean b1=true;boolean b2=false;//boolean b3=1; // não é permitido

char c1 = 'y';char c2 = '\u0222'; // unicodechar c3 = '\'';char c4 = 678; // código decimal do caracter unicodebyte by1 = 34;byte by2 = 'r'; // código decimal do caracter rshort s = 67;int i1 = 233; // decimalint i2 = 0x67F; // hexalong l1 = 2121;long l2 = 07l; // octallong l3 = 0xa1Dl; // hexa

31-07-2007 7/96

Apontamentos sobre Java 5.0 Luís Mendes

//float f1 = 1.56; Erro: Falta o f por isso 1.56 é considerado um doublefloat f2 = 122.8E+2f;float f3 = 122.8e+2F;double d1 = 122.8e+2F;double d2 = 11222.8E+2d;double d3 = 11222.8e+2D;double d4 = 1.2;String s1 = "ola mundo";

Arrays

É um objecto especial que guarda um conjunto de variáveis primitivas do mesmo tipo ou um conjunto de referência para o mesmo tipo de objecto. Os array são homogéneos porque todas as suas entradas só guardam informação de um único tipo. A informação guardada é acedida através de um índice que começa em zero e que progrede até ao último elemento.

A declaração da variável que irá representar o array é feita recorrendo aos caracteres []. A variável deve ser depois instanciada recorrendo ao operador new (sem invocar nenhum construtor) e indicando a dimensão do array. A dimensão do array não pode ser alterada a partir do momento que é criado podendo ser ter um valor positivo ou igual a zero. Quando o array é criado os seus elementos são automaticamente inicializados para 0, null ou false.

int[] temperaturas = new int[5];int tam = 4;short tamanho[] = new short[tam];

É possível instanciar e inicializar um array num mesmo passo através da construção,

// é possível declarar um array constanteint[] idades = {23, 56, tam};// é possível declarar um array e na chamada de um métodoprintArray(new String {23, 56, tam});

Um array tem a propriedade length que devolve a sua dimensão.

Os arrays multidimensional em Java não ocupam uma matriz na zona de memória como é feito pela linguagem C/C++. Cada uma das suas dimensões aponta para outro objecto do tipo Array. Assim, é possível substituir dimensões inteiras de um array multidimensional depois da sua criação.

31-07-2007 8/96

Apontamentos sobre Java 5.0 Luís Mendes

// declaração e instanciaçãoint[] temperaturas = new int[5];int tam = 4;short tamanho[] = new short[tam];// é possível declarar um array constanteint[] idades = {23, 56, tam};// temperaturas = {23, 45}; esta declaração não funciona, só funciona no // momento da declaração

// percorrer todo os elementos do arrayfor (int i =0; i< tamanho.length; i++)

System.out.println(tamanho[i]);

System.out.println(tamanho); // toString devolve o id interno do array e não // os elementos do array S@3e25a5

// java 5: nova forma de iterar todos os índices do arrayfor (int id: idades)

System.out.println(id);

// array multi-dimensionais, são arrays uni-dimensionais a apontar para outros // arrays uni-dimensionais int[][] ids = new int[3][4];ids[0][0] = 2; ids[0][1] = 3; ids[0][2] = 4; ids[0][3] = 5;ids[1][0] = 2; ids[1][1] = 3; ids[1][2] = 4; ids[1][3] = 5;ids[2][0] = 2; ids[2][1] = 3; ids[2][2] = 4; ids[2][3] = 5;

// substituição integral da linha 1 do array multi-dimensionalint[] newId = {23, 34, 34, 43};ids[1] = newId;

Importing (definição do namespace)

O nome completo de cada classe (interface ou enum) é dado pelo sua package mais o nome próprio. Sempre que uma classe necessite de usar outra esta deve ser referenciada pelo seu nome completo. Por exemplo,

...java.lang.Integer i;...

Para evitar o excesso de detalhe que seria necessário escrever sempre que uma classe necessitasse ser usada. As classes podem ser usadas por outras usando apenas o seu nome na altura da invocação desde que o seu caminho completo esteja na zona de importação da classe invocante.

31-07-2007 9/96

Apontamentos sobre Java 5.0 Luís Mendes

import java.lang.Integer;...Integer i; // ==> o compilador sabe após consultar a zona de importação que // Integer é a classe java.lang.Integer ...

Na zona de importação pode-se indicar que todas as classes de uma determinada package podem eventualmente ser usadas pela classe invocante.

import java.lang.*;

A partir da versão 5 também é possível usar campos ou métodos estáticos públicos de um classe sem a necessidade de qualificar os elementos estáticos com o nome da classe. Para tal os elementos estáticos são importado usando a instrução import static.

import static java.awt.Color.Green; // importa o campo estáticoimport static pkga.pkgb.Aclassname.*; // importa todos os elementos estáticos // que podem ser importadosimport static measure.Scales.poundsToMicrogramas(); // importa o método // estático...myColor = GREEN; // em vez de Color.GREEN;poundsToMicrogramas(212); // em vez de Scales.poundsToMicrogramas(212);...

Quando o compilador encontra o uso de uma classe ele tem de obter a sua forma compilada. A sua procura é feita do seguinte modo:

1. procura a classe dentro da package da classe invocadora;2. procura nas packages indicadas através dos comandos import ou import static

presentes na classe invocadora.

O mecanismo de importação só tem custos no momento da compilação.

ClassPath

Durante a compilação os ficheiros com o código fonte de cada classe (com extensão .java) e os ficheiros das classes já compiladas (com extensão .class) são procuradas nas directorias indicadas:

● pela variável de ambiente CLASSPATH;● pelos valores do parâmetro -classpath (tem o sinonimo -cp) do compilador

(javac.exe)

No momento da execução os ficheiros com as classes compiladas são também encontradas da mesma forma que na compilação.

31-07-2007 10/96

Apontamentos sobre Java 5.0 Luís Mendes

Quando as classes compiladas estão em jars, o caminho absoluto para cada um dos jars deve estar no classpath e não apenas a directoria que contém os jars.

As variáveis em Java e a sua inicialização

As variáveis em Java podem ter três tipos de ciclos de vida dependendo do local onde são declaradas:

● Variável membro (member): (instância) é um campo não estático definida numa classe. É criada quando o objecto é criado e destruída em conjunto com o objecto. Para ser acedida é necessário usar a referência do objecto que a contém (depende da sua visibilidade). No momento da declaração não necessita de ser inicializada porque, se não o for a variável é automaticamente inicializada.

● Variável de classe (variável estática): (classe) é um campo estático de uma classe. É criada quando a classe é carregada para memória e destruída quando a classe é descarregada da memória. Esta variável existe mesmo que a classe nunca seja instanciada e é partilhada por todas as instancias da classe. No momento da declaração não necessita de ser inicializada porque, se não o for, a variável é automaticamente inicializada.

● Variável automática: (método) são variáveis declaradas no corpo dos métodos, criadas quando o método inicia a sua execução e destruidas quando o método termina a última instrução. Não são inicializada automaticamente.

As variáveis automáticas têm de ser inicializadas pelo programador antes de serem usadas pela primeira vez. Isto significa que elas podem ser inicializadas ou no momento da declaração ou num momento posterior mas antes da sua primeira utilização.

{int i=1; // declaração e inicializaçãoint g;g = 6; // inicialização depois da declaraçãoi++; // i tem de ser inicializada antes desta linhareturn g; // g tem de ser inicializada antes desta linha

}

As variáveis do tipo membro e do tipo classe são inicializadas automaticamente dependendo do seu tipo de dados:

Tipo de dados Valor inicial

byte 0

int 0

float 0.0f

31-07-2007 11/96

Apontamentos sobre Java 5.0 Luís Mendes

char '\u0000'

Referência para objectos null

short 0

long 0

double 0.0d

Boolean false

Copia dos argumentos dos métodos

Os argumentos dos métodos são sempre passados para o corpo de execução como uma cópia dos valores usados durante a chamada e só há argumentos de entrada. Este regra é válida para argumentos do tipo primitivos ou do tipo referência para objectos.

A forma de um método poder influenciar directamente as variáveis definidas fora do seu corpo de instruções é através do seu retorno (return) ou através da atribuição de valores a variáveis do tipo membro ou de classe dentro do seu corpo.

public static int calc(int i, Short s){

i = 10;short s1 = 5;s = new Short(s1);return 344;

}

public static void main(String[] args) {int i = 2;Short s = new Short("6");System.out.println(i + " " + s+ " "); // ==> 2 6 int res = calc(i, s);System.out.println(i + " " + s+ " " + res); // ==> 2 6 344 i e s de

main() NÃO FORAM ALTERADAS

}

Gargage CollectorOs objectos são criados usando a instrução new e são colocados na zona de memória chamada heap. As variáveis automáticas (dos métodos) são criadas e destruídas na stack do seu método. Não há nenhum comando para apagar os objectos da HEAP. A máquina virtual JAVA disponibiliza um mecanismo assíncrono que apaga da HEAP os objectos que não são usados pelo programa, isto é, os objectos que deixaram de ser

31-07-2007 12/96

Apontamentos sobre Java 5.0 Luís Mendes

referenciados por outros objectos. Ao mecanismo assíncrono dá-se o nome de Garbage Collector.

Antes de um objecto ser eliminado é chamado o método finalize() caso esteja definido pela classe do objecto.

A actuação do garbage collector é imprevisível sendo implementada por uma thread daemon. Pode-se tentar pedir à máquina virtual JAVA que execute o garbage collector programaticamente para limpar a memória através dos métodos as funções System.gc() ou Runtime.gc() mas não é garantido que ele seja actuado na altura em que são executados os métodos. Este métodos têm um comportamento não determinístico.

Declaração de variáveisNa altura de declarar variáveis é possível declarar mais do que uma, desde que sejam do mesmo tipo e se empregue a virgula para separar os nomes das variáveis.

int a=5, r, u;

31-07-2007 13/96

Apontamentos sobre Java 5.0 Luís Mendes

Capítulo 2 – Operadores e atribuições

Operadores da linguagem Java

Precedência na execução

Categoria Operadores Observações

1º unários ++ -- + - ! ~ (type) ! = nega o valor booleano;~ =nega os bits um a um;(type) = operador de cast.

2º aritméticos * / % + - * / e % são realizados primeiro que a + e -.% = resto da divisão inteira ou decimal. È negativo se o operador da esquerda também o for.

3º Shift << >> >>>

4º Comparação < <= > >= instanceof == !=

5º bitwise & | ^ & = And de todos os bits;| = Or de todos os bits;^ = xor de todos os bits

6º short-circuit && || && = se o operador do lado esquerdo for falso o do lado direito não é avaliado e o resultado é verdadeiro;|| = se o operador do lado esquerdo for verdadeiro o operador do lado direito não é avaliado e o resultado é verdadeiro.

7º condicionar ?: Equivale a um if then else que devolve o valor da expressão do then ou da expressão do else de acordo com o valor da expressão do if.Os tipos das expressões do then e do else têm de ser do mesmo tipo.

8º Atribuição =, <op>= Onde, x <op>= y equivale ao desdobramento de x = x <op> y.

<op> pode ser: +, -, *, %, /.

31-07-2007 14/96

Apontamentos sobre Java 5.0 Luís Mendes

É feito um cast automático para o tipo x de

Operadores unários

A seguir lista-se alguns exemplos de como usar os operadores unários,

int a=5, r, u;// pre e post incremento System.out.println(++a); // ==> 6System.out.println(a); // ==> 6

System.out.println(a++); // ==> 6System.out.println(a); // ==> 7

// valores positivos e negativos r = -a; System.out.println(r); //==> -7r = r - +a; System.out.println(r); // ==> -14

// negação dos bits individuais. o resultado tem de ser inteirobyte b = 5;int l = ~b; // o resultado deste operador tem de ser no mínimo int.System.out.println(b + " " + l); // ==> 5 -6

// negação booleana. O resultado é booleano e tipo do operador tem de ser booleanoboolean res= !true; System.out.println(res); //==> falseres = !(b==l); // os parêntese são obrigatórios porque é o resultado de == que deve ser negadoSystem.out.println(res); //==> true

// Cast de tiposdouble d = 6.0;int k = (int)d + 7; System.out.println(k); //==> 13

Operadores aritméticos

O tipo do resultado dos operadores aritméticos é igual ao argumento definido pelo o tipo com maior dimensão. Sendo que no mínimo o resultado é do tipo int, mesmo que a expressão só envolva tipos short, char ou byte. O programador pode alterar esta regra e assumir o risco de um possível overflow ao fazer um cast do resultado da operação para

31-07-2007 15/96

Apontamentos sobre Java 5.0 Luís Mendes

um tipo mais pequeno.

Os operadores de divisão e módulo quando divididos por 0 lançam a excepção AritmeticException.

5/0 ==> ArithmeticException, 5%0 ==> ArithmeticException

Na divisão e multiplicação com número inteiros existe o risco de:

● Na multiplicação o resultado ser maior que o universo de valores do tipo da variável que irá guardar o resultado ==> A esta situação chama-se overflow.

● Na divisão o resultado pode perder a parte decimal => A esta situação chama-se underflow.

Como regra no uso dos operadores de divisão e multiplicação com inteiros deve-se multiplicar primeiro e dividir depois.

O Operador + faz a soma aritmética de dois valores se eles forem de tipos numéricos. O resultado da operação é:

● Um tipo numérico;● No mínimo do tipo int ou então igual ao maior dos tipos associados aos valores; ● Os operadores são primeiro convertidos para o tipo do resultado antes da soma ser

realizada.

Se o operador + tiver como valor pelo menos uma string então o resultado é uma string que resulta da concatenação da representação textual dos dois valores. Se o valor que não é String for de um tipo primitivo então a sua representação textual corresponde ao seu usado o seu valor. Se for uma referência para um objecto então a representação textual é obtida através da invocação do método toString();

As operações sobre números decimais, do tipo float ou double, podem resultar no valor “Not a number”, Float.NaN ou Double.NaN, significando que o resultado do operador não é número.

O valor “not a number” não pode ser comparado usando os operadores == (devolve sempre false) ou !=. Têm de ser testados através dos métodos: Float.isNaN(float) ou Double.isNaN(double);

Exemplos de cbyte s = 6;byte b = (byte) (s / 2); //==> o resultado final da operação é no mínimo int, // por isso o castSystem.out.println(b); // ==> 3

31-07-2007 16/96

Apontamentos sobre Java 5.0 Luís Mendes

int i= 7 / 3;System.out.println(i); // ==> 2

i= 7 % 3; // resto da divisãoSystem.out.println(i); // ==> 1

i = (int)(7.9 / 2); // tem de converter para int porque o resultado da divisão é 7.9 System.out.println(i); // ==> 3

// i= 7 / 0; ==> AritmeticException

i = (int)(2 + 3 + 3.9);System.out.println(i); // ==> 3

// concatenação de stringsString s1 = 2 + 3 + 3.9 + "oi" + 4.9 + 3.9; // o resultado é uma string!! A avaliação é da esquerda para a direitaSystem.out.println(s1); // ==> "8.9oi4.93.9"// s1 = 2 + 3 + 3.9; não é possível porque não há nenhum atributo que seja strings1 = "oi" + 2 + 3 + 3.9; // como oi é uma string os valores numéricos são // convertido para o número System.out.println(s1); // ==> "oi233.9"s1 = 2 - 3 + 3.9 + "oi" + 4.9 + 3.9; // o resultado é uma string!! A avaliação // é da esquerda para a direitaSystem.out.println(s1); // ==> "2.9oi4.93.9"// s1 = 2 - 3 + 3.9 + "oi" + 4.9 - 3.9; // não é possível porque a partir de // "oi" só há concatenação de strings // e por isso a subtração não pode ser // feita.

Operadores de comparação

Os operadores >, <=, >=, < devolvem um valor booleano e aceitam argumentos do tipo numérico ou char.

Os operadores == e != devolvem um valor booleano e aceitam argumentos do tipo numérico, char, booleano ou referência de objectos. Eles comparam os valores dos tipos primitivos e os valores das referências, não comparam se o conteúdo de dois objectos são iguais. Para isso há o método Object.equals().

O operador instanceof devolve um valor booleano e deve ser usado da seguinte forma: <nome da variável com a referência para um objecto> instanceof <nome de uma classe/interface/array/enum/array>. O operador é verdadeiro se o objecto referenciado for do tipo indicado após análise da hierarquia de classes e não se o tipo da referência do objecto é igual à classe indicada. No caso de a variável indicada ao operador ser do tipo array o operador devolve verdadeiro se as seguintes duas condições forem satisfeitas:

1. O nome da classe indicada é um array

31-07-2007 17/96

Apontamentos sobre Java 5.0 Luís Mendes

2. O array é da classe indicada de acordo com a hierarquia de classes.

Alguns exemplos,

Integer i1=null, i2=null;boolean b;b = '\u0002' > '\u2892'; // comparação entre char// não é possível comparar b = true > false;// não é possível comparar b = i1 <= i2; ==> i1 e i2 são referênciasb = 4.8 > 6; // comparação de numéricos

b = 62+2 > 23; // 62+2 têm precedência relativamente a 2System.out.println(b); // ==> true

b = true == true;b = true != false;b = i1 == i2;b = 62+2 != 23; // 62+2 têm precedência relativamente a 2System.out.println(b); // ==> true

// instanceof. // Verifica se o objecto que a referência // aponta é de um determinado tipo. // o tipo da variável não é tido em conta pelo operador.b = i1 instanceof Integer; //==> null instanceof IntegerSystem.out.println(b); // ==> false

i1 = new Integer(3);b = i1 instanceof Integer; //==> é do tipo integerSystem.out.println(b); // ==> trueb = i1 instanceof Number; //==> é do tipo numberSystem.out.println(b); // ==> trueb = i1 instanceof Object; //==> é do tipo ObjectSystem.out.println(b); // ==> true

// mudança do tipo da variável mas não do objecto, que é um Integer.Object o = i1;b = o instanceof Integer; //==> é do tipo integerSystem.out.println(b); // ==> trueb = o instanceof Number; //==> é do tipo numberSystem.out.println(b); // ==> trueb = o instanceof Object; //==> é do tipo ObjectSystem.out.println(b); // ==> true

// arraysInteger[] array = {2, 5};b = array instanceof Integer[];System.out.println(b); // ==> trueb = array instanceof Number[];System.out.println(b); // ==> trueb = array instanceof Object[];System.out.println(b); // ==> true// b = i1 instanceof Integer[]; ==> i1 não é um array

31-07-2007 18/96

Apontamentos sobre Java 5.0 Luís Mendes

Todos os objectos podem ser inquiridos para determinar se são um array através do getClass().isArray().

Não convém usar o operador >>> com variáveis do tipo byte ou short devido às conversões implícitas feitas pela linguagem.

31-07-2007 19/96

Apontamentos sobre Java 5.0 Luís Mendes

Capítulo 3 – Modificadores

Os elementos da linguagem java, como classes, campos, métodos ou variáveis, podem ser qualificados com alguns comandos na sua instrução de definição permitindo desta forma instruir o compilador a alterar o seu comportamento habitual. Aos comandos e aos seus comportamentos associados dá-se o nome de modificadores.

Os modificadores podem ser aplicados na definição dos seguintes elementos da linguagem:

● classes

● classes aninhadas

● interfaces

● enums

● campos

● métodos/construtores

● variáveis/argumentos dos métodos

● pedaços de código soltos

É possível empregar mais do que um modificador nas definições dos elementos anteriores.

Modificadores de acesso

Os modificador mais importantes são os modificadores de acesso. Eles indicam em que condições os elementos podem ser usados por outros. Cada elemento só pode ser declarado com um e só um modificador de acesso.

Modificador de acesso: Public

Aplica-se a:

● classes/interfaces/enums/classes aninhadas: indica que a classe/interface/enums pode ser instanciada por qualquer outra, independentemente das packages das classes.

● Campos: indica que o campo pode ser usado directamente por outras classes ou por subclasses da sua classe.

31-07-2007 20/96

Apontamentos sobre Java 5.0 Luís Mendes

● métodos/construtores: indica que o método pode ser usado directamente por outras classes ou por subclasses da sua classe.

package classespublicas;public class ClassePublica {

public int i = 0;public void metodoprivado(){ }

}

package classespublicas;public class SubClassePublica extends ClassePublica {

public void fazer(){

i++; // ok i é publicometodoprivado(); // ok metodoprivado é publico

}}

package classespublicas;public class ClassePublicaUso {

public void fazer(){

ClassePublica cp = new ClassePublica();SubClassePublica cpsub = new SubClassePublica();cp.i++; // i é publicocp.metodoprivado(); // metodoprivado é publico cpsub.i++; // i é publicocpsub.metodoprivado(); // metodoprivado é publico

}}

Modificador de acesso: Private

Aplica-se a:

● classes aninhadas: indica que a classe aninhada pode ser instanciada e usada apenas pela classe onde está aninhada (definida)

● Campos: indica que o campo só pode ser usado pela própria classe onde está definido. Nem pode ser usada por subclasses da sua classe.

● métodos/construtores: indica que o método só pode ser usado pela própria classe que a define. Nem pode ser usada por subclasses.

31-07-2007 21/96

Apontamentos sobre Java 5.0 Luís Mendes

Nota: A classe que dá o nome a um ficheiro de ficheiro de código não pode ser declarada como private.

package classesprivadas;public class ClassePrivada {

private int i = 0;private void metodoprivado(){ }

}

package classesprivadas;public class SubClassePrivada extends ClassePrivada {

public void fazer(){

//i++; dá erro é privado//metodoprivado(); dá erro é privado

}}

package classesprivadas;public class ClassePrivadaUso {

public void fazer(){

ClassePrivada cp = new ClassePrivada();SubClassePrivada cpsub = new SubClassePrivada();//cp.i; // dá erro é privado//cp.metodoprivado(); // dá erro é privado//cpsub.i; // dá erro é privado//cpsub.metodoprivado(); // dá erro é privado

}}

Modificador de acesso: “Default” (não tem keyword)

Aplica-se a:

● classes/interfaces/enums/classes aninhadas: indica que a classe/interface/enums só pode ser instanciada por qualquer outra que esteja presente na mesma package da classe/interface/enum/classe aninhada.

● Campos: indica que o campo só pode ser usado directamente por outras classes

31-07-2007 22/96

Apontamentos sobre Java 5.0 Luís Mendes

ou por subclasses definidas na mesma package da classe/interface/enum/classe aninhada.

● métodos/construtores: indica que o método só pode ser usado directamente por outras classes ou por suas subclasses definidas na mesma package da classe/interface/enum/classe aninhada.

Considera-se classes definidas na mesma package as classes com os nomes das packages exactamente iguais. Não há a noção de equivalência de packages através da hierarquia , isto é, a classe A na package pkga.pkgb não é a mesma que a classe A da package pkga.pkgb.pkgc.

Em sintese, o modificador de acesso “default”, possibilita que os elementos qualificados sejam tratados como sendo públicos apenas por outros elementos da sua package. Fora da sua package os elementos são considerados como sendo privados.

package classesdefault;// all defaultclass ClasseDefault {

int i = 0;ClasseDefault(){ }

void metodoprivado(){ }

}

package classesdefault;class SubClasseDefault extends ClasseDefault {

public void fazer(){

i++; // Não dá erro SubClasseDefault está na mesma package que // ClassDefault

metodoprivado(); //Não dá erro SubClasseDefault está na mesma //package que ClassDefault

}}

package classesdefault;public class ClasseDefaultUso {

public void fazer(){

ClasseDefault cp = new ClasseDefault();SubClasseDefault cpsub = new SubClasseDefault();

31-07-2007 23/96

Apontamentos sobre Java 5.0 Luís Mendes

cp.i++; // Não dá erro ClasseDefaultUso está na mesma package que // ClassDefault

cp.metodoprivado(); // Não dá erro ClasseDefaultUso está na mesma // package que ClassDefault

cpsub.i++; // Não dá erro ClasseDefaultUso está na mesma package // que SubClassDefault

cpsub.metodoprivado(); // Não dá erro ClasseDefaultUso está na // mesma package que SubClassDefault

}}

package classesdefault.subpackage;public class ClasseDefaultUso {

public void fazer(){

ClasseDefault cp = new ClasseDefault(); // ERRADO Dá erro porque // não está // na mesma package da // classe ClasseDefault.

cp.i++; // dá erro porque cp não foi instanciada cp.metodoprivado(); // dá erro porque cp não foi instanciada

}}

package classesdefault.subpackage;/* ERRADO Dá erro porque não está na mesma package de ClassDefault.*/ class SubClasseDefault extends ClasseDefault {

public void fazer(){

}}

Modificador de acesso: Protected

Aplica-se a:

● classes aninhadas: indica que a classe aninhada só pode ser instanciada por qualquer outra que esteja definida na mesma package da classe.

● Campos: indica que o campo só pode ser usado directamente por outras classes ou por subclasses da sua classe definidas na mesma package onde está presente a classe que contém o método. Subclasses da classe definidas noutras packages podem aceder directamente ao campo.

● métodos: indica que o método só pode ser usado directamente por outras classes ou por subclasses da sua classe definidas na mesma package onde está presente a classe que contém o método. Subclasses definidas noutras packages podem aceder directamente o método.

31-07-2007 24/96

Apontamentos sobre Java 5.0 Luís Mendes

● Construtores: indica que o construtor só pode ser usado por outras classes ou pelas subclasses da sua classe que são definidas na mesma package do constructor.

Em forma de resumo o modificador protected dentro da mesma package comporta-se como o modificador“default”. Fora da package só permite que as subclasses tenham acesso aos elementos protegidos de uma classe.

Nota: Só as classes aninhadas podem ser declaradas protected, a classe principal não pode ser declarada como protected.

package classesprotected;public class ClasseProtected {

public ClasseProtected(){}

protected int i = 0;protected void metodoprivado(){}

}

package classesprotected;public class SubClasseProtected extends ClasseProtected {

// consegue aceder aos métodos/campos protectedpublic void fazer(){

i++; metodoprivado();

}}

package classesprotected;// Classe que não estende outra mas por estar na mesma package que a classe //com elementos protected consegue aceder aos seus métodos e campos protected. //Esta regra é diferente da regra protected da linguagem C++.public class ClasseProtectedUso {

public void fazer(){

ClasseProtected cp = new ClasseProtected();SubClasseProtected cpsub = new SubClasseProtected();cp.i++; cp.metodoprivado(); cpsub.i++; cpsub.metodoprivado();

}}

31-07-2007 25/96

Apontamentos sobre Java 5.0 Luís Mendes

package classesprotected.outrapkg;// Classe noutra package que estende a classe com com elementos protectedpublic class SubClasseProtected extends classesprotected.SubClasseProtected {

@Overrideprotected void metodoprivado() {

super.metodoprivado();}

// consegue aceder aos métodos/campos protectedpublic void fazer(){

i++; metodoprivado();

}}

Se um elemento não tiver nenhum modificador de acesso o seu acesso por omissão é “default”

Modificadores de acesso e a re-escrita de métodosQuando um método é re-escrito por uma subclasse o modificador do método pode alterar desde que seja para o tornar mais público, isto é, desde que o seguinte sentidos de modificação seja respeitado:

private --> default --> protected --> public

Modificador finalAplica-se a:

● classes: significa que a classe não pode ser estendida

● campos/variáveis dos métodos: significa que o seu valor não pode ser alterado (constante) após ter sido atribuído pela primeira vez.

● métodos: significa que não pode ser re-escritos por outras classes filhas.

A inicialização de campos final pode ser realizada de duas formas: em conjunto com declaração do campo ou no constructor da classe.

package finaltest;public class FinalTest {

private final int i = 12; // Forma 1: declaração e inicialização

31-07-2007 26/96

Apontamentos sobre Java 5.0 Luís Mendes

// ao mesmo tempoprivate final Long l; // Forma 2: só declaração, a inicialização

//é feita no construtor

public FinalTest(long y){

l=y; // inicialização da forma 2l=67L; // ERRO: não é permitido porque o campo já foi

// inicializado}

public void inicializar(final int arg){

final int u; // variável final u = 6;//i = 15; // ERRO: não é permitido porque o campo i

// é final e já tem valor//l = 78L; // ERRO: não é permitido, só num construtorl.toString();

}}

Modificador AbstractAplica-se a:

● classes: significa que não pode haver objectos para este tipo de classes, isto é, não podem ser instanciadas. Mas podem ser estendida por outras classes. Desde que a classe tenha um método abstracto ela tem de ser declarada como abstract.

● Métodos: significa que o método é declarado na classe mas não tem implementação. Deverá ser re-escrito pelas classes filhas. Quando uma classe tem pelo menos um método abstract a classe tem de ser obrigatoriamente declarada como abstract também.

Modificador StaticAplica-se a :

● campos: O campo é partilhado por todas as instancias da classe. Não é necessário criar um objecto da classe para usar o campo.

● Métodos: O método é partilhado por todas as instancias da classe. Não é necessário criar um objecto da classe para usar o campo. O código dentro de um método estático só pode invocar outros métodos e campos estáticos ou os métodos das classes que cria. Um método estático não pode ser re-escrito de modo a tornar-se não estático. Um método estático pode ser public, private, protected ou “default”. Os métodos estáticos não podem ser re-escritos, porque estão sempre “agarrados” a uma classe.

● Pedaços de código solto: o código é executado uma única vez quando a classe é

31-07-2007 27/96

Apontamentos sobre Java 5.0 Luís Mendes

carregada pela máquina virtual.

Modificador NativeAplica-se a:

● métodos: representam métodos em java que chamam directamente funções em bibliotecas implementadas noutras linguagens. No sistema operativo Windows os métodos estáticos possibilitam a invocação de funções presentes em dlls. Para que as funções possam estar disponíveis é necessário carrega-las previamente, para isso, deve-se invocar o método System.loadLibrary().

Modificador TransientAplica-se a:

● campos: indica que o campo não deve ser usado pelo mecanismo de serialização da linguagem Java.

Modificador VolatileAplica-se a:

● campos: indica que os campos são modificados assincronamente. Relacionado com a aplicação de threads.

Modificador SynchronizedAplica-se a:

● métodos: Indica que só uma thread num dado momento pode executar o método. As outras threads que nesse momento o queiram invocar vão ter de esperar que a primeira thread execute a última linha do método. Os métodos synchronized também podem ser static.

31-07-2007 28/96

Apontamentos sobre Java 5.0 Luís Mendes

Capitulo 4- Conversões e casting

Os valores dos tipos primitivos e as referencias para classes, interfaces ou array podem ser atribuídos a variáveis de outros tipos caso determinadas regras sejam seguidas.

A atribuição entre variáveis ou literais de diferentes tipos pode ser categorizada de duas formas:

● conversão entre tipos : Neste caso, com base nos tipos das variáveis/literais envolvidos a atribuição pode ser realizada automaticamente pelo compilador.

● casting entre tipos: Não pode haver um conversão de tipo pelo que para conversão possa ser realizada o programado tem de invocar o operador de cast para informar ao compilador o que deve fazer.

Conversões entre tipos primitivosUma variável de um tipo primitivo pode ser convertida noutra variável de outro tipo primitivo se o tipo destino satisfazer as seguintes regras:

● Um dos tipos não é booleano. Um booleano não pode ser convertido noutro tipo primitivo nem um tipo não-booleano num booleano.

● O tipo destino é maior que o tipo original. A grandeza entre os tipos é medida através a seguinte relação,

Conversões nas operações aritméticasOperadores aritméticos operam conversões automáticas aos seus argumentos da seguinte forma:

● Os operadores unários excepto o ++ e o -- convertem o tipo automaticamente para int se o tipo onde actuam é do tipo byte, short ou char e mantem o tipo se ele for maior ou igual que int.

● Os operadores binários só trabalham com argumentos do tipo double, float, int e long. Quando os dois argumentos são de tipos diferentes o argumento com o tipo menor é convertido para o mesmo tipo do outro argumento. Os argumento com tipos byte, short ou char são sempre convertidos pelo menos para o tipo int.

31-07-2007 29/96

byte short int long float double

char

Apontamentos sobre Java 5.0 Luís Mendes

Casting em tipos primitivosSó é necessário efectuar um cast entre tipos primitivos quando o tipo destino é menor que o tipo origem. Esta operação pode provocar alteração do valor original porque o intervalo de valores do tipo destino não comporta todos os valores do tipo origem.

A única situação em que não se pode empregar o operador cast entre tipos primitivos é quando um dos tipos é booleano. Um tipo booleano não pode ser um tipo destino nem origem para o operador de cast.

Conversões entre referências

As referencias para objectos podem ser convertidas entre si, se a referencia de destino for um subtipo da referencia de origem, por exemplo, todas as classes derivam de Object pelo que todas as classes podem ser convertidas para o seu subtipo Object.

Como existem três tipo de referencias, para objectos, para interfaces e para arrays as regras de conversão entre elas são as seguintes:

Tipo de origem é um objecto

Tipo de origem é uma interface

Tipo de origem é um array

Tipo de destino é um objecto

O tipo de destino tem de ser um subtipo do tipo de origem

O tipo de destino tem de ser do tipo Object

O tipo de destino tem de ser do tipo Object

Tipo de destino é uma interface

O tipo de origem tem de implementar a interface.

O tipo de destino tem de ser um subtipo do tipo origem

Tem de ser uma das interfaces implementadas por um array: Cloneable, Serializable

Tipo de destino é um array

Não é possível (erro de compilação)

Não é possível (erro de compilação)

O tipo de destino tem de ser um array.E o tipo de destino do array tem de ser um subtipo do tipo de origem do array.

Casting entre referênciasO casting entre referências para objectos é realizada e verificada em tempo de execução do programa. Isto permite permite que uma referencia para um subtipo de um objecto possa ser convertida para uma referência de uma classe filha. As regras de conversão são validadas durante a execução, pelo que se o objecto não for compatível com o tipo da

31-07-2007 30/96

Apontamentos sobre Java 5.0 Luís Mendes

referência indicada no operador Cast surgirá o lançamento de uma Runtime Exception.

Integer i = new Integer();Object o = i; // conversão: Object é um tipo de IntegerInteger u = (Integer) o; // cast: a referência u estende Object

Tipo de origem é um objecto

Tipo de origem é uma interface

Tipo de origem é um array

Tipo de destino é um objecto

O tipo de destino tem de estender a classe origem.

O tipo de destino tem de ser do tipo Object

O tipo de destino tem de ser do tipo Object

Tipo de destino é uma interface

O tipo de origem tem de implementar a interface.

O tipo de destino tem de estender o tipo origem

Tem de ser uma das interfaces implementadas por um array: Cloneable, Serializable

Tipo de destino é um array

Não é possível (erro de compilação)

Não é possível (erro de compilação)

Os tipos do array tem de ser referências e não tipos primitivos.E o tipo de destino tem de estender o tipo de origem.

31-07-2007 31/96

Apontamentos sobre Java 5.0 Luís Mendes

Capítulo 5 – Controlo do fluxo de execução, Asserções e excepções

Controlo do fluxo de execuçãoAs instruções da linguagem Java para controlar o fluxo de execução de um programa são,

int i;// ================// ciclo Whilei=0;while (i<2){

if (i > 3) continue; // instrução continue sem menção de qual o ciclo a continuar.

// Esta instrução avança para a próxima iteração (linha // contento a instrução while) sem // executar as restantes linhas da iteração actual

i++;break; // termina o ciclo imediatamente

}

// ================// ciclo Do Whilei = 0;mainloop:do // a etiqueta mainloop é opcional e é usado pela instrução // continue{

if (i>3) continue mainloop; // a etiqueta identifica o ciclo cuja iteração deve

//ser retomadai++;break; // termina o ciclo imediatamente

}while (i<2); // é necessário terminar com ;// ================// ciclo for i = 0;for (int j=1; j< 2; j++) // na zona de declaração do ciclo for pode-se // declarar mais do que uma varável desde que seja // do mesmo tipo que a anterior.{

i++;break; // termina o ciclo imediatamente, a instrução continue também

// podia ser usada dentro do ciclo}j = 6; // ==> dá erro porque a variável só existe dentro do ciclo for

// ================// selecção if

31-07-2007 32/96

Apontamentos sobre Java 5.0 Luís Mendes

if (i>4){

System.out.print("oi");}else if (i>5){

System.out.print("ola");}

// ================// selecção switchint y;switch (i*2) // a expressão do switch pode do tipo int, byte, short ou char // mas não pode ser do tipo Long{

case 5: // Cada caso tem de ser identificado por uma constantey=3;y++;break; // tem de ser usado para terminar o switch, senão o

// processamento continua pelos casos do switch ainda não // consultados.

case 15: // dois casos com o mesmo processamentocase 6:

y=3;y++;break;

default: // se todos os outros casos falharemy=2;break;

}

Excepções Os princípios de programação orientada por objectos encoraja a divisão das acções a realizar em diversos objectos e em diversos métodos, formando deste modo, uma teia de invocações para resolver um determinado problema.

Quem programa sabe que a qualquer momento as instruções de um método podem encontrar uma situação inesperada da qual não saibam lidar. Se isso acontecer o método deve parar a sua execução e avisar o método que o invocou que ele terminou abruptamente devido a uma situação de erro. O método invocante deve capturar o aviso, analisa-lo e decidir qual a acção a realizar perante a execução anómala.

O mecanismo de aviso e de analise da situação anómala descrito no paragrafo anterior é disponibilizado pela linguagem Java através das excepções.

O uso das excepções em Java requere dois momentos:1. O lançamento por parte de um método da excepção contendo a informação da

31-07-2007 33/96

Apontamentos sobre Java 5.0 Luís Mendes

anomalia ocorrida2. A captura, por parte do método invocante, da excepção contendo a informação da

anomalia.

Em detalhe,

1. Lançamento da excepção

Para lançar uma excepção usa-se o comando throw e uma referência para um objecto descendente de java.lang.Throwable. O objectivo deste objecto é o de caracterizar a anomalia. Por omissão, a caracterização é feita através do nome da classe do objecto e opcionalmente através de uma mensagem de texto ou através da referência para um outro objecto Throwable. À classe descentente de Throwable é possível adicionar campos e métodos para caracterizar ainda mais a situação de erro.

public void metodo() throws Throwable{

throw new Throwable("ficheiro não disponível!");}

Quando a instrução throw é executada ocorrem os seguintes passos:● A execução do método que lança a excepção termina imediatamente;● É colocado no objecto descendente de Throwable a pilha dos métodos executados

antes do comando throw (fica-se a saber por onde o código passou)● A execução retorna para a instrução do método invocante que está imediatamente

a seguir à chamada do método que lançou a excepção. Se este método não capturar a excepção lançada, a execução do método invocante é imediatamente terminada passando para o método invocante do invocante e assim por diante, até que a excepção seja capturada ou até que o método main da aplicação seja atingido. A captura da excepção por parte de um dos métodos invocantes implica obrigatoriamente a execução das instruções associadas à de captura.

Regra geral, os objectos que caracterizam as excepções não são instâncias directas de Throwable mas de instancias de classes que derivam directamente ou indirectamente da classe java.lang.Execption ou da classe Java.lang.RuntimeException. A diferença entre elas está no tipo de anomalias que caracterizam e na forma como o compilador impõe as regras de uso.

As classes derivadas de java.lang.Exception representam anomalias que o programador não tem possibilidade de controlar quando desenvolve o código, por exemplo, um ficheiro não é encontrado numa directoria. Um método que lança uma excepção deste tipo tem de declarar obrigatoriamente na assinatura do seu método o nome da classe da excepção usando a instrução throws. O método que invoca outro método que declara lançar excepções do tipo java.lang.Exceptions têm de obrigatoriamente capturar a excepção

31-07-2007 34/96

Apontamentos sobre Java 5.0 Luís Mendes

ou se o não fizer tem de declarar na sua assinatura a excepção não capturada (recorrendo à instrução throws). Como a imposição das regras para as excepções do tipo java.lang.Exceptions é feita pelo compilador este tipo de excepções chama-se: excepções ckecked.

public class run {public void metodo(int i) throws Exception // Declaração da excepção

// checked {

if (i>3) throw new Exception("ficheiro não encontrado."); // lançamento da

// excepção, // devido a uma // situação // anómala

}

public static void main(String[] args) {run r = new run();try {

r.metodo(4);}catch (Exception e) // captura da excepção que pode ser lançada

// por r.metodo(){

System.out.println("erro: " + e.getMessage());e.printStackTrace();

}}

}

As classes derivadas de java.lang.runtimeException representam anomalias que o programador pode controlar enquanto está a desenvolver o programa e que não deveriam acontecer durante a execução do código, por exemplo, o índice de um array é maior que a sua dimensão. O compilador não impõe nenhumas regras ao método que lança este tipo de excepção nem aos métodos invocantes, dando-se o nome de unchecked a esta familia de excepções. Note-se que estas excepções podem acontecer durante a execução do código e se não forem capturadas em algum ponto poderão provocar a interrupção abrupta do programa.

public class run {public void metodo(int i) {

if (i>3) throw new RuntimeException("índices fora de execução.");

}

31-07-2007 35/96

Apontamentos sobre Java 5.0 Luís Mendes

public static void main(String[] args) {run r = new run();r.metodo(4); // não é obrigatório que seja capturado a excepção

try { // as excepções Runtime podem ser capturadas mas não é // obrigatório que o sejam

r.metodo(6);}catch (RuntimeException e) {

System.out.print("RuntimeException:" + e.getMessage());e.printStackTrace();

}}

}

As classes que caracterizam as excepções estão relacionadas entre si da seguinte forma,

Uma excepção do tipo java.lang.Error representa um erro grave durante a execução da aplicação ou durante a compilação. Embora esta excepções possam ser capturadas habitualmente não o são porque representam anomalias extremas que geralmente não podem ser recuperadas pelo próprio programa. Estas excepções são consideradas como unchecked pelo que as suas regras de uso são semelhantes às excepções que derivam de java.lang.RuntimeException.

As boas práticas recomendam que não sejam lançadas através da instrução throw instancias directas de Throwable, Exception, Error, e RuntimeException mas instâncias de suas subclasses, para que deste modo, possa ser realizada uma triagem da excepção na altura da captura. A triagem é feita através do nome da classe da excepção.

31-07-2007 36/96

java.lang.Throwable(checked)

java.lang.Exception(checked)

java.lang.Error(unchecked)

java.lang.RuntimeException(unchecked)

?(unchecked)

?(unchecked)

?(unchecked)

Apontamentos sobre Java 5.0 Luís Mendes

As excepções lançadas pelos métodos são classes normais que fazem parte de hierarquias de classes. Desta forma, os objectos das excepções estão sujeitos às regras do operador instanceof, isto é, um objecto da classe RuntimeException é também uma Exception e uma Throwable, por exemplo.

2. captura de excepções

A instrução try catch finally permite capturar excepções que poderão ser lançadas pela instrução throw.

O código que lança execpções é colocado dentro do bloco try, sendo que existirão tantos blocos catch quantas classes de excepções estivermos preparados para capturar. Cada catch identifica a classe da excepção que é responsável por capturar e ao mesmo tempo define um conjunto de instruções que serão executadas quando a excepção for lançada dentro do bloco try.

Quando uma instrução do bloco try lança uma excepção as restantes são imediatamente ignoradas. As instruções catch são percorridas por ordem de escrita para encontrar a primeira que está habilitada a capturar a excepção lançada, ou seja, o primeiro bloco catch que identifica a classe da excepção ou de uma classe das classes pai da excepção (de acordo com as regras do operador instanceof). Caso seja encontrado o bloco catch para a excepção as instruções do próprio bloco são executadas para tratar a anomalia detectada. Uma vez finda a execução do código para tratar a excepção, a instrução a imediatamente a seguir ao try catch finally é executada. Para a hipótese de não ser encontrada nenhum bloco catch para a excepção lançada a execução o fluxo de execução do programa é interrompido para outro método que possa capturar a execpção.

O bloco finaly permite executar instruções que são sempre executadas independentemente de a excepção ter sido capturada por um bloco catch ou mesmo independentemente de uma excepção ter sido lançada no bloco try. Devido à garantia de execução do bloco finaly após a execução do bloco try ou do catch é costume usar este bloco para realizar operações limpeza, como fechar ficheiros ou streams.

i = 2;

System.out.println("antes do try-catch-finally"); // é executadotry {

System.out.println("try 1");if (i>1)

throw new FileNotFoundException("ficheiro não encontrado");System.out.println("try 2"); // NÃO É EXECUTADO

}catch (FileNotFoundException e) {

System.out.println("dentro FileNotFoundException"); // é executado}

31-07-2007 37/96

Apontamentos sobre Java 5.0 Luís Mendes

catch (Exception e){

System.out.println("Dentro Exception"); // NÃO É EXECUTADO}finally{

System.out.println("dentro do finally"); // É executado}System.out.println("depois do try-catch-finally"); // é executado

O código dos blocos catch e finally também pode ter a instrução try-catch-finally ou lançar novas exepções. O exemplo a seguir ilustra a situação em que uma excepção é lançada de dentro de um bloco catch,

i = 2;

System.out.println("antes do try-catch-finally"); // é executadotry {

System.out.println("try 1");if (i>1)

throw new FileNotFoundException("ficheiro não encontrado");System.out.println("try 2"); // NÃO É EXECUTADO

}catch (FileNotFoundException e) {

System.out.println("dentro FileNotFoundException"); // é executado// um catch também pode lançar uma excepçãoif (i>1)

throw new RuntimeException("Excepção lançada num catch");System.out.println("dentro FileNotFoundException 2"); // NÂO É EXECUTADO

}catch (Exception e){

System.out.println("Dentro Exception"); // NÃO É EXECUTADO}finally{

System.out.println("dentro do finally"); // é executado}System.out.println("depois do try-catch-finally"); // ==> NÂO é executado porque foi lançada uma excepção dentro do bloco catch e esta não foi ainda capturada

uma execpção é lançada de dentro de um bloco finally,

i = 2;

System.out.println("antes do try-catch-finally"); // é executadotry {

System.out.println("try 1");if (i>1)

throw new FileNotFoundException("ficheiro não encontrado");System.out.println("try 2"); // NÃO É EXECUTADO

}

31-07-2007 38/96

Apontamentos sobre Java 5.0 Luís Mendes

catch (FileNotFoundException e) {System.out.println("dentro FileNotFoundException"); // é executado// um catch também pode lançar uma excepçãoif (i>1)

throw new RuntimeException("Excepção lançada num catch");System.out.println("dentro FileNotFoundException 2"); // NÃO É EXECUTADO

}catch (Exception e){

System.out.println("Dentro Exception"); // NÃO É EXECUTADO}finally{

System.out.println("dentro do finally"); // é executadoif (i>1)

throw new RuntimeException("Excepção lançada num finally"); // ==> esta excepção substitui a excepção do catch

System.out.println("dentro do finally 2"); // Não é executado}System.out.println("depois do try-catch-finally"); // ==> NÃO é executado //porque foi lançada a excepção dentro do finally

}

A ordem de escrita dos blocos catch é importante porque apenas o primeiro que é compatível com a classe da excepção é executado. No exemplo anterior se o bloco catch(Exception e) estivesse definido antes de catch(FileNotFoundException e) seria o bloco catch(Exception e) executado porque uma classe FileNotFoundException também é uma Exception.

É garantido a execução do bloco finally independentemente do que acontece nos blocos catch/try. O bloco finally é opcional, sendo perfeitamente possível usar apenas o try-catch. Também é possível só usar apenas o try-finally.

AsserçõesNão é apropriado lançar uma excepção para validar pré-condições de métodos, a não ser que sejam públicos. Para implementar a validação das pré-condições de métodos não públicos é mais natural recorrer às asserções.

Uma asserção em JAVA é realizada recorrendo à instrução assert que pode ser usado de duas formas:

● assert <condição booleana>; ==> se a condição booleana for falsa é lançado um erro do tipo AssertionError e o programa termina.

● assert <condição booleana> : “mensagem de texto”; ==> se a condição booleana for falsa é lançado um erro do tipo AssertionError com a mensagem de texto indicada e o programa termina.

31-07-2007 39/96

Apontamentos sobre Java 5.0 Luís Mendes

public void metodo(int i) {

System.out.println("antes do assert");assert i>2:"erro nos parâmetros";System.out.println("depois do assert"); // não é executado se i<2

}

As asserções são recursos que o programador deve usar apenas quando está a desenvolver o programa e que lhe permitem validar pressupostos pensados durante o desenvolvimento do código. Normalmente são usadas em métodos privados para validar as suas pré-condições e pós-condições durante a fase de desenvolvimento do código.

Os compiladores da versão 5 aceitam por omissão o comando assert. O compilador 1.4 só recolhe-se o comando se o código for compilado com o parâmetro: -source 1.4

A máquina virtual JAVA só executa as asserções se for lançada com o parâmetro -enableassertions ou -ea (normalmente usadas durante a fase de desenvolvimento). Caso contrário, as asserções do código são ignoradas durante a execução do programa.

31-07-2007 40/96

Apontamentos sobre Java 5.0 Luís Mendes

Capítulo 6 – Objectos e classes

Classes

Em Java tudo gira à volta das classes. Um programa em Java é constituído apenas por classes, podendo na pior das hipóteses ser constituído somente por uma classe. As classes são a unidade mínima de trabalho para o compilador.

Numa classe é possível definir :● campos: são as variáveis próprias da classe. Representa os dados da classe;● métodos: são as funções e procedimentos da classe. Representa o

comportamento da classe.

As classes encapsulam dados e comportamento, contudo elas não são usadas directamente durante a execução de um programa. Para as usar elas têm de ser instanciadas, isto é, é necessário criar objectos de uma classe. Um objecto tem estruturalmente os mesmos métodos e os mesmos campos que a sua classe, acontece que em tempo de execução cada objecto de uma mesma classe possui valores próprios nos seus campos. Embora as instruções dos métodos dos objectos de uma classe sejam iguais entre si – provêm da definição da classe - durante a execução do programa os métodos dos objectos quando consultam ou alteram os valores dos campos do seu objecto não estão implicitamente a alterar os valores dos campos dos outros objectos da mesma classe.

Cada classe da linguagem Java é definida num ficheiro de texto onde o nome do ficheiro é igual ao nome da classe mais a extensão .java. Em cada ficheiro só pode ser definida uma classe, embora dentro da classe possa ser definidos outras classes (ver classes aninhadas).

Construtores

O acto de criar um objecto de uma classe, ou em outras palavras, de criar uma instancia de uma classe é realizado a através do comando new.

O comando new invoca um método especial da classe que é usado para inicializar os campos do objecto e retorna uma referência (apontador) para o objecto criado. Ao método especial dá-se o nome de construtor e é caracterizado por ter o mesmo nome que a classe, por poder ter qualquer número de parâmetros e de não ter nenhum tipo de retorno.

31-07-2007 41/96

Apontamentos sobre Java 5.0 Luís Mendes

Integer ref = new Integer(34); // um dos construtores da classe Integer

As classes podem ter vários construtores desde que eles apresentem diferentes listas de parâmetros,

// overloading dos construtorespublic class ClassA {

public ClassA() {}// private ClassA() {} NÂO é PERMITIDO construtor duplicado do de cima

private ClassA(int a) {}// private ClassA(int a) throws Exception {} NÂO é PERMITIDO construtor

duplicado do de cima

protected ClassA(int a, String b) {}ClassA(Long l) {};

}

Se uma classe não definir nenhum construtor e só neste caso o compilador java cria automaticamente um com as seguintes características:

● Sem parâmetros;● O modificador de acesso é igual ao da classe, podendo ser “default” ou “public”;● Caso a classe estenda outra, o construtor criado pelo compilador chama

automaticamente o construtor sem argumentos da classe pai.

Ao construtor criado pelo compilador dá-se o nome de “default construtor”. Portanto o compilador garante que todas as classes têm pelo menos um construtor.

Quando uma classe estende outra classe, os construtores não seguem as regras de herança dos métodos, porque não há herança de construtores. Os construtores de uma classe só estão disponíveis para essa classe. As sub-classes terão de definir construtores para si que definirão as suas próprias regras de inicialização.

Quando um objecto de uma classe é criada e esta estende outras é necessário inicializar todos os campos da hierarquia, para isso, cada construtor de uma classe têm de obrigatoriamente invocar um dos construtores da classe da qual estende e esta por sua vez também tem de invocar um dos construtores da classe que estende e assim por diante. Para invocar um construtor da classe estendida usa-se o método especial super(lista com os valores para os parâmetros do construtor a invocar).

public class ClassA {public ClassA(int a) {}

}

public class ClassB extends ClassA {

31-07-2007 42/96

Apontamentos sobre Java 5.0 Luís Mendes

public ClassB(String a){

// super(); Não pode ser porque a classA não tem um construtor // sem argumentos

super(2); // chama o construtor com o argumento inteiro de ClassA}

}

public class ClassC extends ClassB {public ClassC(long l){

// super(); NÃO pode ser, a ClassB não tem um construtor default// super(1); NÃO pode ser, a ClasseB não tem um construtor para um

// inteiro e só é possível chamar construtores da classe que // se estende, neste caso ClassB

super("SLB"); // é chamado o construtor ClassB.classB(String)}

}

O método especial super tem de ser a primeira instrução de um construtor porque a linguagem java impõe que a inicialização dos campos de uma hierarquia seja feita no sentido descente: da classe raiz (a de topo) para a última classe. Se a primeira instrução de um construtor não for o método super, então o compilador insere automaticamente a linha super() como a primeira instrução provocando a invocação do construtor default da classe estendida. Se o construtor default não existir então ocorre um erro de compilação.

public class ClassA {public ClassA(){

System.out.println("Construtor default da classe A");}

public ClassA(int i){

System.out.println("Construtor int da classe A" +i );}

}

public class ClassB extends ClassA {public ClassB(){

// O compilador chama automaticamente o construtor defaultSystem.out.println("Construtor default da classe B");

}

public static void main(String[] args){

ClassB c = new ClassB(); // ==> "Construtor default da classe A" e // "Construtor default da classe B"

}

31-07-2007 43/96

Apontamentos sobre Java 5.0 Luís Mendes

}

Dentro da mesma classe é possível que um construtor invoque outro para, por exemplo, aproveitar código comum de inicialização da classe. A invocação de construtores da mesma classe por parte de outro construtor é realizada recorrendo ao método especial this(lista de valores para os parâmetros do construtor a invocar). O método especial this tem de ser a primeira instrução de um construtor e não pode ser usada em conjunto com o método especial super.

public class ClassA {int i ;public ClassA(int y){

super(); // Chama o construtor da classe Object. // Não era necessário. o compilador colocaria esta

// instrução automaticamente.i = 2;

}

public ClassA(int y, int a){

this(y); // chama o constructor ClassA.ClassA(int)a++;

}}

Métodos

Um método de uma classe é constituído pela sua declaração e por um conjunto de instruções.

A declaração é constituída por:

● modificadores do método: static, final, public, private, etc,.

● Tipo do retorno do método: pode ser void se não retornar nada

● nome do método

● Lista dos tipos e nomes dos parâmetros do método, é opcional.

● Lista das excepções que o método pode lançar, é opcional. Obrigatoriamente têm de estar nesta lista as excepções checked lançadas pelo método.

public Long calcLong(int a) {};protected void readFile(int a) throws FileNotFountException {};

31-07-2007 44/96

Apontamentos sobre Java 5.0 Luís Mendes

Da declaração de cada método é possível extrair a sua assinatura que permite identificar univocamente o método na hierarquia da classe onde está declarada. A assinatura é formada:

● pelo nome do método;

● e pelos tipos da lista de argumentos, na exacta ordem e número tal como estão presentes na declaração.

Dentro de uma classe podem ser definidos métodos com o mesmo nome, a esta técnica chama-se overloading, desde que os tipos da lista de parâmetros dos métodos não sejam iguais na sua ordem e nem nos seus tipos. Se as diferenças entre métodos da classe com o mesmo nome apenas se cingirem ao tipo de retorno, modificador ou lista de excepções, o compilador considera que os métodos são duplicados resultando num erro de compilação. Os métodos estáticos também seguem esta regra.

public class ClassA {// método que será overloadingprotected Number metodo(int a) { return new Integer(3); }// NOK: adição de uma excepção não faz o método diferente// protected Number metodo(int a) throws Exception {

// return new Integer(2); } ==> não pode ser definido

// OK: são métodos diferentes. a lista de argumentos é diferenteprotected Number metodo(String a) { return new Integer(3); }protected Number metodo(int a, short s) { return new Integer(33); }// NOK: alteração apenas do retorno não faz um método diferente//protected Integer metodo(int a) { return new Integer(3); }//protected void metodo(int a) { return new Integer(3); }

// NOK: alteração apenas do modificador não faz um método diferente//public Number metodo(int a) { return new Integer(3); }//protected static Number metodo(int a) { return new Integer(3); }

// os métodos estáticos têm o mesmo comportamento protected static void metodoestatico(int o) {}protected static void metodoestatico(int o, short s) {}// NOK: não pode ser //protected void metodoestatico(int o) {}

public static void main(String[] args){

// uso dos métodos ClassA c = new ClassA();c.metodo(2);c.metodo("slb");c.metodo(3, (short)89);ClassA.metodoestatico(3);

31-07-2007 45/96

Apontamentos sobre Java 5.0 Luís Mendes

ClassA.metodoestatico(2, (short)7);}

}

Dentro de uma classe não pode haver métodos com a mesma assinatura embora numa hierarquia de classes é possível a existência de métodos com assinaturas identicas desde que definidos em classes distintas. A esta técnica dá-se o nome de : re-escrita de métodos (overriding).

Numa hierarquia de classes, a classe que estende outra pode substituir a implementação de um método definido pela classe Pai. Chama-se a esta técnica overriding. Para que tal aconteça o método da classe que estende tem de ser declarado do seguinte modo:

● o modificador de acesso pode ser diferente desde que seja alterado para um que satisfaça a seguinte ordem: private -> default -> protected -> public.

● O retorno pode ser diferente se o novo tipo for um subtipo do original● O nome tem de ser o mesmo● A lista de parâmetros tem de respeitar a ordem de declaração e os tipos do método

definido pela classe Pai● A lista de excepções se existir pode ser diferente, desde que as novas excepções

sejam sub-classes das originais ou que sejam retiradas excepções à lista da declaração.

public class ClassA {// método normal protected Number metodo(int a) throws FileNotFoundException {

return new Integer(3); }// método estáticoprotected static int metodoestatico(short o) {return 3;}

}

public class ClassB extends ClassA {// overriding do método//OK método exactamente igual//protected Number metodo(int a) throws FileNotFoundException {

return new Integer(78); }

//OK: o retorno é um subtipo de Number//protected Integer metodo(int a) throws FileNotFoundException {

return new Integer(78); }

// NOK: o retorno NÃO é um subtipo de Number//protected int metodo(int a) throws FileNotFoundException {

return new Integer(78); }

// OK: o modificador é mais público que protected

31-07-2007 46/96

Apontamentos sobre Java 5.0 Luís Mendes

// public Integer metodo(int a) throws FileNotFoundException { return new Integer(78); }

// OK: pode não devolver nenhuma excepçãoprotected Number metodo(int a) { return new Integer(78); }// NOK: porque Exception não é uma sub-classe de FileNotFoundException //protected Number metodo(int a) throws Exception {

return new Integer(78); }

// OK: Mas isto não é um overridding do método mas sim um overloadingprotected Number metodo(int a, long l) {return null;}// Pode-se re-escrever método estático mas ele não substitui // o método da classe Pai onde está definido.// Os métodos estáticos só pertence à classe que os define, embora// sejam herdados.protected static int metodoestatico(short o) {

return 5;}

public static void main(String[] args) throws Exception{

ClassA c = new ClassB();// uso do método normal que é overridenSystem.out.println(c.metodo(5)); // ==> "78"

// uso do método estático que é overridenSystem.out.println(c.metodoestatico((short)5)); // ==> "3"// uso do método que é overloaded((ClassB)c).metodo(2, 5); // ==> null

}}

A implementação dos métodos estáticos não pode ser re-escrita por outra classe embora possa ser definido um método com a mesma assinatura em cada classe da hierarquia.

O corpo de um método contém as instruções necessárias para implementar as suas regras de negócio. As instruções podem ser qualquer elemento da linguagem JAVA como, operadores, ciclos, selectores, ou invocações de outros métodos ou campos definidos na sua classe ou na possível classe da qual estende. Observe-se que o acesso a campos e métodos de outras classes está dependente das permissões dadas pelos modificadores de acesso que foram atribuídas ao campos e métodos.

O compilador da linguagem JAVA disponibiliza a cada classe dois campos especiais:● this: este campo é usado para aceder a campos e a implementações de métodos

definidos na própria classe e na classe que é estendida.● super: este campo é usado para aceder de acordo com as permissões de acesso a

campos e a implementações de métodos definidos apenas na classe que é estendida. O campo super não permite aceder a implementações especificas de

31-07-2007 47/96

Apontamentos sobre Java 5.0 Luís Mendes

método re-escritos que não sejam realizados na classe que é estendida.

Numa hierarquia de classes os métodos e campos de cada classe é a união de todos os campos e métodos possíveis de serem acedidos em cada classe de acordo com as regras dos modificadores de acesso. A implementação dos métodos re-escritos é dada pela última classe da hierarquia que o fez.

public class ClassA {public void metodoA(){

System.out.println("Class A. metodoA");}

}

public class ClassB extends ClassA{@Overridepublic void metodoA(){

System.out.println("Class B. metodoA"); }

public void metodoB(){

System.out.println("Class B. metodoB");}

}

public class ClassC extends ClassB{@Overridepublic void metodoA(){

System.out.println("Class C. metodoA");super.metodoA(); // chama a implementação da classe B // ATENÇÂO: Não é possível aceder à implementação do // métodoA da ClassA porque super só permite aceder// aos métodos da classe estendida, isto é,

// isto não é possível// super.super.metodoA();

}

public void metodoC(){

System.out.println("Class C. metodoC");this.metodoA(); // vai chamar o método desta classe

}

public static void main(String[] args){

ClassC c = new ClassC();c.metodoC(); // produz o output:

31-07-2007 48/96

Apontamentos sobre Java 5.0 Luís Mendes

//Class C. metodoC //Class C. metodoA

//Class B. metodoA

c.metodoB(); // produz o output Class B. metodoB}

}

Até agora todos os métodos exibidos nos exemplos apresentam uma lista de argumentos fixa. A versão 5 do JAVA introduziu a capacidade de um método poder ter uma lista de argumentos variável desde que sejam do mesmo tipo. Os valores dos argumentos variáveis são acedidos no corpo do método através de um array. Na declaração do método a parte variável têm de ser o último argumento e só pode haver um argumento variável por método.

public class ClassA {// o argumento variável tem de ser o último e só pode // haver um argumento variável, neste caso é o argumento valspublic void metodo(int a, String...vals){

System.out.println(a + " Numero de args:" + vals.length );for (String s:vals)

System.out.println("arg:" + s);}

public static void main(String[] args){

ClassA c = new ClassA();c.metodo(23, "Sapo", "argila", "couves"); // ==> output

//23 Numero de args:3//arg:Sapo//arg:argila//arg:couves

}}

Classes aninhadas

Dentro de uma classe é possível definir outras classes, são as chamadas classes aninhadas. Uma classe pode ter várias classes aninhadas e uma classe aninhada também pode definir outras classes aninhadas. Inclusive a uma classe aninhada é permitido estender outras classes. É uma classe no seu pleno direito. O nome qualificado das classes aninhadas é constituido: <nome da classe principal>.<nome da classe aninhada>.

Só uma instancia da classe principal pode criar uma instancia de uma classe aninhada, através da invocação um pouco diferente do habitual do comando new,

31-07-2007 49/96

Apontamentos sobre Java 5.0 Luís Mendes

ClassePrincipal.ClasseAninhada refCA = new ClassePincipal().new ClasseAninhada();ouClassePrincipal refCP = new ClassePrincipal();ClasseAninhada refCA = refCP.new ClasseAninhada();

A vantagem de declarar classes aninhadas é que elas têm acesso a todos os elementos da classe principal inclusive ao campos e métodos privados.

A classe aninhada pode ter os quatro modificadores de acesso: public, “default”, private e protected. Desta forma é possível de acordo com as regras dos modificadores controlar a visibilidade da classe aninhada quando a classe principal é usada.

public class ClassA {private int a;protected int b;int c;public int d;public AninhadaPublica aninhadaPublica;public AninhadaPrivada aninhadaPrivada;public AninhadaDefualt aninhadaDefault;public AninhadaProtected aninhadaProtected;public ClassA(){

// a classe de fora pode criar qualquer instancia das suas // classes aninhadas

aninhadaPublica = new AninhadaPublica();aninhadaDefault = new AninhadaDefualt();aninhadaPrivada = new AninhadaPrivada();aninhadaProtected = new AninhadaProtected();

}

public class AninhadaPublica{

public void metodo(){ //acede a todo o tipo de campos/métodos da classe principal

// e a qualquer tipoa++; b++; c++; d++;AninhadaPrivada i = new AninhadaPrivada();

}}

private class AninhadaPrivada{

public void metodo(){ //acede a todo o tipo de campos/métodos da classe principal

a++; b++; c++; d++;// pode até criar objectos da classe principalClassA c = new ClassA();

}

31-07-2007 50/96

Apontamentos sobre Java 5.0 Luís Mendes

}

// Uma classe aninhada pode até estender outras classesclass AninhadaDefualt extends AninhadaPrivada{

public void metodo(){ //acede a todo o tipo de campos/métodos da classe principal

a++; b++; c++; d++;}

// Uma classe aninhada PODE inclusive definir // outras classes aninhadas

class outraClass{

}}

protected class AninhadaProtected{

public void metodo(){ //acede a todo o tipo de campos/métodos da classe principal

a++; b++; c++; d++;}

}}

uso das classes aninhadas,public class ClassB {

public static void main(String[] args){

ClassA c = new ClassA();// NOK: Não é possível criar uma instancia de uma classe aninhada// directamente!//ClassA.AninhadaDefualt c1 = new ClassA.AninhadaDefualt();

// OK: Só a instancia da classe principal pode criar uma instancia // da classe aninhadaClassA.AninhadaPublica c1 = c.new AninhadaPublica();// OK: estão na mesma packageClassA.AninhadaDefualt c2 = c.new AninhadaDefualt();ClassA.AninhadaProtected c4 = c.new AninhadaProtected();// NOK: Não está visível porque é privada

// ClassA.AninhadaPrivada c3 = c.new AninhadaPrivada();

}}

Uma classe aninhada também pode ser considerada estática. O que significa que a classe aninhada pertence à classe principal e que só pode aceder a campos e métodos estáticos da classe principal. Para criar uma classe estática-se usa-se o comando,

31-07-2007 51/96

Apontamentos sobre Java 5.0 Luís Mendes

new ClassPrincipal().ClassAninhadaEstatica();

Classes de métodosÉ possível definir classes de pleno direito dentro de métodos, estas classes só são visíveis dentro do próprio método e podem aceder aos campos e métodos da classe principal e às variáveis e argumentos do método desde que sejam declarados como final.

package classesdemetodos;public class ClassA {

private int a;protected int b;int c;public int d;public void metodo(final int i){

int a1=3;final int r=4;// Uma classe de método não tem modificador de acesso.

// É sempre privado ao métodoclass classMetodo extends ClassA{

public void metodo(){

// tem acesso às variáveis finalSystem.out.print("ClassMetodo.metodo" + i + r);// a1++; ==> NOK: não pode porque a1 não é final;// tem acessoa++; b++; c++; d++;

}

}

// uso da classeclassMetodo c = new classMetodo();c.metodo();

}

public static void main(String[] args){

ClassA c = new ClassA();c.metodo(2);

// NOK: A classe de método não é conhecida. //ClassA.classMetodo

}}

31-07-2007 52/96

Apontamentos sobre Java 5.0 Luís Mendes

Atenção que a instancia de uma classe declarada dentro de um método pode continuar viva mesmo após o fim da execução do método.

Classes anónimas

Dentro de um método também é possível declarar uma classe sem nome que estende outra classe ou implemente apenas uma interface, mas não ambas as coisas. Este tipo de classe só pode ser instanciada uma única vez e apenas dentro do método. Não oferece definição de construtores pelo que a sua inicialização têm de ser feita através de blocos soltos de código.

A declaração e instanciação é realizada através do comando new,

new ClasseAEstender(construtor da classe)/InterfaceAimplementar() {{

blocos de inicialização}métodos e campos da própria classe

};

Tal como a classe de método, as classes anónimas podem aceder aos campos e métodos da classe principal e às variáveis e argumentos do método onde está definida desde que sejam final.package classesanónimas;public class ClassA {

private int a;protected int b;int c;public int d;public void metodo(final int i){

int a1=3;final int r=4;Object o = new Object() {

// definição de um campopublic int u;{

// inicialização da classe anónima, é opcional u++;

}private void metodo(){

System.out.print("metodo" + i + r);

31-07-2007 53/96

Apontamentos sobre Java 5.0 Luís Mendes

a++; b++; c++; d++;// a1; Não pode ser acedido porque é final

}

@Overridepublic String toString() {

return super.toString() + "SLB";}

};

// NOK: Não pode ser porque o método metodo não pertence a Object // o.metodo();

o.toString(); // chama o método toString re-escrito.

}

public static void main(String[] args){

ClassA c = new ClassA();c.metodo(2);

// não é conhecido. ClassA.new Object()

}}

As classes de método e as classes anónimas são sobretudo usadas para re-escrever métodos de classes “normais” ou para implementar interfaces, uma vez que o tipo que representam está confinado apenas ao âmbito do método.

EnumsEnums são classes (o que poderia ser em Java!) que funcionam de uma forma muito especial porque, em alguns aspectos são como uma classe mas noutros não.

Um enum é declarado através da instrução enum em vez da instrução class. Um enum é um tipo como uma classe mas não pode estender outra classe nem ser estendido. Na prática os enums estendem da classe java.lang.enum mas, isso é feito implicitamente pelo compilador. Os enums permite ao programador definir construtores, senão o compilador adiciona o construtor default. Os enums podem ter campos e métodos próprios. Os enums podem ser convertidos para Object, isto é, eles também são um Object tal como acontece com as classes.

Agora as diferenças relativamente às classes. Os enums não podem ser instanciados. O que acontece é que a primeira linha da declaração do enum identifica através de uma lista quais as instancias do enum. As instancias são criadas automaticamente pelo compilador. Na lista de instancias que é possível passar parâmetros para os construtores do enum.

31-07-2007 54/96

Apontamentos sobre Java 5.0 Luís Mendes

public enum Color {red(1), YELLOW(1), GREEN(1); // lista das únicas instancias de Color.

private int u;private Color(int y){

u = y;}

public void metodo(){

System.out.println("Enum metodo: "+u);}

}

A cada instância de um enum é disponibilizado o método name() que devolve o nome da instância tal qual como foi definida na lista de instâncias do enum (no exemplo anterior o método name() da instancia red devolveria “red”) e o método ordinal() que devolve o número de ordem da instância na lista de declaração.

Para usar uma instancia de um enum, usa-se o nome qualificado,

<nome do enum>.<nome da instancia>

podendo as instancias de um enum participar como casos do selector switch,

public class Enums{public Color oneColor;public static void main(String[] args){

int i = 0;Enums e = new Enums();e.oneColor = Color.YELLOW;

// ordinal é a posição do enum dentro do métodoSystem.out.println(Color.GREEN.name() + " ordinal: " +

Color.GREEN.ordinal()); // ==> YELLOW ordinal: 1Color.GREEN.metodo(); //método próprio de cada instancia, devolve 3

switch (e.oneColor){

// no caso não pode ser usado o nome qualidicado Color.GREENcase GREEN:

System.out.println("cor verde");break;

case red: System.out.println("cor vermelha");i++;break;

31-07-2007 55/96

Apontamentos sobre Java 5.0 Luís Mendes

default:System.out.println("outra cor");i--;break;

}

try {// ==> String --> enum// É possível converter o nome de uma instância de um enum

// na própria instancia através do método valueOf Color c = Color.valueOf("red");System.out.println(c); // ==> redc = Color.valueOf("RED");System.out.println(c); // ==> red

}catch (IllegalArgumentException e1) {

System.out.println("color not found");}

}

}

31-07-2007 56/96

Apontamentos sobre Java 5.0 Luís Mendes

Capítulo 7 – Threads

O que é?É possível executar duas ou mais acções ao mesmo tempo, se elas estiverem contidas em:

● Processos: cada processo tem os seus próprios recursos. Os recursos podem ser: sockets, ficheiros, espaços de memória, etc.

● Threads: As threads são pedaços de código que correm autonomamente dentro de um processo. Um processo contém no mínimo uma thread. Os recursos do processo (espaços de memória, ficheiros, etc,.) são partilhados pelas threads desse processo.

Criação de uma threadNa linguagem Java, uma thread é criada através da extenção da classe java.lang.Thread que implementa a interface runnable. O código a executar na thread é definido pelo programador no método public void run(). Este método pode ser obtido a partir de :

● uma re-implementação do método public void run() da classe Thread (implica uma extensão da classe Thread) ou,

● uma implementação da interface Runnable que só tem o método public void run(). Depois uma instancia da class Thread executa a implementação da interface.

Ciclo de vida de uma threadAs threads de um processo em Java dividem-se em dois tipos:

● Daemons: são threads que não são levadas em conta para determina o fim do processo.

● User-defined: quando todas as threads deste tipo terminarem o processo também termina.

A máquina virtual JAVA segue os seguintes passos para executar o método main da classe indicada como parâmetro à máquina virtual:

31-07-2007 57/96

Processo

Recursos Privados (mémoria, ficheiros)

Thread #1

Thread #2Thread #3

Apontamentos sobre Java 5.0 Luís Mendes

1. Cria o processo onde será executado o programa.2. Lança as threads “daemons” necessárias para as suas actividades, como o garbage

collector ou o tratamento dos eventos Swing se a aplicação tiver uma interface gráfica.

3. Cria uma user-defined thread chamada de “main-thread” cujo método run() irá executar o método main() da classe indicada à máquina virtual. O método main() poderá directamente ou através de classes criar outras threads de qualquer tipo. O processo e indirectamente o programa, só termina quando os métodos run() das threads user-defined terminarem.

Um processo continua a executar mesmo após o fim da thread “main-thread” enquanto as outras threads “user-defined” permanecerem em execução.

Para executar o código presente no método public void run() de uma java.lang.Thread usa-se o método Thread.start(). Pode-se considerar que o método start() é o criador da thread contudo, o método start() não fica à espera que a thread criada termine porque é um método não bloqueante.

Após a execução da última linha do método run() a thread passa para um estado morto significando que não é possível executar novamente o método start() nem deste modo voltar a executar o método run(). Para re-executar uma thread uma nova java.lang.Thread tem de ser criada novamente e o método start() da nova instância tem de voltar a ser invocado.

package interface_runnable;public class OneThread implements Runnable {

private int i ;public OneThread(){

super();System.out.println("Uma instancia da classe OneThread foi

criada"+this.toString());i = 1;

}

public void run() {System.out.println("inicio do run() o i chegou com o valor"+i);i = 1;while (i < 50){

System.out.println("Estou a correr na thread: id:"+Thread.currentThread().getId() + " nome:" + Thread.currentThread().getName() + " contador:" + i);

i++;try {

Thread.sleep(30);} catch (InterruptedException e) {

31-07-2007 58/96

Apontamentos sobre Java 5.0 Luís Mendes

// TODO Auto-generated catch blocke.printStackTrace();

}}

}}

package interface_runnable;public class MainThread {

public static void main(String[] args){

System.out.println("Usando a interface runnable");Thread oneT = new Thread(new OneThread());Thread twoT = new Thread(new OneThread());System.out.println("oneT isAlive?:" + oneT.isAlive());

System.out.println("oneT.run directamente inicio");oneT.run();System.out.println("oneT.run directamente fim ");

oneT.start();twoT.start();

System.out.println("oneT isAlive?:" + oneT.isAlive());try {

oneT.join(); // fica à espera que a oneT termine} catch (InterruptedException e) {

// TODO Auto-generated catch blocke.printStackTrace();

}

System.out.println("oneT.run após o start directamente inicio");oneT.run(); // não é executado// oneT.start(); é lançada uma excepçãoSystem.out.println("oneT.run após o fim directamentefim ");

System.out.println("Fim da Main Thread");}

}

Quando uma thread é criada os valores por omissão para a sua prioridade de execução e o tipo de thread são iguais aos valores da thread que a criou.

Métodos mais importantes da Classe Thread

Nome Descrição

void run() O método run() de uma thread pode ser invocado livremente

31-07-2007 59/96

Apontamentos sobre Java 5.0 Luís Mendes

antes do método start ser executado. Neste caso, não é criada nenhum thread apenas o código do run() é executado pela thread que o chamou.

void static() O método start() de uma thread não pode ser executado mais do que uma vez durante o tempo de vida de uma instância de java.lang.Thread.

Static Thread currentThread() Devolve a referência para a thread que a máquina virtual está a

executar no momento da invocação de currentThread().

static void yield() A thread que executa este método deixa de executar instruções e devolve o cpu às outras threads.

public static void sleep(long millis) A thread que executa este método deixa de executar só

voltando a ser executada pelos menos após a passagem dos milissegundos indicados.

public final boolean isAlive() Um valor true indica que o método start() da thread foi

executado e que o método run() ainda não terminou a sua execução, isto é, a thread não está no estado morto.

public final void setPriority(int newPriority)

Define a prioridade da thread relativamente às outras. Quanto maior for a prioridade maior precedência terá para ser aquela escolhida pela máquina virtual. A prioridade é um valor de 1 a 10 com 10 a ser o valor para a maior prioridade. Por omissão a prioridade das threads é 5.

public final void setName(String name) Define o nome da thread.

public final void join() Permite que uma thread espere pelo fim de outra. A thread que

quer esperar invoca o método join() da java.lang.Thread que deverá terminar. A thread invocante do método aguarda (bloqueia) que a thread invocada termine a execução do método run() e que passe para o estado morto. Este método é perigoso por o seu uso permite que duas threads possam aguardar que a outra thread termine que executar provocando um impasse na execução do processo:é o chamado deadlock.

public final void setDaemon(boolean on) Configura a thread para que seja do tipo daemon. Este método

não pode ser executado depois da invocação do método start().

public long getId() Identificador da thread. É um valor inteiro positivo.

31-07-2007 60/96

Apontamentos sobre Java 5.0 Luís Mendes

Os objectos são monitores

Cada objecto Java é um monitor isto é, tem um lock interno que permite ao programador impor às threads que acedem simultaneamente a um objecto exclusão mutua no momento da execução dos seus métodos, mesmo que as threads queiram executar métodos distintos de um objecto.

Os métodos de um objecto que devem ser executados somente por uma thread num dado momento são declarados com o modificador synchronized. O algoritmo é simples, uma thread que queira executar um método synchronized só inicia a sua execução se o lock do objecto estiver livre, tomando-o para si ao mesmo tempo e liberta o lock ao executar a última de linha do método synchronized. Enquanto o lock estiver tomado por uma thread mais nenhuma pode iniciar a execução de métodos synchronized do objecto mesmo que seja outro método que não aquele executado pela thread que possui o lock. Estas thread supendem a sua execução e ficam a aguardar que o lock do objecto seja libertado.

Da descrição anterior dos monitores pode-se concluir que há um lock por objecto mas não há um por método e que os métodos não synchronized podem ser acedidos por qualquer thread independentemente se possui o lock do objecto.

Os construtores das classes não podem ser declarados como synchronized.

Para além dos métodos também é possível reduzir a zona de exclusão a um pedaço de código usando a seguinte instrução java:

synchronized(<referência para o objecto que será necessário obter o lock>){

// código a serem executadas em exclusão mútua;}

A referência para o objecto pode ser this. Neste caso estarmos a excluir o acesso com base no lock do próprio objecto.

Métodos estáticos de uma classe também podem ser sincronizados. Neste caso como os métodos estáticos não tem um objecto associado mas uma classe, o lock é realizado implicitamente sobre o objecto classe.Class.

package sincronizacao;// cada classe tem um lock por só// pode ser obtido por uma thread de cada vez.// O lock é adquirido no inicio da execução de um // método synchronized e libertado no fimpublic class Contador {

31-07-2007 61/96

Apontamentos sobre Java 5.0 Luís Mendes

private int count;// os construtores não podem ser synchronizedpublic Contador(){

count = 0;}

// só uma thread de cada vez pode executar integralmente este métodopublic synchronized void inc(){

System.out.println(System.currentTimeMillis() + " : inicio inc():"+Thread.currentThread().getId());

try {System.out.println(System.currentTimeMillis() + " : Vai

dormir"+Thread.currentThread().getId());Thread.sleep(10*1000);

} catch (InterruptedException e) {e.printStackTrace();

} // 10 segSystem.out.println(System.currentTimeMillis() + " : Terminou de

dormir"+Thread.currentThread().getId());count++;System.out.println(System.currentTimeMillis() + " fim

inc():"+Thread.currentThread().getId());}

// qualquer thread pode executar este método em qualquer momento e em // simultâneo com outra

public void dec(){

System.out.println(System.currentTimeMillis() + " + inicio dec():"+Thread.currentThread().getId());

synchronized(this){

count--;}System.out.println(System.currentTimeMillis() + " + fim

dec():"+Thread.currentThread().getId());}

// só uma thread de cada vez pode executar integralmente este métodopublic synchronized int getCount(){

return count;}

// qualquer thread pode executar este método em qualquer momento e em // simultâneo com outra

public int getCountnotSync(){

return count;}

}

31-07-2007 62/96

Apontamentos sobre Java 5.0 Luís Mendes

Sincronização entre threads

Os métodos públicos wait(), notify() ou notifyAll() definidos pela classe Object só são passíveis de serem usados dentro da zona sincronizada de um monitor.

Ao ser executado o método wait() na zona de código sincronizado a thread que o invocou, isto é, a thread que possui o lock do monitor, sofre imediatamente as seguintes alterações :

● liberta o CPU, deixando de executar;● liberta o lock do monitor;● Vai para a lista de espera do monitor que contém as threads que executaram o

método wait (está lista é diferente da lista de espera para aceder aos métodos do monitor);

Ao ser executado o método notify() na zona de código sincronizado pela thread com o lock do monitor, o monitor irá aleatoriamente retirar uma thread da lista de espera das threads que invocaram o método wait() do monitor mas não cederá o seu tempo de CPU à thread retirada da lista. Esta, quando chegar o momento oportuno, terá de lutar com as outras threads do processo pela posse do lock do monitor, para que assim que o obtiver, retomar a execução na instrução a seguir ao método wait() anteriormente executado na zona de código sincronizado.

O método notifyAll() funciona de forma semelhante ao método notify(). A diferença reside no factor de notifyAll() retirar da lista de espera do método wait() não uma thread mas todas as threads. Posteriormente estas threads terão de lutar pela posse do lock do monitor para retomarem a sua execução.

Padrões de uso do Wait() e notify()

Normalmente o método wait() é invocado se determinadas condições no estado do monitor não são satisfeitas. Para garantir que o monitor só avança quando as condições são efectivamente verdadeiras o wait() deve estar dentro de um ciclo da seguinte forma.

while (! condições)wait();

Também é conveniente usar o método notifyAll() em vez do notify() para dar iguais chances a todas as threads de continuarem o seu trabalho. A ordem de execução das threads não é determinística.

package wait_notify;// Cada classe tem um único lock que

31-07-2007 63/96

Apontamentos sobre Java 5.0 Luís Mendes

// pode ser obtido por uma thread de cada vez.// O lock é adquirido no inicio da execução de um // método synchronized e libertado no fimpublic class Contador {

private int count;public synchronized static void printMsg(String txt){

System.out.println(System.currentTimeMillis() + " " + txt + " " + Thread.currentThread().getId());

}

// os construtores não podem ser sincronizedpublic Contador(){

count = 0;}

// só incrementa o campo count se menor que -1public synchronized void inc() {

printMsg(" : inicio inc():");

//while (count > -2)

try {wait(); // coloca a currentthread à espera que count

seja menor que -1printMsg("Foi recebido um notify. o Wait acabou");

} catch (InterruptedException e1) {// TODO Auto-generated catch blocke1.printStackTrace();

}System.out.println(System.currentTimeMillis() + " depois da

condição count > -2 ser válida:" + Thread.currentThread().getId());count++;System.out.println(System.currentTimeMillis() + " fim inc():" +

Thread.currentThread().getId());}

public void dec(){

System.out.println(System.currentTimeMillis() + " + inicio dec():" + Thread.currentThread().getId());

synchronized(this) // zona de código sincronizado com o lock da // instancia desta classe

{count--;notifyAll(); // notifica as outras threads

// que foi decrementado o valor.}System.out.println(System.currentTimeMillis() + " + fim dec():" +

Thread.currentThread().getId());

31-07-2007 64/96

Apontamentos sobre Java 5.0 Luís Mendes

}

public synchronized int getCount(){

return count;}

// método não sincronizado!public int getCountnotSync(){

return count;}

}

Deadloacks

Quando duas ou mais threads necessitam de executar os mesmos métodos de um classe mas por ordem inversa pode ocorrer deadlock, isto é, ambas ficam à espera de obter um lock que está na posse da outra classe.

Observações

Quanto uma thread possui o lock de um objecto mais nenhuma thread pode executar outro método sincronizado do objecto. Ista situação pode provocar “fome” às threads que estão à espera no caso de o lock demorar muito tempo a ser libertado.

Mesmo quando todos os métodos de um objecto são declarados como sinchronized uma thread pode detectar inconsistências na informação guardadas pelo monitor quando duas ou mais threads usam simultaneamente o mesmo objecto. Por exemplo, assumindo que os métodos do objecto rgb são todos sinchronized e que está a ser usado por duas threads o seguinte cenário pode acontecer,

[ThreadA] rgb.setRgb(“azul”, 23, 67, 89);[ThreadA] rgb.getName(); // ==> devolve azul[ThreadB] rgb.setRgb(“azul”, 45, 67, 89);[ThreadA] rgb.getRed(); // ==> devolve 45 e não 23 como se podia estar à // espera.

Para corrigir esta situação deve ser declarada uma zona de código sincronizado para a invocação dos métodos do objecto rgn, por exemplo:

[ThreadA] rgb.setRgb(“azul”, 23, 67, 89);[ThreadA] synchronized(rgb) {rgb.getName(); rgb.getRed(); } // ==> devolve 45 // e azul[ThreadB] rgb.setRgb(“azul”, 45, 67, 89); // com a zona sincronized é

31-07-2007 65/96

Apontamentos sobre Java 5.0 Luís Mendes

// garantido que este método só é // invocado após o fim da execução // dos métodos getName() e getRed()

31-07-2007 66/96

Apontamentos sobre Java 5.0 Luís Mendes

Capítulo 8 – Package Java.lang e java.util

ObjectTodas as classes Java directamente ou indirectamente derivam da classe java.lang.Object. Todas as classes Java são do tipo Object.

A classe Object disponibiliza os métodos de sincronização dos monitores wait(), notify() e notifyAll().

Também disponibiliza três métodos que todas as classes “devem” re-escrever de acordo com os seus campos e funcionamento. Note-se que estes métodos não são abstractos, pelo que têm uma implementação por omissão.

● public String toString(): Este método é usado quando se pretende obter a representação textual (String) de um objecto. Habitualmente apresenta os valores dos campos do objecto. A implementação por omissão da classe Object devolve o nome completo da classe e o valor hexadecimal do método hashCode().

String s = o.toString();System.out.format("oi : %s\n", new Object[] {o});System.out.println(o + " = " + s);

● public boolean equals(Object ob): compara se o objecto passado no parâmetro do método é igual ao objecto que executa o método. A igualdade deve ser vista tanto do ponto de vista do valor da referência dada como parâmetro, como também do ponto de vista dos valores de cada campo do objecto. Se o valor do parâmetro for null então os dois objectos não são iguais. Dois objectos são considerados iguais se:

■ ob==this ou ■ ob!=this and (this.properties[].equals(ob.properties[])

A implementação por omissão da classe Object só compara as referências entre os objectos.

public class object {private String p;public boolean equals(Object arg0) {

if (arg0 == null)return false;

if (arg0 == this) // comparar as referênciareturn true;

// verificar se a classe do parâmetro é do mesmo tipo que esta: objectif (!(arg0 instanceof object))

31-07-2007 67/96

Apontamentos sobre Java 5.0 Luís Mendes

return false;// converter a referência para esta classeobject o = (object) arg0;boolean isEqual;// para cada propriedade verificar se // ela é null nos dois objectos ou se os seus valores são equivalentes if (p==null)

isEqual = o.getP()==null;else

isEqual = p.equals(o.getP());

return isEqual; }

● public int hashCode(): Devolve um inteiro que identifica a classe numa hashtable. Dois objectos iguais segundo o método equals() têm de retornar o mesmo hashCode(). A implementação por omissão da classe Object devolve o número da referência de memória do objecto da classe. Se o método equals() for implementado então hashCode() também o deve ser, devendo computar o valor a partir de todas as propriedades da classe usadas na implementação do método equals().

public int hashCode() { int hash = 7;

hash = 31 * hash + (null == p ? 0 : p.hashCode());return hash;

}

● protected object clone() throws CloneNotSupportedException. Este método cria um novo objecto que é uma replica do objecto que invocou o método. No fim do método clone() o tipo e os valores dos campos do novo objecto são iguais ao objecto original e a invocação do método equals() aos dois objectos deve devolver um valor verdadeiro. A implementação por omissão de Object faz uma shallow copy de todos os campos, isto é, os valores dos campos de tipos primitivos são copiados integralmente e as referências dos campos de tipos objecto são copiadas também integralmente. Outro mecanismo de cópia terá de ser implementada pelo programador. Para que este método possa ser implementado por uma classe é necessário que ela implemente a interface sem métodos Cloneable pois caso contrário será lançada a excepção CloneNotSupportedException durante a execução do programa.

public class object implements Cloneable { // como o método clone() é protected, criou-se este método para // disponibilizar a operação de clonagem a outros objectos public object doClone()

31-07-2007 68/96

Apontamentos sobre Java 5.0 Luís Mendes

{object o=null;try {

o = (object)this.clone();} catch (CloneNotSupportedException e) {

e.printStackTrace();}return o;

}

// uso da clonagem shallow copyo.setI(2);o.setP(34);object o_clone = o.doClone();o_clone.setP(32);o_clone.setI(56);

System.out.println("o.getP=" + o.getP() + " o_clone.getP=" + o_clone.getP() +"o.getI=" + o.getI() + " o_clone.getI=" + o_clone.getI());

MathA classe Math disponibiliza diversos métodos estáticos para realizar operações matemáticas. Os mais importantes são:

Métodos Descrição

int abs(int), float abs(float) devolve o valor absoluto de um número

double ceil(double d) devolve o menor inteiro que não é menor que o valor decimal de d, isto é, o inteiro a seguir ao decimal

double floor(double d) devolve o maior inteiro que não é maior que o valor decimal de d, isto é, o inteiro a atrás do decimal

int round(float f) devolve o inteiro mais perto do valor do decimal

int max(int, int), float max(float, float) devolve o maior de dois números

int min(int, int), float min(float, float) devolve o menor de dois números

double sin(double), double cos(double). Double tan(double d)

funções trigonometrias

double random() devolve um número decimal aleatório entre 0.0 e 1.0

String, StringBuffer, StringBuilder

31-07-2007 69/96

Apontamentos sobre Java 5.0 Luís Mendes

Todos os literais do tipo strings presentes no código Java, ou seja, o texto que está entre aspas no código de um programa, são representadas pela máquina virtual como sendo instâncias da classe String.

A classe String tem a característica de ser imutável, isto é, o seu valor inicial não é alterado sob qualquer circunstância e todas as operações da classe que possam alterar o seu valor resultam na criação de outros objectos String com o resultado da operação.

Não sendo as Strings alteráveis, a máquina virtual coloca todos os literais string presentes no código das classes do programa numa pool de instâncias de objectos String, obtendo desta forma a garantia de que haverá apenas uma única instância String em memória independentemente do número de literais com o mesmo texto encontrados ao longo do programa.

// Os dois literais “SLB” são representados pela // mesma instância String na pool System.out.println("será igual?" + ("SLB" == "SLB")); //==> true

Para evitar um crescimento de memória é de evitar a criação de literais através dos construtor da classe String,

// Técnica a evitar. String s = new String(“ola”); // Deveria ser simplesmente s = “ola”;

porque este código cria duas instancias da classe String distintas mas com o mesmo valor: uma na pool de strings para o literal “ola” e outra para o objecto String criado através do operador new.

O operador + comporta-se de maneira diferente quando detecta que um argumento é do tipo String: Trata a operação como se fosse a concatenação de strings, convertendo inclusive os operandos que não são do tipo String numa representação textual.Os outros operadores aritméticos não podem usar operandos do tipo String.

A classe String implementa a interface CharSequence.

Os métodos da classe String mais importantes são:

// Uma String é um objecto imutável.// o operador + é especial quando encontra uma string transforma todos os // operandos em stringSystem.out.println(1 + "Benfica"); // ==> “1Benfica”

System.out.println("será igual?" + ("SLB" == "SLB")); // ==> true

String s = "ola";s = s.replace('a', 'e');

31-07-2007 70/96

Apontamentos sobre Java 5.0 Luís Mendes

System.out.println(s + " " + ("ola"==s)); // ==> ole false

System.out.println(s.charAt(2)); // ==> devolve o carácter e (começa em zero)System.out.println(s.indexOf('o')); // ==> 0 (zero)System.out.println(s.lastIndexOf('e')); // ==> 2System.out.println("benfica".compareTo("grande")); // ==> -5 , benfica é menor de grandeSystem.out.println("BENFICA".toLowerCase()); // ==> benficaSystem.out.println("benfica".toUpperCase()); // ==> BENFICASystem.out.println(" [benfica] ".trim()); // ==> [BENFICA]System.out.println(" Ola mundo ".endsWith("ndo ")); // ==> trueSystem.out.println(" Ola mundo ".substring(4)); // ==> " mundo"System.out.println(" Ola mundo ".concat("maravilhoso")); // ==> " Ola mundo maravilhoso"System.out.println(" Ola mundo ".contains("maravilhoso")); // ==> falseSystem.out.println(" Ola mundo ".length()); // ==> 11 System.out.println("".isEmpty()); // ==> trueSystem.out.println("ola:mundo".split(":").length); // ==> array de string com dois elementos ola e mundo

// formatar a stringSystem.out.println(String.format("%d ola %s e viva o %s", 1, "mundo", "benfica")); // ==> "1 ola mundo e viva o benfica"

// Métodos estáticos de conversões de tipos primitivos para stringSystem.out.println(String.valueOf(true)); // ==> trueSystem.out.println(String.valueOf('Y')); // ==> "y"System.out.println(String.valueOf(23)); // ==> "23"System.out.println(String.valueOf(234L)); // ==> "234"System.out.println(String.valueOf(56.0)); // ==> "56.0"System.out.println(String.valueOf(78233.7d)); // ==> "78233.7"

A classe StringBuffer permite definir strings mutáveis, isto é as operações realizadas afectam a sequência de caracteres que a classe guarda. A sequência de caracteres é guardada num buffer que têm uma capacidade inicial que é automaticamente aumentada se for necessário guardar mais caracteres ou pode ser reduzida a pedido do programador.

StringBuffer é uma classe thread-safe, ou seja os seus métodos são definidos como synchronized. Os métodos principais são o append() que adiciona uma string, inteiro, carácter, etc,. ao final do buffer e o insert() que insere uma string numa determinada posição do buffer.

StringBuffer sb = new StringBuffer();System.out.println(sb.capacity()); // ==> capacidade de por omissão é de 16System.out.println(sb.length()); // ==> 0

System.out.println(sb.append("ola").append("jeitosa").append(1).insert(3, " mundo ")); // ==> "ola mundo jeitosa1"

// é mutável por isso é possível apagarSystem.out.println(sb.deleteCharAt(sb.length()-1));// ==> "ola mundo jeitosa"

31-07-2007 71/96

Apontamentos sobre Java 5.0 Luís Mendes

System.out.println(sb.replace(0, 3, "OLA")); // ==> "OLA mundo jeitosa"

sb.setCharAt(sb.lastIndexOf("j"), 'J');System.out.println(sb); // ==> "OLA mundo Jeitosa"

System.out.println(sb.capacity()); // ==> mudou para 34

// diminuir o tamanhosb.setLength(3); System.out.println(sb); // ==> "OLA"

// aumentar o tamanhosb.setLength(50); System.out.println(sb); // ==> "OLA\0\0\0" encheu com o carácter nuloSystem.out.println(sb.capacity()); // ==> mudou para 70

A classe StringBuilder oferece os mesmos métodos que a classe StringBuffer só que não é thread-safe tornando-se mais rápida nos casos em que o buffer só é acedido por uma única thread. A classe StringBuilder só está disponível na versão 5.0 da linguagem Java.

As classes Wrapper

As classes wrapper representam os tipos primitivos Java. As classe são imutáveis, isto é, o valor que guardam não pode ser alterado depois de serem construídas, sendo o valor indicado no construtor do objecto.

Estas classes são úteis quando é necessário usar tipos primitivos em estruturas de dados que só aceitam objectos, classes descendentes de Object, como unidade mínima de trabalho. Exemplos de essas estruturas de dados são as listas, map ou sets. Outro uso para as classes wrapper em alternativa aos tipos primitivos é quando é necessário guardar valores que são opcionais. Neste caso as classes wrapper apresentam a vantagem de poderem guardar o “valor” null podendo significar que não têm valor.

A relação entre os tipos primitivos e as classes wrapper são:

Tipo primitivo Classe wrapper

(java.lang)

Classe Pai

boolean Boolean Object

byte Byte Number

char Character Object

short Short Number

int Integer Number

31-07-2007 72/96

Apontamentos sobre Java 5.0 Luís Mendes

long Long Number

float Float Number

double Double Number

As classes Byte, Short, Integer, Float e Double descendem da mesma classe Number e apresentam mais ou menos os mesmos métodos. A seguir mostra-se alguns métodos da classe Integer,

int i = 0;// pode-se construir a partir de inteirosInteger inte = new Integer(34);System.out.println(i++ + ": " + inte.intValue()); // ==> 34

// pode-se construir a partir de inteiros em stringsInteger intestr = new Integer("34");System.out.println(i++ + ": " + intestr.doubleValue()); // ==> 3443.0

// igualdade de valoresSystem.out.println(i++ + ": " + intestr.equals(inte)); // ==> true

//Integer intestr1 = new Integer("34432lç"); // ==> devolve a excepção runtime java.lang.NumberFormatException: //System.out.println(i++ + ": " + intestr1.intValue());

// conversões directas para Integer (factory methods) devolve uma nova instanciaSystem.out.println(i++ + ": " + Integer.valueOf(344500).shortValue()); // ==> 16820 há uma redução na dimensão do novo tipo short System.out.println(i++ + ": " + Integer.valueOf("344500").shortValue()); // ==> 16820 há uma redução na dimensão do novo tipo short. pode devolver a excepção de runtime

// conversões directas de String para int (valor primitivo)System.out.println(i++ + ": " + Integer.parseInt(new StringBuilder("344500").toString())); // ==> 344500 . pode devolver a excepção de runtime

// converter para stringString s = Integer.valueOf("233").toString();System.out.println(i++ + ": " + s); // ==> 233

// converter para string um inteiro primitivoSystem.out.println(i++ + ": " + Integer.toBinaryString(233)); // ==> 11101001System.out.println(i++ + ": " + Integer.toString(233)); // ==> 233System.out.println(i++ + ": " + Integer.toHexString(233)); // ==> 11101001

// Ler Integers valores de prioridades de sistemas (em ficheiros de propriedades) System.out.println(i++ + ": " + Integer.getInteger("ola mundo")); // ==> null por que não existe a propriedade ola mundo

31-07-2007 73/96

Apontamentos sobre Java 5.0 Luís Mendes

// comparar entre dois Integers decode converte uma string de um número numa // base diferente da decimalSystem.out.println(i++ + ": " + Integer.decode("0xABC").compareTo(Integer.valueOf(67))); // ==> 1 significa que 0xABC > 67

// new Integer("0xABC"); // ==> dá uma excepção 0xABC", deve-se usar o método decode

A classe Boolean tem menos métodos que as classes numéricas. Para a classe Boolean todas as strings que sejam diferente de “true” (independentemente de letras maiúsculas ou minúsculas) são convertidas para o valor booleano false.

int i = 0;// pode-se construir a partir de booleanos primitivosBoolean inte = new Boolean(false);System.out.println(i++ + ": " + inte.toString()); // ==> true

// pode-se construir a partir de strings (case insensitive)Boolean intestr = new Boolean("tRUE");System.out.println(i++ + ": " + intestr.booleanValue()); // ==> true

// uma string que não seja true devolve sempre falso. NÃO DEVOLVE NENHUMA EXCEPÇÂOBoolean intestr1 = new Boolean("tRUEdsds");System.out.println(i++ + ": " + intestr1.booleanValue()); // ==> false

// igualdade de valoresSystem.out.println(i++ + ": " + intestr.equals(Boolean.TRUE)); // ==> true

// conversões directas para Boolean (factory methods) devolve uma nova instanciaSystem.out.println(i++ + ": " + Boolean.valueOf(false).booleanValue()); // ==> false System.out.println(i++ + ": " + Boolean.valueOf("sapato").booleanValue()); // ==> true

// conversões directas de String para boolean (valor primitivo)System.out.println(i++ + ": " + Boolean.parseBoolean("TRUE")); // ==> true

// comparaçõesSystem.out.println(i++ + ": " + Boolean.TRUE.compareTo(Boolean.FALSE)); // ==> 1 que o valor a comparar era positivo e o valor indica era falso. No caso contrário o resultado seria -1

// toStringSystem.out.println(i++ + ": " + Boolean.toString(Boolean.parseBoolean("oi"))); // ==> "false"

A classe Character representa um carácter na tabela Unicode. Cada carácter é guardado num inteiro numa representação de 16 bits por carácter.

31-07-2007 74/96

Apontamentos sobre Java 5.0 Luís Mendes

Autoboxing e autounboxing

Uma vez que as classes wrapper são imutáveis para realizar operações aritméticas com os valores que elas guardam é necessário criar um novo objecto para receber o resultado e usar os métodos XXXValue() para extrair o valor que guardam. Este cálculo pode ser um tanto cansativo pelo que a partir da versão 5 o compilador da linguagem Java cria automaticamente a classe wrapper para um valor primitivo (Boxing) e extrai automaticamente o valor da classe wrapper (Unboxing) quando são usados em contextos que necessitem de ser convertidos.

// Autoboxing: Conversão automática de tipos primitivos // para classes wrapper e vice-versa

// Boxing: conversão de tipo primitivo para class wrapperBoolean b = true; // equivale a new Boolean(true)Character c = 'u'; // equivale a new Character('u')Byte by = 7; // equivale a new Byte (7)Short s = 78; // equivale a new Short(78)Integer i = 67; // equivale a new Integer(67)Long l = 67678L; // equivale a new Long(67678L)Float f = 672.0f; // equivale a new Float(672.0f)Double d = 672.0d; // equivale a new Double(672.0d)

// Double d1 = 32; erro, o tipo primitivo tem de corresponder à classe wrapper// Double d1 = 23f; erro,o tipo primitivo tem de corresponder à classe wrapper

// Unboxing: conversão automática de wrapper para inteiroboolean p_b = b; // equivale a b.booleanValue();char p_c = c; // equivale a c.charValue();byte p_by = by; // equivale a by.byteValue();short p_s = s; // equivale a s.shortValue();int p_i = i; // equivale a i.integerValue();long p_l = l; // equivale a l.longValue();float p_f = f; // equivale a f.floatValue();double p_d = d; // equivale a d.doubleValue();// Com o autoboxing é possível combinar tipo // primitivos e wrappers na mesma operação.Long res = by + p_i + l; // equivale a new Long(by.byteValue() + p_i + // l.longValue())

double p_res = d + p_f + l; // equivale a d.doubleValue() + p_f + // l.longValue();

Contudo o boxing e unboxing pode provocar ineficiências como ilustra o seguinte caso,

l++; // implica a criação de um novo objecto corresponde ao seguinte código // l = new Long(l.longValue()++)

Por motivos de eficiência as classes wrapper nunca devem ser variáveis dos ciclo for, while ou do while.

31-07-2007 75/96

Apontamentos sobre Java 5.0 Luís Mendes

Colecções (Java Collection Framework)

As colecções são estruturas de dados que guardam referências para objectos (não guardam tipos primitivos) cuja dimensão não é conhecida na altura de criação, o que é uma característica que contrapõe a obrigatoriedade da especificação da dimensão dos arrays quando estes são criados.

As colecções presentes na Java Collection Framework são de três tipos:● conjunto : permite guardar N objectos mas não os mantem numa ordem

especifica.● lista : permite agrupar N objectos onde cada objecto tem uma posição especifica

na lista.● map : o map ou array associativo permite guardar um par de objectos onde um é

denominado “chave” e o segundo “valor”. No momento da pesquisa é possível obter o objecto “valor” que corresponde ao objecto “chave”

A ideia dos designeres da Java Collection Framework foi o de separar a especificação das operações de cada tipo de colecção da implementação dessas operações. Para tal, as operações disponibilizadas por um dado tipo de colecção são definidas por uma interface e as classes implementadoras de um determinado tipo de colecção obrigatoriamente terá de implementar a interface associada. Com esta separação é possível obter implementações com características diferentes para o mesmo tipo de colecção, características essas que podem passar pela memória ocupada, velocidade de execução ou outras nuances como permitir o armazenamento de objectos repetidos.

De acordo com as boas práticas de uso da Java Collection Framework os programadores devem criar as colecções a partir das suas interfaces em vez das duas classes, porque deste modo torna-se mais fácil mais tarde alterar as características da implementação sem ter de alterar extensivamente o programa original. As operações das colecções não são thread-safe, isto é, os métodos não são synchronized.

As implementações das colecções quando necessitam de comparar objectos recorrem sempre ao método equals() e nunca ao operador ==. Deste modo é conveniente que as classes cujos objectos estejam previstos serem guardados em colecções implementem correctamente o método equals().

31-07-2007 76/96

Apontamentos sobre Java 5.0 Luís Mendes

As interfaces das colecções disponíveis pela Java Collection Framework e as suas relações estão ilustradas na seguinte figura :

Collection A interface Collection define as operações básicas que uma colecção que guarda N objectos deverá ter para inserir, apagar e extrair informação.

Método Descrição

add(Object) Adiciona um objecto à colecção

addAll(Collection) Adiciona à colecção todos os objectos presentes na colecção indicada pelo parâmetro.

clear() Remove todo os objectos da colecção

contains(Object) Indica se o objecto está presente na colecção

containsAll(Collection) Indica se na colecção estão todos os objectos da colecção passada como parâmetro

isEmpty() Indica se a colecção não tem elementos

size() Devolve quantos elementos tem a colecção

remove(Object) Remove da colecção o objecto indicado.

removeAll(Collection) Remove da colecção os objecto que estão

31-07-2007 77/96

Java.util.Collection

Java.util.Set Java.util.List

Java.util.Map

Java.util.Iterable

Apontamentos sobre Java 5.0 Luís Mendes

na colecção passada como parâmetro

retainAll(Collection) Remove da colecção todos os objecto excepto os elementos que estão na colecção passada como parâmetro

object[] toArray() Devolve num array do tipo Object todos os elementos da colecção.

T[] toArray(T[]) Devolve num array do tipo indicado pelo array passado como parâmetro todos os elementos da colecção, por exemplo para obter os elementos do tipo num array de Strings deve-se fazer: String[] y = x.toArray(new String[0]);

A interface Collection não fornece nenhum método para aceder aos elementos guardados na colecção. O acesso aos objectos guardados numa colecção é dado ao programador através de um objecto autónomo da colecção chamado Iterator. Um iterador é obtido através do método Collection.Iterator() permitindo percorrer a colecção num único sentido. A classe Iterator disponibiliza três métodos:

● boolean hasNext() : indica se há mais elementos na colecção que podem ser visitados

● Object next() : Devolve o próximo elemento da colecção que deve ser iterado.● void remove() : remove da colecção o último elemento iterado.

O padrão típico de uso é,

Collection c = ...;Iterator i = c.iterator();while (i.hasNext())

Object o = i.next();

A partir da versão 5 da linguagem Java é possível percorrer uma colecção sem recorrer ao iterador usando apenas o ciclo for,Collection c = ...;for (Object o: c){ ...}

A interface Collection não tem classes implementadoras, só as suas sub-interfaces.

31-07-2007 78/96

Apontamentos sobre Java 5.0 Luís Mendes

List

É uma colecção que oferece a semântica de um array uni-dimensional dinâmico que cresce e diminui quando os elementos são retiradas ou colocados na lista. Tal como num array cada objecto ocupa um índice na lista. Os valores dos índices variam entre [0, size()-1]. Quando é inserido um elemento a meio da lista os índices à direita aumentam em uma unidade. Quando é removido um elemento do meio da lista os índices à direita diminuem uma unidade. Só se pode adicionar um elemento até à cauda da lista, isto é, com o índice igual a list.size().

Acrescenta à interface Collection os seguintes métodos,

Método Descrição

void Add(int index, Object) Adiciona um elemento à lista e coloca-o na posição indicada (não substituí o elemento que ocupava esse índice ele é movido para a direita)

Object get(int index) Devolve o objecto que ocupa na lista a posição indicada

Int indexOf(Object) Devolve a posição na lista onde o objecto indicado como parâmetro está guardado

Object remove(int index) Remove da lista o objecto que está na posição indicada.

Object set(int index, Object) Substitui na lista o elemento guardado no índice indicado.

ListIterator listIterator() Devolve um iterador para a lista permitindo percorrer a lista nos dois sentidos: ascendente e descendente.

As classes da Java Collection Framework que implementam esta interface são:● ArrayList : os elementos são guardados num array que é redimensionado quando

fôr necessário. Pode guardar valores null. Tem tempos de execução constantes para quase todos os métodos da interface List.

● LinkedList : os elementos são guardados numa lista ligada. Pode guardar valores null. As operações da interface List que funcionam com base no índice de um objecto necessitam de percorrer a lista ligada resultando, por isso, em tempos de

31-07-2007 79/96

Apontamentos sobre Java 5.0 Luís Mendes

execução não constantes. Esta classe oferece através de métodos próprios a capacidade de funcionar como uma fila do tipo First In First Out (FIFO).

List<Integer> list = new LinkedList<Integer>(); // também podia ser new ArrayList<Integer>();

System.out.println(++i + ": " + list.isEmpty()); // ==> truelist.add(3); // insere no final da lista. // automáticamente há autoboxing ==> list.add(new Integer(3)) list.add(5); // insere no final da listaSystem.out.println(++i + ": " + list.get(1)); // ==> 5list.add(0,9); // foi inserido um terceiro elemento na posição 0System.out.println(++i + ": " + list.get(0)); // ==> 9System.out.println(++i + ": " +list.size()); // ==> 3list.add(5); // LinkedList permite elementos repetidos. a comparação é feita // através do método equal()System.out.println(++i + ": " +list.size()); // ==> 4

// operação de remoção. Quando se remove um elemento, // os que estão à esquerda ocupam o seu índiceSystem.out.println(++i + ": " + list.get(1)); // ==> 3list.remove(new Integer(3)); // remover o elemento através do objecto e não // através da posição que ocupa na lista, neste // caso a posição dois. é usado o método equals() // para pesquisar o elemento na listaSystem.out.println(++i + ": " + list.get(1)); // ==> 5System.out.println(++i + ": " +list.size()); // ==> 3

// Substitui o último elemento da lista. Não é adicionado nenhum elemento list.set(list.size()-1, 789);System.out.println(++i + ": " +list.size()); // ==> 3System.out.println(++i + ": " + list.get(list.size()-1)); // ==> 789

// Verifica se um objecto está contido na lista. É usado o método equals() // para comparar os elementosSystem.out.println(++i + ": " + list.contains(789)); // ==> true

// Devolver todos os elementos da lista num arraySystem.out.println("Conteúdo da lista para um array");Integer[] ints = list.toArray(new Integer[0]);for (Integer o: ints){

System.out.println(++i + ": " +o);}

// Percorrer a lista usando o iteradorSystem.out.println("percorrer todos os elemento usando o iterador");Iterator<Integer> iter = list.iterator();while (iter.hasNext())

System.out.println(++i + ": " +iter.next());

// percorrer a lista usando o ciclo for System.out.println("percorrer todos os elemento usando o ciclo for");for (Integer in: list)

System.out.println(++i + ": " + in);

31-07-2007 80/96

Apontamentos sobre Java 5.0 Luís Mendes

// Percorrer ao contrário a lista usando o ListIteratorSystem.out.println("percorrer ao contrário a lista usando o ListIterator");ListIterator<Integer> listIter = list.listIterator(list.size()); // coloca o iterado depois do último elemento da listawhile (listIter.hasPrevious())

System.out.println(++i + ": Elemento no index: " + listIter.previousIndex() + " : " + listIter.previous());

list.add(null); // pode ser adicionado uma referência null// toString() elementSystem.out.println(++i + ": toString(): " + list); // ==> [9, 5, 789, null] // list.toString() , chama // o método toString() de // cada um dos elementos da // lista

// operações com listasSystem.out.println(++i + " " + list.containsAll(list.subList(0, 1))); //==> true

Set

É um conjunto de objectos onde os elementos não são armazenados por nenhuma ordem específica. Não permite a inserção de elementos repetidos. Um elemento do conjunto é identificado como estado repetido através do método equals(). Um elemento só pode ser inserido ou removido ao conjunto, ele não pode ser substituído como acontece nas listas.

Esta interface da Java Collection Framework não acrescenta nenhum método à interface Collection.

As implementações disponíveis da interface Set são:● java.util.HashSet: Os tempos de execução dos métodos são constantes. Não

garante a ordem dos elementos entre diferentes iterações. Aceita elementos null.

● java.util.LinkedHashSet: Os tempos de execução dos métodos são constantes. Garante que a ordem dos elementos entre diferentes iterações é igual à ordem de inserção dos elementos no Set. Aceita elementos null.

● java.util.TreeSet: Os tempos de execução dos métodos são logarítmicos. Não aceita elementos null. Garante que a ordem dos elementos entre diferentes iterações é igual à ordem natural dos elementos, isto é, não é a ordem de inserção mas a ordem indicada pelo método int compareTo(Object) da interface Comparable. A classe dos elementos que serão colocados no TreeSet têm de implementar a interface java.lang.Comparable. Os objecto da package java.lang implementão esta interface, assim, uma instância de TreeSet que armazene Integers irá ordenar os inteiros por ordem crescente.

31-07-2007 81/96

Apontamentos sobre Java 5.0 Luís Mendes

Set<Integer> set = new HashSet<Integer>(); System.out.println(++i + ": " + set.isEmpty()); // ==> trueset.add(3); // insere no conjunto. Há autoboxing ==> set.add(new Integer(3)) set.add(3); // Não insere porque é repetido set.add(9); // insere no final do conjuntoset.add(null);System.out.println(++i + ": " + set.size()); // ==> 3 (só três e não quatro)System.out.println(++i + ": " + set.contains(null)); // ==> trueset.remove(null);set.add(9892); // insere no final do conjuntoset.add(344); // insere no final do conjunto

Integer[] res = set.toArray(new Integer[0]);System.out.println(++i + ": " + res.length); // ==> 4

// O percorrer os elementos dá uma ordem completamente diferente da // ordem de inserção dos objectosSystem.out.println("percorrer os elementos");for (Integer u: set) // 9892, 3, 9, 344

System.out.println(++i + ": " + u); // ==> 4

System.out.println(++i + ": toString() " + set); // ==> [9892, 3, 9, 344] diferente da ordem de inserção

Set<Integer> set = new LinkedHashSet<Integer>(); System.out.println(++i + ": " + set.isEmpty()); // ==> trueset.add(3); // insere no conjunto. Há autoboxing ==> set.add(new Integer(3)) set.add(3); // Não insere porque é repetido set.add(9); // insere no final do conjuntoset.add(null);System.out.println(++i + ": " + set.size()); // ==> 3, só trêsSystem.out.println(++i + ": " + set.contains(null)); // ==> trueset.remove(null);set.add(9892); // insere no final do conjuntoset.add(344); // insere no final do conjunto

Integer[] res = set.toArray(new Integer[0]);System.out.println(++i + ": " + res.length); // ==> 4

// O percorrer os elementos dá uma ordem igual à ordem // de inserção dos objectosSystem.out.println("percorrer os elementos");for (Integer u: set) // 3, 9, 9892, 344

System.out.println(++i + ": " + u); // ==> 4

System.out.println(++i + ": toString() " + set); // ==> [3, 9, 9892, 344] é igual à ordem de inserção

Set<Integer> set = new TreeSet<Integer>();

31-07-2007 82/96

Apontamentos sobre Java 5.0 Luís Mendes

System.out.println(++i + ": " + set.isEmpty()); // ==> trueset.add(3); // insere no conjunto. Há autoboxing ==> set.add(new Integer(3)) set.add(3); // Não insere porque é repetido set.add(9); // insere no final do conjunto// set.add(null); Não aceita nullsSystem.out.println(++i + ": " + set.size()); // ==> 3, só trêsSystem.out.println(++i + ": " + set.contains(9)); // ==> true

// set.remove(null); não aceita nullsset.add(9892); // insere no conjuntoset.add(344); // insere no conjunto

Integer[] res = set.toArray(new Integer[0]);System.out.println(++i + ": " + res.length); // ==> 4

// Devolve os elementos do conjunto numa ordem completamente diferente da ordem de inserção. Neste caso, devolve os elementos por ordem numérica crescente.System.out.println("percorrer os elementos");for (Integer u: set) // 3, 9, 344, 9892

System.out.println(++i + ": " + u);

System.out.println(++i + ": toString() " + set); // ==> [3, 9, 344, 9892] ordenação númerica

Map

Uma colecção do tipo Map é um dicionário ou também pode ser chamado de array associativo. É uma estrutura de dados que armazena pares de objectos na forma de [chave, valor], sendo habitualmente usada para devolver um valor a partir de uma chave. Uma colecção Map não aceita duas chave iguais e o valor só pode ser um objecto. É possível percorrer a colecção dos valores, a colecção das chaves ou a colecção dos pares (chave, valor).

Implementações:● java.util.HashMap : As execuções dos métodos têm um tempo constante.

Permite armazenar chaves e valores null. As colecções de valores, chaves ou dos pares (chaves, valores) devolvidas por HashMap não seguem nenhuma ordem específica.

● java.util.LinkedHashMap : As execuções dos métodos têm um tempo constante. Permite armazenar chaves e valores null. As colecções de valores, chaves ou dos pares (chaves, valores) são devolvidas de acordo com a ordem de inserção dos elementos no Map.

● java.util.TreeMap : As execuções dos métodos têm um tempo logarítmico. Permite armazenar valores null mas não chaves null. As colecções de valores, chaves ou dos pares (chaves, valores) são devolvidas de acordo com a ordem definida pelo método java.lang.Comparable.int compareTo(Object) da classe usada como chave do Map.

31-07-2007 83/96

Apontamentos sobre Java 5.0 Luís Mendes

A interface Map não estende a interface Collection, na verdade não estende nenhuma interface.

Map<String, Integer> notas = new HashMap<String, Integer>(); System.out.println(++i + ": " + notas.isEmpty()); // ==> truenotas.put("lmen", 20);notas.put("ze", 8);notas.put("joao", 23);notas.put(null, 233); // é aceite notas.put("maria", null); // é aceitenotas.put("ze", 10); // ==> substitui o valor da chave "ze"System.out.println(++i + ": " + notas.size()); // ==> 5

// método toString(), os pares que são escritos na string não seguem // nenhuma ordem em particularSystem.out.println(++i + ": " + notas); // ==> toString() {null=233, joao=23, // lmen=20, ze=10, maria=null}

System.out.println(++i + ": " + notas.get("lmen")); // devolve 20 é usado o // método equals() para // encontrar a chave no // Map

notas.remove("maria");System.out.println(++i + ": " + notas.size()); // ==> 4

// percorrer todas as chavesSystem.out.println("todas as chaves");Set<String> nomes = notas.keySet();for (String s: nomes)

System.out.println(++i + ": " + s); // ==> "null", "joao", "lmen", "ze" // as chaves são percorridas por uma ordem aleatória // nomes.add("oi"); não é permitido adicionar elementos ao keySet

// percorrer todos os valoresSystem.out.println("todas os valores");Collection<Integer> valores_notas = notas.values(); // devolve uma colecção e // não um setfor (Integer val: valores_notas)

System.out.println(++i + ": " + val); // ==> 233, 23, 20, 10 // Os valores são percorridos por // uma ordem aleatória

// valores_notas.add(43); não é permitido adicionar elementos à colecção // valores_notas

// obter todos os pares (chave, valor)System.out.println("todos os pares chave, valor");Set<Entry<String, Integer>> set_do_map = notas.entrySet();for (Entry<String, Integer> e: set_do_map)

System.out.println(++i + ": [" + e.getKey() + ", " + e.getValue() + "]"); // ==> [null, 233], [joao, 23], [lmen, 20], [ze, 10] // são devolvidos por ordem aleatória

31-07-2007 84/96

Apontamentos sobre Java 5.0 Luís Mendes

Map<String, Integer> notas = new LinkedHashMap<String, Integer>(); System.out.println(++i + ": " + notas.isEmpty()); // ==> truenotas.put("lmen", 20);notas.put("ze", 8);notas.put("joao", 23);notas.put(null, 233); // é aceite notas.put("maria", null); // é aceitenotas.put("ze", 10); // ==> substitui o valor da chave "ze"System.out.println(++i + ": " + notas.size()); // ==> 5

// método toString(), os pares que são escritos na string seguem // a ordem de inserçãoSystem.out.println(++i + ": " + notas); // ==> toString() {lmen=20, ze=10, joao=23, null=233, maria=null}

System.out.println(++i + ": " + notas.get("lmen")); // devolve 20 é usado o método equals() para encontrar a chave

notas.remove("maria");System.out.println(++i + ": " + notas.size()); // ==> 4

// percorrer todas as chavesSystem.out.println("todas as chaves");Set<String> nomes = notas.keySet();for (String s: nomes)

System.out.println(++i + ": " + s); // ==> "lmen", "ze", "joao", "null" // KeySet devolve as chaves pela sua // ordem da inserção no Map // nomes.add("oi"); não é permitido

// percorrer todos os valoresSystem.out.println("todas os valores");Collection<Integer> valores_notas = notas.values(); // devolve uma colecção e não um setfor (Integer val: valores_notas)

System.out.println(++i + ": " + val); // ==> 20, 10, 23, 233 // values devolve os valores por // ordem de inserção

// valores_notas.add(43); não é permitido

// obter todos os pares (chave, valor)System.out.println("todos os pares chave, valor");Set<Entry<String, Integer>> set_do_map = notas.entrySet();for (Entry<String, Integer> e: set_do_map)

System.out.println(++i + ": [" + e.getKey() + ", " + e.getValue() + "]"); // --> [lmen, 20], [ze, 10], [joao, 23], [null, 233] // entrySet devolve os pares do Map por ordem de inserção

Map<String, Integer> notas = new TreeMap<String, Integer>();

31-07-2007 85/96

Apontamentos sobre Java 5.0 Luís Mendes

System.out.println(++i + ": " + notas.isEmpty()); // ==> truenotas.put("lmen", 20);notas.put("ze", 8);notas.put("joao", 23);// notas.put(null, 233); // NÃO é aceite notas.put("maria", null); // é aceitenotas.put("ze", 10); // ==> substitui o valor da chave "ze"System.out.println(++i + ": " + notas.size()); // ==> 5

// To StringSystem.out.println(++i + ": " + notas); // ==> toString() {joao=23, lmen=20, maria=null, ze=10}

System.out.println(++i + ": " + notas.get("lmen")); // devolve 20 é usado o método equals() para encontrar a chave do Map

notas.remove("maria");System.out.println(++i + ": " + notas.size()); // ==> 4

// percorrer todas as chavesSystem.out.println("todas as chaves");Set<String> nomes = notas.keySet();for (String s: nomes)

System.out.println(++i + ": " + s); // ==> "joao", "lmen", "ze" // é devolvido por ordem alfabética // nomes.add("oi"); não é permitido

// percorrer todos os valoresSystem.out.println("todas os valores");Collection<Integer> valores_notas = notas.values(); // devolve uma colecção e não um setfor (Integer val: valores_notas)

System.out.println(++i + ": " + val); // ==> 23, 20, 10 // é devolvido por ordem // alfabética das chaves

// valores_notas.add(43); não é permitido

// obter todos os pares (chave, valor)System.out.println("todos os pares chave, valor");Set<Entry<String, Integer>> set_do_map = notas.entrySet();for (Entry<String, Integer> e: set_do_map)

System.out.println(++i + ": [" + e.getKey() + ", " + e.getValue() + "]"); // --> [joao, 23], [lmen, 20], [ze, 10] // é devolvido por ordem alfabética das chaves.

As classes que implementam as interfaces da Java Collection Framework podem oferecer outros métodos para além dos definidos nas interfaces, contudo, o seu uso não permite separar a especificação da colecção com a sua implementação o que pode dificultar a manutenção do código a longo prazo.

31-07-2007 86/96

Apontamentos sobre Java 5.0 Luís Mendes

Processamento de texto

Por vezes temos uma string que contem informação separada por um carácter por exemplo, uma lista de números separados por ponto e virgula. Para processar a string afim de extrair a informação entre os separadores existe a classe java.util.Scanner. No seu modo mais básico de uso é somente necessário indicar a fonte dos dados, isto é, uma string ou um ficheiro por exemplo, e o carácter delimitador,

Scanner s = new Scanner("122;4554;45665;");s.useDelimiter(";"); // Indica à classe que quando encontrar um ; // termina e começa novo númerowhile (s.hasNextLong())

System.out.println(s.nextLong()); //==> 122 4554 45665

A classe Scanner analisa a fonte de strings e procura da string delimitadora, assim que encontra uma devolve a informação obtida entre as duas últimas strings delimitadora processadas. As strings encontradas são devolvidas na forma de String ou num tipo primitivo por meio dos métodos hasNextXXX() e nextXXX() onde XXX corresponde ao tipo primitivo a devolver.

Os métodos hasNextXXX() da classe Scanner podem bloquear se a fonte de dados num dado momento não contiver informação para processar.

A string delimitadora pode ser um carácter isolado ou uma expressão regular. A especificação das expressões regulares é bastante flexível e complexa. A seguir apresentam-se exemplos dos elementos de especificação de expressões regulares mais simples: Seleccionar caracteres:

● [abc] = selecciona um dos caracteres minúsculos: a, b, ou c● [a-c] = selecciona um dos caracteres minúsculos de a a c ● . = selecciona um qualquer carácter ● \d = selecciona um digito ou corresponde aos caracteres [0-9] ● \s = selecciona um espaço em branco, ou um tab ou uma mudança de linha● \w = selecciona uma letra maiúscula ou minúscula, ou seja corresponde aos

caracteres [a-z] e [A-Z]

Cardinalidades do padrão de selecção de caracteres:● (padrão)* = zero ou mais ocorrências do padrão. É um padrão

opcional que pode ocorrer mais do que uma vez ● (padrão)+ = uma ou mais ocorrências do padrão. É um padrão que

existe pelo menos uma vez.● (padrão)? = zero ou uma ocorrência do padrão. É um padrão

opcional.

31-07-2007 87/96

Apontamentos sobre Java 5.0 Luís Mendes

Para encontrar as string que estão divididas por dígitos, pode-se usar a classe Scanner do seguinte modo,

s = new Scanner("11a1b3V78");s.useDelimiter("\\d+"); while (s.hasNext())

System.out.println(s.next()); //==> a b V

A classe String tem o método public String[] split(<String expressão regular>) que do ponto de vista funcional é uma versão simplificada da classe Scanner, pois só devolve a informação na forma de strings.

Formatação de texto

A representação dos tipos primitivos e dos objectos em string tem até aqui sido considerada apenas através da concatenação de strings ou através dos métodos append() das classes StringBuffers ou StringBuilds.

Contudo, para representar a informação das variáveis e campos de uma forma mais flexível e de acordo com as convenções do local onde o código é executado é necessário outro tipo de funcionalidades.

Para a construção de mensagens de texto envolvendo diversos tipos de dados há na linguagem JAVA a classe java.util.Formatter que permite construir mensagens de texto à la printf da linguagem C e que automaticamente formata os diversos tipos de acordo com o local escolhido,

StringBuilder sb = new StringBuilder();Formatter f = new Formatter(sb);// segue o formato do printf Cf.format("ola mundo %s %d %b %f\n", "benfica", 678, false, 6783.09 );// é possível impor dimensões mínimas nos números inteiros e decimaisf.format("ola mundo [%10d] [%8.8f] \n", 678, 6783.09 );f.format("ola mundo [%-10d] [%-8.8f]", 678, 6783.09 );System.out.println(sb);

// devolvendo o textoola mundo benfica 678 false 6783,090000ola mundo [ 678] [6783,09000000] ola mundo [678 ] [6783,09000000]

A funcionalidade da classe Formatter foi adicionada a algumas classes já nossas conhecidas como a System.out e a String.

31-07-2007 88/96

Apontamentos sobre Java 5.0 Luís Mendes

// A classe Formatter na classe System.out System.out.format("ola mundo %s %d %b %f\n", "benfica", 678, false, 6783.09 );// A classe Formatter na classe StringString g = String.format("ola mundo %s %d %b %f\n", "benfica", 678, false, 6783.09 );System.out.println(g);

Formatação de texto para datas

Os computadores só guardam números. Se quisermos guardar informação não númerica, seja ela uma String ou uma data por exemplo, temos de a codificar em números. Veja-se por exemplo, o alfabeto das strings, cada letra é representada por um número que depois pode ser convertido por uma impressora ou placa gráfica em representações gráficas da letra.

O tempo, na sua vertente, mês, dia, ano ou na vertente hora, minuto, segundo segue a mesma regra. Em java o tempo é representado como sendo um long contendo o número de microsegundos decorridos desde o dia 1 de Janeiro de 1970 até à data e hora que pretendemos guardar. Como era de esperar em Java esse long está encapsulado numa classe, a java.util.Date, que disponibiliza diversos métodos para aceder à data e hora guardada.

A classe java.util.Date não é uma classe imutável uma vez que tem métodos que permitem alterar o número de milisegundos guardados pela classe.

Os métodos de java.util.Date mais úteis são:

Método Descrição

Date() construtor que inicializa a classe com a data e hora da sua criação, de acordo com o valor indicado pelo relógio do computador.

Date(long date) construtor que inicializa com os número de milissegundos indicados no argumento

boolean after(Date when) indica se a data do objecto é posterior à data do argumento

boolean before(Date when) indica se a data do objecto é anterior à data do argumento

int compareTo(Date anotherDate)

indica se a data do objecto é maior, menor ou igual que a data do argumento.

Long getTime() devolve o número de milissegundos da data

31-07-2007 89/96

Apontamentos sobre Java 5.0 Luís Mendes

void setTime(long time) substitui o número de milissegundos do objecto pelo valor passado por argumento.

As pessoas estão habituadas a lidar com o tempo usado termos como dias, horas, minutos, dia da semana, meses e não com valores em milissegundos. A classe java.lang.Calendar faz exactamente essa conversão. É ela que faz a ponte entre o valor da classe Date e as operações possíveis de serem realizadas sobre uma data recorrendo às noções de dia, hora, semana, etc,. A classe Calendar adapta-se ao formato da data e hora usada pelo local especificado no computador.

A classe Calendar é uma classe abstracta que não tem construtores públicos mas oferece o método public static Calendar getInstance() que devolve uma instancia de Calendar baseada no local da máquina virtual.

Calendar cal = Calendar.getIntance();Date d = cal.getTime();

A presentação textual de uma data varia de país para país. A classe java.text.DateFormat permite transformar um objecto de java.lang.Date numa representação textual da data de acordo com as convenções do local de execução do código. A classe também é abstracta mas é possível obter uma instancia através do método public static DateFormat getDateFormat(),

Calendar cal = Calendar.getInstance();Date date = cal.getTime();DateFormat dformat = DateFormat.getDateInstance(DateFormat.FULL);System.out.println(dformat.format(date)); // ==> Segunda-feira, 30 de Julho de 2007

As constantes para o método getDateFormat() são: DateFormat.FULL, DateFormate.SHORT, DateFormate.MEDIUM, DateFormate.LONG.

A classe DateFormat também converte strings representativas de uma data em objectos Date através do método parse(String s)

Para que a classe DateFormat mostre apenas as horas ou a combinação da data com a hora deve-se usar a classe do seguinte modo:

// exibir só as horasdformat = DateFormat.getTimeInstance(DateFormat.LONG); System.out.println(dformat.format(date)); // ==> 17:51:59 BST

// exibir a data e as horas

31-07-2007 90/96

Apontamentos sobre Java 5.0 Luís Mendes

dformat = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.LONG);System.out.println(dformat.format(date)); // ==> Segunda-feira, 30 de Julho de 2007 17:54:21 BST

Formatação de números

A classe java.text.NumberFormat permite criar objectos que convertem números inteiros ou decimais em representações textuais de acordo com as convensões de representação de números do local da máquina virtual.

// exibir números decimaisNumberFormat nFormat = NumberFormat.getInstance();System.out.println(nFormat.format(233232.787f)); // 233.232,781

// exibir dinheiro, o símbolo do dinheiro é colocado automaticamenteNumberFormat diFormat = NumberFormat.getCurrencyInstance();System.out.println(diFormat.format(233232.787f)); // 233.232,78 €

// exibir inteirosNumberFormat iFormat = NumberFormat.getIntegerInstance();System.out.println(iFormat.format(233232)); // ==> 233232

// exibir percentagem, o símbolo da percentagem é colocado automaticamenteNumberFormat pFormat = NumberFormat.getPercentInstance();System.out.println(pFormat.format(0.67)); // ==> 67%

31-07-2007 91/96

Apontamentos sobre Java 5.0 Luís Mendes

Capitulo 9 – I/O e Streams

A Classe File

A classe File permite interagir com o sistema de ficheiros onde a máquina virtual está a executar. Uma directoria ou ficheiro do sistema de ficheiros é representado em JAVA por uma instancia da classe java.io.File.

Um objecto de File pode verificar se um ficheiro/directoria existe, obter o seu nome, indagar se é uma directoria ou ficheiro, criar directorias, apagar ficheiros ou directorias, entre outras operações. Um dos construtores de File aceita uma string com a caminho absoluto para o ficheiro ou directoria que será objecto das operações da classe, mesmo que essa directoria ou ficheiro ainda não exista.

File file = new File(“c:\\autoexec.bat”);if (file.exists())

file.delete();

A classe RandomAccessFile

A classe RandomAccessFile permite ler e escrever num ficheiro de uma forma aleatória. Ela representa o conteúdo do ficheiro como se fosse uma sequência de bytes onde um apontador móvel indica onde será realizada a próxima operação de leitura ou escrita. As operações de leitura e escrita lêem e escrevem apenas sequências de bytes.

O construtor permite indicar o caminho para o ficheiro que será acedido pela classe RandomAccessFile e as operações que poderão ser realizadas sobre o ficheiro. As operações seleccionadas são indicadas através de uma String com os seguintes valores possíveis:

● “r” : leitura● “rw” : leitura e escrita● “rws” : leitura e escrita sendo as alterações são imediatamente realizadas no

sistema de ficheiros● “rwd” : leitura e escrita com as alterações a não serem imediatamente realizadas

no sistema de ficheiros.

Após a construção de um objecto da classe RandomAccessFile o apontador móvel situa-se no inicio do ficheiro. Sendo os consequentes movimentos do apontador especificados através da quantidade de bytes que dista a nova posição e o inicio do ficheiro (valor 0).

31-07-2007 92/96

Apontamentos sobre Java 5.0 Luís Mendes

Os métodos mais importantes de RandomAccessFile são,

Método Descrição

Long getFilePointer() Devolve em número de bytes a actual posição do apontador no ficheiro

Long length() Tamanho do ficheiro em bytes

Void seek(long position) Posiciona o apontador no byte indicado pelo valor do parâmetro, sendo este valor o número de bytes da nova posição desde o inicio do ficheiro.

Int read(), void write(int b) Lê e escreve um byte no ficheiro.

Boolean readBoolean()void writeBoolean()

Lê e escreve um boolean no ficheiro.

Boolean readLong()void writeLong()

Lê e escreve um long no ficheiro.

Void close() Fecha o ficheiro. Operação obrigatória de ser executada quando o ficheiro deixa de ser necessário.

Streams, Readers e Writers

StreamsUm Stream é um conceito que representa uma entidade armazenadora que é constituída por bytes, por exemplo, um ficheiro ou um array em memória. Os bytes são a única unidade que um stream sabe trabalhar. Outra característica apresentada pelos streams é o facto de operarem uni-direccionalmente (sequêncialmente), isto é, quando um byte é lido ou escrito não pode ser novamente lido ou escrito pelo mesma instância do stream.

Na linguagem Java há para cada tipo de Stream duas classes distintas: uma com as operações de leitura de bytes e outra com operações de escrita de bytes.

Há também uma outra classificação para os stream disponíveis em Java relacionada com as suas unidades de trabalho:

● streams de baixo nível: são streams que só trabalham com bytes e guardam ou lêem a informação da entidade que representam.

● streams de alto nível: são streams que trabalham com outros tipos de dados como longs, inteiros, por exemplo e que convertem esses dados para byte, ou seja, trabalham em cooperação com os streams de baixo nível porque são eles que têm acesso à entidade armazenadora de informação.

Os streams de baixo nível mais importantes em Java são:

31-07-2007 93/96

Apontamentos sobre Java 5.0 Luís Mendes

Classes entidade

FileInputStream, FileOutputStream

representa um ficheiro no sistema de ficheiros

InputStream, OutputStream Representam as operações básicas disponíveis às classes de leitura e escrita de um stream

ByteArrayInputStream, ByteArrayOutputStream

representa um array de bytes na memória da máquina virtual

PipedInputStream, PipedOutputStream

representa um “pipe” que pode ser usado na comunicação entre threads.

Os streams de alto nível mais importantes em Java são:

Classes entidade

DataInputStream, DataOutputStream

Permite escrever e ler os dados dos tipo primitivos num stream de baixo nível.

BufferInputStream, BufferOutputStream

As operações são realizadas num buffer em memória e só mais tarde são realizadas no stream.

PrintStream Converte texto e tipos primitivos na sua representação textual.

Os streams podem ser combinados entre si desde que sejam todos de leitura ou todos de escrita, por exemplo,

FileInputStream f = new FileInputStream(“text”);BufferedInputStream b = new BufferedInputStream(f);DataInputStream d = new DataInputStream(b);

Readers e writers

Os Readers e Writers têm o mesmos propósito e organização que os streams. A diferença está na sua unidade de mínima de trabalho que é o char (unicode) e não o byte. Também há readers/writers de alto nível e de baixo nível.

Os readers/writers de baixo nível mais importantes em java são:

Classes entidade

FileReader, FileWriter representa um ficheiro de texto no sistema de ficheiros

CharArrayReader, CharArrayWriter

representa um array de chars na memória da máquina virtual

31-07-2007 94/96

Apontamentos sobre Java 5.0 Luís Mendes

StringReader, StringWriter Lê e escreve strings

PipedReader, PipedWriter representa um “pipe” que pode ser usado na comunicação entre threads.

Os readers/writers de alto nível mais importantes em java são:

Classes entidade

BufferedReader, BufferedWriter

Oferece um buffer para as suas operações

PrintWriter Converte texto e tipos primitivos na sua representação textual.

Quando os readers e writers de baixo nível fazem a conversão entre byte e char ou vice-versa é feita de acordo com o encoding da máquina virtual ou de um especificado no momento da construção dos objectos.

Serialização de objectos

A serialização de uma classe é a operação de gravar/ler os valores dos campos de um objecto numa memória persistente, como por exemplo no disco rígido. Como normalmente os objectos mantém referências entre si formando um grafo de dependências a serialização tem de se preocupara de gravar/ler todos os objectos que fazem parte desse grafo pois caso contrário quando a serialização recupera da memória persistente os objectos haverá referências para objectos inexistentes, fantasmas.

As classes dos objectos a serializar tem de implementar a interface sem métodos Serializable.

A o mecanismo de serialização de objectos é feita através dos streams de alto nível:● ObjectOutputStream: o método writeObject(object o) grava objectos no stream;● ObjectInputStream: o método readObject(Object o) cria na memória da

máquina virtual objectos e lê do stream os valores dos seus campos.

A classe ObjectOutputStream grava também todos os objectos que são referenciados pelo objecto invocado no método writeObject().

A classe ObjectInputStream cria os objectos serializados por meio do constructor sem algumentos, pelo que a sua inclusão é obrigatória nas classes a persistir.

Quanto à identificação dos campos a serializar os dois streams têm dois modos de funcionamento:

● automático: Os campos dos objectos a serializar são analisadas automaticamente

31-07-2007 95/96

Apontamentos sobre Java 5.0 Luís Mendes

independentemente do seu modificador de acesso sendo todos os campos da classe serializáveis a não ser que sejam:

● campos estáticos;● campos com o modificador transient;

● manual: o programador tem a possibilidade de indicar quais os campos a serializar na implementação dos métodos de leitura ou de escrita. Há duas formas de o fazer:

● a classe a serializar implementa os métodos :● private void writeObject(ObjectOutputStream oos) throws

IOException;● private void readObject(ObjectInputStream oos) throws IOException;

● ou a classe implementa a interface java.io.Externalizable com os métodos:● public void writeExternal(ObjectOutput o) throws IOException● public void readExternal(ObjectInput o) throws IOException

31-07-2007 96/96