A maior rede de estudos do Brasil

Grátis
118 pág.
poojava

Pré-visualização | Página 14 de 37

FEEC/UNICAMP 40
Programação orientada a objetos com Java 3.2. Entrada e saída
oferece métodos para apresentar representações textuais dos valores de tipos primitivos Java, através
das várias assinaturas dos métodos print() (impressão sem mudança de linha) e println()
(impressão com mudança de linha).
A classe ObjectInputStream oferece o método readObject() para a leitura de objetos
que foram serializados para um ObjectOutputStream. Através desse mecanismo de serialização
é possível gravar e recuperar objetos de forma transparente.
PipedInputStream oferece a funcionalidade de leitura de um pipe de bytes cuja origem está
associada a um objeto PipedOutputStream. Já PipedOutputStream implementa a origem
de um pipe de bytes, que serão lidos a partir de um objeto PipedInputStream. Juntos, obje-
tos dessas classes oferecem um mecanismo de comunicação de bytes entre threads de uma mesma
máquina virtual.
3.2.3 Manipulação de arquivos
Outro dispositivo de entrada e saída de vital importância é disco, manipulado pelo sistema opera-
cional e por linguagens de programação através do conceito de arquivos. Um arquivo é uma abstração
utilizada por sistemas operacionais para uniformizar a interação entre o ambiente de execução e os
dispositivos externos a ele em um computador. Tipicamente, a interação de um programa com um
dispositivo através de arquivos passa por três etapas: abertura ou criação de um arquivo; transferência
de dados; e fechamento do arquivo.
Em Java, a classe File permite representar arquivos nesse nível de abstração. Um dos construto-
res desta classe recebe como argumento uma string que pode identificar, por exemplo, o nome de um
arquivo em disco. Os métodos desta classe permitem obter informações sobre o arquivo. Por exem-
plo, exists() permite verificar se o arquivo especificado existe ou não; os métodos canRead()
e canWrite() verificam se o arquivo concede a permissão para leitura e escrita, respectivamente;
length() retorna o tamanho do arquivo e lastModified(), o tempo em que ocorreu a última
modificação (em milissegundos desde primeiro de janeiro de 1970). Outros métodos permitem ainda
realizar operações sobre o arquivo como um todo, tais como delete() e deleteOnExit().
Observe que as funcionalidades de transferência seqüencial de dados a partir de ou para um
arquivo não é suportada pela classe File, mas sim pelas classes FileInputStream, File-
OutputStream, FileReader e FileWriter. Todas estas classes oferecem pelo menos um
construtor que recebe como argumento um objeto da classe File e implementam os métodos bá-
sicos de transferência de dados suportados respectivamente por InputStream, OutputStream,
Reader e Writer.
Para aplicações que necessitam manipular arquivos de forma não seqüencial (ou “direta”), envol-
vendo avanços ou retrocessos arbitrários na posição do arquivo onde ocorrerá a transferência, Java
oferece a classe RandomAccessFile. Esta não é derivada de File, mas oferece um construtor
que aceita como argumento de especificação do arquivo um objeto dessa classe. Outro construtor
recebe a especificação do arquivo na forma de uma string. Para ambos construtores, um segundo
argumento é uma string que especifica o modo de operação, “r” para leitura apenas ou “rw” para
leitura e escrita.
Os métodos para a manipulação da posição corrente do ponteiro no arquivo são seek(long
pos), que seleciona a posição em relação ao início do arquivo para a próxima operação de leitura ou
escrita, e getFilePointer(), que retorna a posição atual do ponteiro do arquivo. Além disso, o
método length() retorna o tamanho do arquivo em bytes.
c
�
2001 FEEC/UNICAMP 41
Programação orientada a objetos com Java 3.2. Entrada e saída
Para a manipulação de conteúdo do arquivo, todos os métodos especificados pelas interfaces
DataInput e DataOutput são implementados por essa classe. Assim, é possível por exemplo
usar os métodos readInt() e writeInt() para ler e escrever valores do tipo primitivo int,
respectivamente.
3.2.4 Serialização
Sendo Java uma linguagem de programação orientada a objetos, seria de se esperar que, além
das funcionalidades usuais de entrada e saída, ela oferecesse também alguma funcionalidade para
transferência de objetos. O mecanismo de serialização suporta essa funcionalidade.
O processo de serialização de objetos permite converter a representação de um objeto em memó-
ria para uma seqüência de bytes que pode então ser enviada para um ObjectOutputStream, que
por sua vez pode estar associado a um arquivo em disco ou a uma conexão de rede, por exemplo.
Por exemplo, para serializar um objeto da classe Hashtable, definida no pacote java.util,
armazenando seu contéudo em um arquivo em disco, a seqüência de passos é:
// criar/manipular o objeto
Hashtable dados = new Hashtable();
...
// definir o nome do arquivo
String filename = ...;
// abrir o arquivo de saída
File file = new File(filename);
if(!file.exists()){
file.createNewFile();
}
FileOutputStream out = new FileOutputStream(file);
// associar ao arquivo o ObjectOutputStream
ObjectOutputStream s = new ObjectOutputStream(out);
// serializar o objeto
s.writeObject(dados);
O processo inverso, a desserialização, permite ler essa representação de um objeto a partir de um
ObjectInputStream usando o método readObject():
FileInputStream in = new FileInputStream(file);
ObjectInputStream s = new ObjectInputStream(in);
dados = (Hashtable) s.readObject();
Objetos de classes para os quais são previstas serializações e desserializações devem implementar
a interface Serializable, do pacote java.io. Essa interface não especifica nenhum método
ou campo — é um exemplo de uma interface marker, servindo apenas para registrar a semântica de
serialização.
Serialização é um processo transitivo, ou seja, subclasses serializáveis de classes serializáveis
são automaticamente incorporadas à representação serializada do objeto raiz. Para que o processo
de desserialização possa operar corretamente, todas as classes envolvidas no processo devem ter um
construtor default (sem argumentos).
c
�
2001 FEEC/UNICAMP 42
Programação orientada a objetos com Java 3.3. Framework de coleções
3.3 Framework de coleções
Estruturas de dados são mecanismos para manipular coleções de elementos em um programa.
O pacote java.util oferece algumas classes definidas na API padrão de Java que implementam
funcionalidades associadas a estruturas de dados.
As classes de coleções da API Java compõem o chamado framework de coleções, que foi comple-
tamente redefinido a partir da versão 1.2 de Java. As classes até então existentes para a manipulação
de conjuntos de objetos — Vector, Stack e Hashtable, atualmente denominadas “as classes de
coleção históricas” — foram totalmente reprojetadas para se adequar ao novo framework. Elas foram
mantidas por motivos de compatibilidade, embora a recomendação seja utilizar as novas classes de
coleções.
Todas essas estruturas manipulam coleções de objetos, ou seja, qualquer objeto de qualquer classe
de Java pode ser elemento de uma dessas coleções. No entanto, não é possível criar diretamente uma
coleção de tipos primitivos, como int ou double. Para manipular coleções de tipos primitivos é
necessário utilizar as classes wrappers (Seção 3.1).
O framework de coleções tem por raiz duas interfaces básicas, Map e Collection.
A interface Map especifica as funcionalidades necessárias para manipular um grupo de objetos
mapeando chaves a valores. Entre outros, os seguintes métodos são especificados nessa interface:
� Object put(Object key, Object value): associa um novo valor associado à cha-
ve especificada, retornando o valor antigo (ou null se não havia valor associado à chave).
� Object get(Object key): retorna o valor associado à chave especificada.
� boolean containsKey(Object key): indica se a chave especificada está presente na
coleção.
� int size(): retorna a