Campos - ondas e dispositivos eletromagnéticos
Campos - ondas e dispositivos eletromagnéticos
Fundamentos da programação orientada a objetos
Fundamentos da programação orientada a objetos
Fundamentos da programação orientada a objetos

Classes
API
Java
Uso das classes da API padrão de Java
Um dos grandes atrativos da programação orientada a objetos é a possibilidade de adaptar funcionalidades oferecidas em classes existentes às necessidades de cada aplicação. Java, não diferentemente, oferece essa facilidade, aliando-a ao oferecimento de um amplo conjunto de classes organizadas nos pacotes da API padrão.
As classes que compõem o núcleo de funcionalidades Java estão organizadas em pacotes, grupos de classes, interfaces e exceções afins ou de uma mesma aplicação. Entre os principais pacotes oferecidos como parte do núcleo Java estão: java.lang, java.util, java.io, java.awt, java.applet e java.net.
Observe que esses nomes seguem a convenção Java, pela qual nomes de pacotes (assim como
nomes de métodos) são grafados em letras minúsculas, enquanto nomes de classes têm a primeira letra (de cada palavra, no caso de nomes compostos) grafada com letra maiúscula.
3.1 Funcionalidades básicas
O pacote java.lang contém as classes que constituem recursos básicos da linguagem, necessários à execução de qualquer programa Java.
A classe Object expressa o conjunto de funcionalidades comuns a todos os objetos Java, sendo a raiz da hierarquia de classes Java.
As classes Class e ClassLoader representam, respectivamente classes Java e o mecanismo para carregá-las dinamicamente para a Máquina Virtual Java.
A classe String permite a representação e a manipulação de strings cujo conteúdo não pode ser modificado. Para manipular string modificáveis — por exemplo, através da inserção de um caráter na string — a classe StringBuffer é oferecida.
A classe Math contém a definição de métodos para cálculo de funções matemáticas (trigonométricas, logarítimicas, exponenciais, etc.) e de constantes, tais como E e PI. Todos os métodos e constantes definidos nessa classe são estáticos, ou seja, para utilizá-los basta usar como prefixo o nome da classe.
O pacote oferece também um conjunto de classes wrappers. Um objeto de uma classe wrapper contém um único valor de um tipo primitivo da linguagem, permitindo assim estabelecer uma ponte entre valores literais e objetos. Essas classes são Boolean, Character, Byte, Short, Integer, Long, Float e Double. Além dos métodos para obter o valor associado ao objeto de cada uma dessas classes, métodos auxiliares como a conversão de e para strings são suportados.
As classes System, Runtime e Process permitem interação da aplicação Java com o ambiente de execução. Por exemplo, a classe System tem três atributos públicos e estáticos associados aos arquivos padrão de um sistema operacional: System.in, para a entrada padrão; System.out, para a saída padrão; e System.err, para a saída padrão de erros. Adicionalmente, as classes Thread e ThreadGroup, assim como a interface Runnable, dão suporte à execução de múltiplas linhas de execução que podem ser associadas a um processo.
As classes que definem erros e exceções, respectivamente Error e Exception, são também definidas nesse pacote. A classe Error é a raiz para condições de erro não-tratáveis, tais como OutOfMemoryError. Já a classe Exception está associada à hierarquia de condições que podem ser detectados e tratados pelo programa, como ArithmeticException (divisão inteira por zero) e ArrayIndexOutOfBoundsException (acesso a elemento de arranjo além da última posição ou antes da primeira posição). Ambas as classes são derivadas de Throwable, também definida nesse pacote.
Sub-pacotes relacionados à java.lang incluem java.lang.ref, de referências a objetos, e o pacote java.lang.reflect, que incorpora funcionalidades para permitir a manipulação do conteúdo de classes, ou seja, identificação de seus métodos e campos.
Deve-se ressaltar ainda que funções matemáticas são definidas em java.lang.Math. Outro pacote, java.math, contém funcionalidades para manipular números inteiros e reais de precisão arbitrária.
3.1 Entrada e saída
Por entrada e saída subentende-se o conjunto de mecanismos oferecidos para que um programa executando em um computador consiga respectivamente obter e fornecer informação de dispositivos externos ao ambiente de execução, composto pelo processador e memória principal.
De forma genérica, havendo um dispositivo de entrada de dados habilitado, o programa obtém dados deste dispositivo através de uma operação read(). Similarmente, um dado pode ser enviado para um dispositivo de saída habilitado através de uma operação write().
A manipulação de entrada e saída de dados em Java é suportada através de classes do pacote java.io.
Essas classes oferecem as funcionalidades para manipular a entrada e saída de bytes, adequadas para a transferência de dados binários, e para manipular a entrada e saída de caracteres, adequadas para a transferência de textos. Fontes de dados manipuladas como seqüências de bytes são tratadas em Java pela classe InputStream e suas classes derivadas. Similarmente, saídas de seqüências de bytes são tratadas por OutputStream e suas classes derivadas.
Aplicações típicas de entrada e saída envolvem a manipulação de arquivos contendo caracteres. Em Java, a especificação de funcionalidades para manipular esse tipo de arquivo estão contidas nas classes abstratas Reader (e suas classes derivadas) e Writer (e suas classes derivadas), respectivamente para leitura e para escrita de caracteres.
3.1.1 Transferência de texto
A classe Reader oferece as funcionalidades para obter caracteres de alguma fonte de dados — que fonte de dados é essa vai depender de qual classe concreta, derivada de Reader, está sendo utilizada. A classe Reader apenas discrimina funcionalidades genéricas, como o método read(), que retorna um int representando o caráter lido, e ready(), que retorna um boolean indicando, se verdadeiro, que o dispositivo está pronto para disponibilizar o próximo caráter.
Como Reader é uma classe abstrata, não é possível criar diretamente objetos dessa classe. É preciso criar objetos de uma de suas subclasses concretas para ter acesso à funcionalidade especificada por Reader.
BufferedReader incorpora um buffer a um objeto Reader. Como a velocidade de operação de dispositivos de entrada e saída é várias ordens de grandeza mais lenta que a velocidade de processamento, buffers são tipicamente utilizados para melhorar a eficiência dessas operações de leitura e escrita. Adicionalmente, na leitura de texto a classe BufferedReader adiciona um método readLine() para ler uma linha completa. LineNumberReader estende essa classe para adicionalmente manter um registro do número de linhas processadas.
CharArrayReader e StringReader permitem fazer a leitura de caracteres a partir de ar- ranjos de caracteres e de objetos String, respectivamente. Assim, é possível usar a memória principal como uma fonte de caracteres da mesma forma que se usa um arquivo.
FilterReader é uma classe abstrata para representar classes que implementam algum tipo de filtragem sobre o dado lido. Sua classe derivada, PushbackReader, incorpora a possibilidade de retornar um caráter lido de volta à sua fonte.
InputStreamReader implementa a capacidade de leitura a partir de uma fonte que fornece bytes, traduzindo-os adequadamente para caracteres. Sua classe derivada, FileReader, permite associar essa fonte de dados a um arquivo.
PipedReader faz a leitura a partir de um objeto PipedWriter, estabelecendo um mecanismo de comunicação inter-processos — no caso de Java, entre threads de uma mesma máquina virtual.
As funcionalidades de escrita de texto estão associadas à classe abstrata Writer e suas classes derivadas. Entre as funcionalidades genéricas definidas em Writer estão o método write(int), onde o argumento representa um caráter, sua variante write(String) e o método flush(), que força a saída de dados pendentes para o dispositivo sendo acessado para escrita.
Assim como para a classe Reader, as funcionalidades da classe Writer são implementadas através de suas subclasses concretas.
BufferedWriter incorpora um buffer a um objeto Writer, permitindo uma melhoria de eficiência de escrita ao combinar várias solicitações de escrita de pequenos blocos em uma solicitação de escrita de um bloco maior.
CharArrayWriter e StringWriter permitem fazer a escrita em arranjos de caracteres e em objetos StringBuffer, respectivamente.
FilterWriter é uma classe abstrata para representar classes que implementam algum tipo de filtragem sobre o dado escrito.
OutputStreamWriter implementa a capacidade de escrita tendo como destino uma seqüência de bytes, traduzindo-os adequadamente desde os caracteres de entrada. Sua classe derivada, FileWriter, permite associar esse destino de dados a um arquivo.
3.1.1 Transferência de bytes
A classe abstrata InputStream oferece a funcionalidade básica para a leitura de um byte ou de uma seqüência de bytes a partir de alguma fonte. Os principais métodos dessa classe incluem available(), que retorna o número de bytes disponíveis para leitura, e read(), que retorna o próximo byte do dispositivo de entrada.
Como para a leitura de texto, InputStream é usado de fato através de uma de suas classes derivadas. ByteArrayInputStream permite ler valores originários de um arranjo de bytes, permitindo assim tratar uma área de memória como uma fonte de dados.
Outra funcionalidade associada à transferência de dados está relacionada à conversão de formatos, tipicamente entre texto e o formato interno de dados binários. Essa e outras funcionalidades são suportadas através do oferecimento de filtros, classes derivadas de FilterInputStream, que podem ser agregados aos objetos que correspondem aos mecanismos elementares de entrada e saída. FileInputStream lê bytes que são originários de um arquivo em disco. Usualmente, um objeto dessa classe é usado em combinação com os filtros BufferedInputStream e DataInputStream. Outro exemplo de filtro é PushbackInputStream, que oferece métodos unread() que permitem repor um ou mais bytes de volta à sua fonte, como se eles não tivessem sido lidos.
A classe BufferedInputStream lê mais dados que aqueles solicitados no momento da operação read() para um buffer interno, melhorando a eficiência das operações subseqüentes para essa mesma fonte de dados.
DataInputStream permite a leitura de representações binárias dos tipos primitivos de Java, oferecendo métodos tais como readBoolean(), readChar(), readDouble() e readInt(). É uma implementação da interface DataInput. Essa interface, também implementada pela classe RandomAccessFile, especifica métodos que possibilitam a interpretação de uma seqüência de bytes como um tipo primitivo da linguagem Java.
Adicionalmente, é também possível interpretar a seqüência de bytes como um objeto String.
Em geral, métodos dessa interface ge- ram uma exceção EOFException se for solicitada a leitura além do final do arquivo. Em outras situações de erro de leitura, a exceção IOException é gerada.
A classe SequenceInputStream oferece a funcionalidade para concatenar dois ou mais objetos InputStream; o construtor especifica os objetos que serão concatenados e, automaticamente, quando o fim do primeiro objeto é alcançado os bytes passam a ser obtidos do segundo objeto.
A classe abstrata OutputStream oferece a funcionalidade básica para a transferência seqüencial de bytes para algum destino. Os métodos desta classe incluem write(int) e flush(), suportados por todas suas classes derivadas.
ByteArrayOutputStream oferece facilidades para escrever para um arranjo de bytes interno, que cresce de acordo com a necessidade e pode ser acessado posteriormente através dos métodos toByteArray() ou toString(). FileOutputStream oferece facilidades para escrever em arquivos, usualmente utilizadas em
conjunção com as classes BufferedOutputStream e DataOutputStream.
FilterOutputStream define funcionalidades básicas para a filtragem de saída de dados, implementadas em alguma de suas classes derivadas: BufferedOutputStream armazena bytes em um buffer interno até que este esteja cheio ou até que o método flush() seja invocado. Da- taOutputStream é uma implementação da interface DataOutput que permite escrever valores de variáveis de tipos primitivos de Java em um formato binário portátil. A classe PrintStream 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.1.1 Manipulação de arquivos
Outro dispositivo de entrada e saída de vital importância é disco, manipulado pelo sistema operacional 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 construtores 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 exemplo, 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”), envolvendo 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.
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.1.1 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).
-
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 completamente 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 à chave 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 quantidade de elementos na coleção.
Se adicionalmente deseja-se que esse grupo de objetos seja manipulado de acordo com alguma ordem específica, a interface derivada SortedMap pode ser utilizada. Assim, além das funcionalidades acima, esta interface especifica métodos tais como:
Object firstKey() para retornar o elemento da coleção com a chave de menor valor.
Object lastKey() para retornar o elemento da coleção com a chave de maior valor.
SortedMap subMap(Object fromKey, Object toKey) para obter o subconjunto dos elementos compreendidos entre as chaves especificadas.
Para essas interfaces, duas implementações básicas são também oferecidas nesse mesmo pacote java.util. A classe HashMap implementa o mecanismo básico de mapeamento entre chaves e valores. A classe TreeMap oferece a funcionalidade adicional de associar uma ordem aos elementos da coleção. Para alguns tipos de objetos, tais como Integer, Double ou String, uma ordem “natural” já é pré-estabelecida. Para objetos de classes da aplicação, a classe deverá implementar a interface Comparable.
Um objeto que implementa a interface Collection representa um grupo de objetos genérico, onde duplicações são permitidas. Entre os métodos básicos especificados nessa interface estão:
boolean add(Object element) para adicionar o elemento especificado à coleção.
boolean remove(Object element) para remover o elemento especificado da coleção.
boolean contains(object element) para verificar se a coleção contém o elemento especificado.
int size() para obter a quantidade de elementos na coleção.
Iterator iterator() para obter um objeto que permite varrer todos os elementos da coleção.
Duas outras interfaces, Set e List, são derivadas diretamente de Collection.
Set é uma extensão de Collection que não permite duplicações de objetos — nenhum método novo é introduzido, mas apenas a necessidade de garantir essa restrição. A interface SortedSet é uma extensão de Set que agrega o conceito de ordenação ao conjunto, introduzindo a especificação de métodos tais como first(), para obter o primeiro elemento do conjunto; last(), para obter o último elemento do conjunto; e subSet(), para obter o subconjunto com todos os elementos contidos entre os dois elementos especificados como argumentos do método.
A interface Set tem duas implementações já oferecidas no pacote java.util. A classe HashSet é uma implementação padrão que utiliza o valor de código hash para detectar e impedir duplicações.
A classe TreeSet é uma implementação de SortedSet que usa uma estrutura de dados em árvore para manter ordenados os elementos do conjunto.
A interface List é uma extensão de Collection que introduz mecanismos de indexação. Além dos métodos básicos de coleções, adiciona métodos tais como get() para obter o elemento armazenado na posição especificada como argumento; indexOf() para obter a posição da primeira ocorrência do objeto especificado como argumento; e subList() para obter uma sublista contendo os elementos compreendidos entre as duas posições especificadas, incluindo o elemento da primeira posição mas não o da segunda.
Da mesma forma que para Set, há duas implementações padronizadas para a interface List já definidas em java.util. A classe ArrayList oferece uma implementação básica da interface, enquanto que a classe LinkedList oferece uma implementação otimizada para manipular listas dinâmicas, introduzindo os métodos addFirst(), getFirst(), removeFirst(), addLast(), getLast() e removeLast().
Para percorrer os elementos de uma coleção no novo framework, um objeto que implementa a interface Iterator é utilizado. Essa interface especifica os métodos hasNext(), que retorna um valor booleano verdadeiro para indicar que há mais elementos na coleção, e next(), que retorna o objeto que é o próximo elemento da coleção. Adicionalmente, um método void remove() pode ser utilizado para retirar um elemento da coleção através da referência a um Iterator.
A interface ListIterator é uma extensão de Iterator que permite a varredura da coleção nas duas direções, especificando novos métodos tais como boolean hasPrevious() e Object previous().
3.1 Extensões padronizadas
Além das funcionalidades básicas oferecidas por esses pacotes, há também APIs definidas para propósitos mais específicos compondo a extensão padronizada ao núcleo Java.
Por exemplo, o Input Method Framework está associado ao pacote java.awt.im e contempla funcionalidades para manipular textos não-ocidentais usando teclados convencionais.
Funcionalidades para definir e utilizar componentes de software em Java são oferecidas através de Java Beans, um conjunto de APIs definidas através do pacote java.beans e seu subpacote java.beans.context para suportar os modelos de instrospecção, customização, eventos, pro- priedades e persistência desses componentes.
Uma das extensões mais significativas é definida pelo conjunto da Java Foundation Classes, extensão de AWT que oferece um conjunto de componentes de interface gráfica com usuário completamente portátil.
Três pacotes compõem JFC: Swing, Drag and Drop e Java 2D.
Swing define uma família de pacotes com o prefixo javax.swing com componentes e serviços GUI de aparên- cia independente de plataforma.
Drag and Drop, associado ao pacote java.awt.dnd, suporta o compartilhamento de dados baseado no padrão MIME, Multipurpose Internet Mail Extension.
Java 2D acrescenta novas classes aos pacotes java.awt e java.awt.image para estender as funcionalidades associadas à manipulação de imagens bidimensionais e acrescenta novos pacotes
java.awt.color,
java.awt.font,
java.awt.geom,
java.awt.image.renderable e
java.awt.print.
As funcionalidades para a construção de interfaces gráficas com usuários são apresentadas na Seção 4.
Outra funcionalidade acrescentada à Java foi a capacidade de acesso a bancos de dados relacionais usando a linguagem de consulta SQL. Através da API definida em JDBC, através do pacote java.sql, é possível enviar comandos SQL para um servidor de banco de dados e processar o retorno obtido nessa consulta. A utilização de JDBC é descrita na Seção 5.2.3.
Java IDL integra a funcionalidade da arquitetura CORBA a aplicações Java, permitindo obter inter-operabilidade e conectividade baseadas nesse padrão do OMG (Object Management Group). Além das classes presentes nos pacotes org.omg.CORBA e org.omg.CosNaming, alguns aplicativos são acrescidos ao ambiente de desenvolvimento e execução de Java, como o compilador de Java para IDL e um aplicativo que oferece o serviço de nomes. O desenvolvimento de aplicações usando Java IDL é descrito na Seção 5.4.3.