
































Estude fácil! Tem muito documento disponível na Docsity
Ganhe pontos ajudando outros esrudantes ou compre um plano Premium
Prepare-se para as provas
Estude fácil! Tem muito documento disponível na Docsity
Prepare-se para as provas com trabalhos de outros alunos como você, aqui na Docsity
Encontra documentos específicos para os exames da tua universidade
Prepare-se com as videoaulas e exercícios resolvidos criados a partir da grade da sua Universidade
Responda perguntas de provas passadas e avalie sua preparação.
Ganhe pontos para baixar
Ganhe pontos ajudando outros esrudantes ou compre um plano Premium
Artigos Tema de capa Iniciaç
Tipologia: Notas de estudo
1 / 40
Esta página não é visível na pré-visualização
Não perca as partes importantes!

































notícias opinião tema de capa a programar segurança tutorial gnu/linux internet bluescreen comunidade especial
Rui Maia David Pintassilgo
Miguel Pais
Joel Ramos
Pedro Abreu
Sérgio Matias João Matos Sandro Pinto Miguel Araújo Fábio Correia Pedro Teixeira Filipe Jorge Andreia Gaita
David Ferreira Daniel Correia José Oliveira Sérgio Santos
revistaprogramar @portugal-a-programar.org
www.revista-programar.info
Miguel Pais
Coordenador Adjunto desde a 4ª edição, é a c t u a l m e n t e o C o o r d e n a d o r d a Revista Programar. Entrará este ano no curso de Engenharia I n f o r m á t i c a e d e Computadores.
Quando se desenvolve uma actividade por conta própria ou se está a iniciar no mercado existem sempre dificuldades em orçamentar correctamente os produtos e serviços.
Pretendo com este artigo tentar esclarecer algumas dúvidas e tentar ajudar nesta quase sempre complicada tarefa.
Uma das melhores formas de orçamentar um serviço é definir um valor hora e multiplicar pelo número de horas previstas de execução. Este valor deve ter em conta os seguintes factores de influência:
A somar a estes valores estão também:
Outra forma de orçamentar pode passar pela definição de um preçário por tarefas. As tarefas regem-se pelas mesmas variantes, mas podem facilitar alguns tipos de orçamentação para trabalhos mais regulares.
Imagem por =O-range
Pode aplicar-se, por exemplo, em casos como desenvolvimento de designs, instalação de CMS, entre outros.
Assim sendo, por exemplo, no caso de um trabalho que consista na criação de um backoffice para um site pode-se orçamentar das seguintes maneiras:
Desenvolvimento de Web site com backoffice para gestão de conteúdos: 40h x 15€ = 600€
ou
Desenvolvimento de Web site com backoffice para gestão de conteúdos
Mas nem só de preços se faz um orçamento. Por vezes existem problemas com clientes que à partida não definiram correctamente o que desejavam, criando uma situação de deficiente orçamentação, ou uma má apresentação da mesma. Assim sendo, um orçamento também deve incluir:
Para finalizar recordo que estas dicas apenas tentam ajudar a orçamentar, e cabe a cada um o bom senso de definir o valor do seu trabalho. Aborrece-me ver pessoas que muitas vezes dificultam o trabalho de quem vive dos serviços que presta, criando uma ideia errada aos clientes de um baixo preço de alguns serviços, quando muitos sabem as dificuldades de quem legalmente desenvolve.
Sérgio Matias
foi desenvolvido para o NASM, logo recomendo que o usem. Atenção que a sintaxe pode ser diferente entre assemblers (existem 2 tipos genéricos de sintaxe: AT&T e Intel), logo um código para um determinado assembler pode não funcionar noutro.
No que toca a linkers, não existem tantas opções como na categoria dos assemblers. O linker que vai ser usado é o ld, que vem com o pacote binutils do projecto GNU (http://www.gnu.org/software/binutils/). Outra alternativa é o alink (http://alink.sourceforge.net/).
Podem usar qualquer editor de texto. As escolhas mais populares em ambientes GNU/Linux são o vi/vim, emacs, e pico/ed/nano. Caso não se sintam à vontade a editar o código na consola (shell), também podem usar um editor de texto com interface gráfica, como o gedit, Geany, etc.
Caso o vosso sistema não tenha os pacotes instalados, procurem na documentação da vossa distribuição como o fazer.
Arquitectura do computador
Antes de começarmos a programar em Assembly, temos de aprender os conceitos básicos do funcionamento interno de um computador.
A arquitectura dos computadores modernos é baseada na arquitectura Von Neumann, seguindo o nome do seu criador. Esta arquitectura divide o computador em duas partes principais: o processador (CPU - Central Processing Unit) e a memória. Esta arquitectura é usada em todos os computadores modernos, incluindo os computadores pessoais, super computadores, mainframes, consolas de jogos e até mesmo telemóveis.
Estrutura da memória do computador
A memória do computador é o espaço onde estão armazenados todos os dados do computador. Este espaço tem um tamanho fixo e os dados podem ser acedidos através de endereços. Por exemplo, imaginem que têm 128MB de RAM no computador. Isto corresponde a 131072 kilobytes, ou 134217728 bytes. Neste caso, estão disponíveis 134217728 posições de armazenamento diferentes do tamanho de um byte. Não esquecer que o computador começa a contar no 0, logo os endereços de memória disponíveis neste caso começam no 0 e acabam em
O processador é o componente do computador que interpreta e executa as instruções dos programas e processa os dados. Este é constituído por vários sub-sistemas, dos quais se destacam a ALU (Arithmetic Logic Unit) - responsável por todas as operações aritméticas (ex. adição e subtracção) e lógicas (ex. AND, XOR, OR); FPU (Floating Point Unit) - equivalente ao ALU mas para números decimais; os registos - zona de armazenamento ultra-rápida, utilizada pelo processador para acelerar a execução dos programas permitindo acesso aos valores utilizados mais frequentemente.
Existe um número limitado de operações, sendo o conjunto de todas essas operações e das suas variações designado por ISA (Instruction Set Architecture). Existem diferentes conjuntos de instruções mas consideram-se duas categorias: RISC (Reduced Instruction Set Architecture - ex. arquitectura MIPS) e CISC (Complex Instruction Set Architecture - ex. arquitectura x86).
Este tutorial vai abordar o conjunto de instruções base x (este surgiu pela primeira vez em 1978 no processador Intel 8086). Ao longo dos anos têm sido feitas extensões a este conjunto de instruções, tais como o MMX, 3DNow!, SSE, SSE2 e SSE3.
Todos os processadores com base na arquitectura Von Neumann funcionam com base num ciclo constituído por 3 passos essenciais: fetch, decode, execute.
No primeiro passo o processador obtém a próxima instrução a executar a partir da posição contida no registo PC, que armazena a posição actual da memória do programa; no segundo passo, o processador divide a instrução (em código máquina) em secções: uma com o opcode da operação a executar (Operation Code) e as outras com dados complementares para realizar a operação; no terceiro passo a operação é executada.
Outro componente do processador são os registos. Os registos são como variáveis ultra-rápidas embutidas no processador. É nos registos que são armazenados todos os dados necessários para efectuar os cálculos ou outras operações realizadas pelo processador.
O problema da família x86 de processadores é que existem poucos registos disponíveis, o que leva a que o programador tenha de gerir bem os registos necessários para a sua aplicação.
Os registos são normalmente de 32bits, mas também existem registos de 16bits e de 8bits. Por exemplo, tomando como base o registo AX de 16bits, podemos considerar o registo EAX (Extended AX) de 32bits, e os registos AH (AX Higher) e AL (AX Lower) que correspondem a dois registos de 8bits.
Registos gerais: Tal como o nome indica estes são os registos usados a maior parte do tempo. A maioria das instruções tem como base estes registos.
•ECX, CX, CH, CL: “Counter register” É usado como contador de loops e para shifts.
Registos de segmento
Index e ponteiros
1. Endereçamento por valor imediato (immediate address mode) 2. Endereçamento de registo (register address mode) 3. Endereçamento directo (direct addressing mode) 4. Endereçamento por index (indexed addressing mode) 5. Endereçamento indirecto (indirect adressing mode) 6. Endereçamento por ponteiro base (base pointer addressing mode)
No primeiro caso, atribuímos o valor directamente. Por exemplo, se quisermos inicializar um registo para 0, introduzimos directamente o valor 0, em vez de darmos um endereço para o processador ler o valor 0.
No modo de endereçamento de registo, a instrução contém o registo de onde deve obter o valor, em vez de uma localização na memória.
No modo de endereçamento directo, a instrução contém o endereço da memória que contém o valor. Por exemplo, podemos pedir ao processador para copiar um valor num determinado endereço da memória para o registo do processador.
No modo de endereçamento por index, a instrução contém um endereço de memória para aceder e um index, que funciona como um offset. Por exemplo, se utilizarmos o endereço 1390 e um index de 10, o valor lido vai ser o da
Neste caso, a primeira instrução a ser realizada é a mov eax,
Nota: Na sintaxe AT&T, a ordem dos operandos é inversa.
Nas linhas seguintes movemos o valor 0 para o registo ebx do processador, e fazemos uma chamada ao sistema com a instrução int 0x80 (abreviatura de interrupt).
Como estamos a chamar o serviço exit do sistema (valor 1 no registo eax), o programa retorna à consola com o valor no registo ebx. Se experimentarem alterar este valor no código fonte, e voltarem a correr o programa, podem ver que o valor que o programa retorna para a consola é diferente. Nota: Não utilizar valores superiores a 255 (o valor máximo de um unsigned byte) ou podem ocorrer problemas de overflow. Ao ultrapassar o valor máximo que o byte permite, o comportamento do sistema pode ser inesperado. No meu caso, ao utilizar 266, o valor retornado foi de 10 (266-255).
De seguida o clássico Hello World:
João Matos
Primeiro declaramos a string Hello World usando ‘db’ (declare byte). No fim da string usamos o caracter de representação hexadecimal 0x0a, mais conhecido por \n ou mudança de linha.
Na linha seguinte atribuímos a ‘len’ o tamanho da string msg, usando para isso o ‘equ’. Para obter o valor do tamanho da string subtraímos a posição actual $ ao endereço inicial de msg.
No resto do programa usamos a chamada ao sistema write, para escrever a mensagem para a consola, e depois usamos o código do exemplo anterior para retornar do programa.
Esta foi a primeira parte deste artigo, saiu mais teórica do que o previsto. Assim ficam com as bases para aprender a maior parte dos conceitos mais complexos desta fabulosa linguagem. Estejam atentos às próximas edições para o próximo artigo que vai contar com muitos mais exemplos de código.
; write mov ebx, 1 ; ficheiro de saida
- stdin mov ecx, msg ; apontador para o buffer mov edx, len ; tamanho do buffer mov eax, 4 ; chamada write ao sistema int 0x
; exit mov eax, 1 ; move o valor 1 para o registo eax mov ebx, 0 ; move o valor 0 para o registo ebx int 0x80 ; chamada de sistema para a kernel
section .data msg db "Hello World!",0x0a ; string hello world len equ $-msg ; calcula o tamanho da string msg
section .text ; inicio da seccao de texto global _start ; onde deve comecar a execucao
_start: ; label start - a execucao comeca aqui
Bem, tal com referi na edição anterior, para este número da revista ficou reservada a parte mais prática da questão, isto é, enquanto outrora me preocupei em explicar como funcionava cada bloco de programação, agora tenciono analisar alguns programas exemplo, bem como explicar como se processa a comunicação entre o compilador e o robô, de forma a consolidar e fortificar os conhecimentos que o leitor já adquiriu.
Na verdade, antes de partirmos, efectivamente, para os exemplos práticos seria interessante explicar as regras e estrutura dos programas. Com efeito, a primeira ideia a clarear é que o ciclo do programa será sempre de cima para baixo, isto é, começa pelo bloco imediatamente abaixo do bloco de configuração das entradas e saídas do microcontrolador (“PROGRAM START”), e caso não exista nenhuma comparação o bloco seguinte a ser executado é o bloco logo abaixo.
Todavia, caso exista uma comparação, existem duas hipóteses: se se verificar a condição imposta no bloco de comparação, o próximo bloco a ser executado é o bloco logo abaixo deste; se não se verificar a condição, neste caso, o bloco a ser executado é o bloco imediatamente à direita da comparação, que poderá ser uma nova comparação ou simplesmente não existir. No caso de não existir nenhum bloco à direita, é considerado como ciclo terminado e é iniciado um novo.
Explicada, basicamente, a sintaxe dos programas vamos, portanto, analisar o primeiro exemplo:
Este é, sem dúvida, dos programas mais simples desta linguagem. Como podemos observar, utilizamos um bloco PWM de regulação da velocidade dos motores seguido de um bloco temporizador.
Assim, lendo o programa de cima para baixo, vemos que o robô irá andar para a frente (conforme se pode ver na figura da direita), contudo como por baixo temos um
bloco temporizador ele não o irá fazer eternamente, mas apenas 0,5 segundos (0,5 porque como vimos no bloco temporizador o tempo encontra-se escalado em períodos de 0,25 – 0,25x2=0,5). É importante referir que há logo a passagem do bloco de PWM para o bloco de temporização pois não existe nenhum bloco de comparação, e, como tal, não se tem de verificar se uma determinada condição é verdadeira ou falsa. Passados os dois segundos, visto que não há mais nenhum bloco de instrução, o ciclo do programa é dado como finalizado, mas logo de seguida iniciar-se-á um novo, ou seja, os programas nesta linguagem estão em “loop” constante.
Visto o primeiro exemplo, está na altura de compreender algo um pouco mais elaborado:
Antes de mais, convém referir que este é um bom exemplo para se demonstrar como se podem utilizar os blocos de comparação simples e com AND lógico, para além de se reforçar a compreensão dos blocos de movimentos pré- definidos e de regulação de velocidade. Feito o aparte, começarei então a explicar a estrutura do programa. Como podemos ver, o primeiro bloco imediatamente abaixo do
para trás (ambas os motores a 0) durante 0,75 segundos. Ora, agora em vez de um incremento, no final da coluna temos um bloco de atribuição de valores. Como o próprio nome indica, vamos atribuir um valor a variável V1, que sendo 10 fará com que se volte ao início do programa e se execute, novamente, todas as tarefas anteriormente mencionadas.
Em suma, este programa, através de incrementos (que também poderiam ser decrementos) e atribuição de valores às variáveis, faz com que o dispositivo realize uma sequência de movimentos, que bem elaborada, trabalhada e “alongada”, poderá habilitar o robô a representar uma coreografia (falo por experiência própria!).
Neste momento, depois do estudo de todos os blocos e de alguns exemplos concretos, o básico e intermédio da programação já está deveras consolidado. Visto isso, torna- se agora importante perceber como é que é feita a comunicação entre o compilador e o robô, isto é, como se descarregam os programas na PIC do dispositivo.
Assim, para descarregar o programa devemos, em primeiro lugar, efectuar a sua compilação, ou seja, depois dele estar produzido devemos carregar no botão de compilação existente na Toolbar, conforme se pode observar na figura abaixo.
Depois de efectuada a compilação é, então, automaticamente aberta uma aplicação chamada “CD2M”. Agora, torna-se importante a existência de um cabo RS232, essencial para estabelecer a comunicação entre a entrada série do PC e do robô.
Ligado o terminal macho ao robô e o terminal fêmea ao computador, na aplicação CD2M devemos especificar a porta série que estamos a utilizar “Com1”, “Com2”, “Com3”
ou “Com4” no campo Commport e seleccionar a velocidade de comunicação de 9600. De seguida, utilizando-se a opção Browse abre-se o ficheiro Prog.hex que se encontra na directoria onde foi instalado o software de programação. Finalmente, com o robô desligado (interruptor geral “OFF”) liga-se o interruptor de Programação (põe-se “ON”), e seguidamente é que se liga o interruptor geral (“ON”). Carregamos em START e vemos então o programa a ser enviado para o microcontrolador. Quando aparecer a mensagem “Code has been downloaded” podemos desligar o interrutor geral e de programação (por esta ordem). Retira-se o cabo série, e accionando o interruptor geral vemos o dispositivo electrónico a fazer o que definimos na programação.
Agora que chegamos ao final do artigo, considero que os leitores estão já com um certo à-vontade em relação a esta linguagem em ambiente gráfico. Contudo, agora cabe a cada um aprofundar e “trabalhar” a informação adquirida de forma a tornar-se um “expert” na programação deste robô.
Sandro Pinto
Esta é a primeira parte de um artigo que pretende fazer com que o leitor compreenda e use grafos sem quaisquer problemas.
Contudo, dada a extensão do tema, fui forçado a dividi-lo em duas partes. Uma bastante mais cansativa e outra bastante mais interessante. Fui forçado a tal facto uma vez que a teoria é tão importante como a prática.
Deixo-vos contudo um pequeno aperitivo da segunda: Flood Fill. Na segunda parte vamos concentrar os nossos esforços em algoritmos cada vez mais complexos, de modo a cobrir o máximo possível deste mundo.
O leitor certamente que já ouviu falar em grafos. São amplamente usados em matemática, mas sobretudo em programação. Formalmente, um grafo é uma colecção de vértices (V) e uma colecção de arcos (E) constituídos por pares de vértices. É uma estrutura usada para representar um modelo em que existem relações entre os objectos de uma certa colecção. Pense nos vértices como “locais”. O conjunto dos vértices é o conjunto de todos os locais possíveis. Nesta analogia, os arcos (ou arestas) representam caminhos entre estes locais. O conjunto E (vou usar o termo mais comum - “E” do inglês “edges”) contém todas as ligações entre os locais.
Utilizar grafos é de grande utilidade na representação de problemas da vida real.Podem ser cidades, e uma rede de estradas. Redes de computadores. Até mesmo os movimentos de um cavalo num tabuleiro de xadrez podem ser representados através de um grafo.
E depois de representá-los correctamente, o que podemos descobrir? O caminho mais curto entre duas cidades num mapa; dadas as coordenadas de n cidades, que estradas construir de modo que o número de quilómetros de estrada seja mínimo mas fiquem todas conectadas; dado um mapa de uma casa (em que paredes e chão são representados com caracteres diferentes) saber qual a divisão com maior área; etc.
As possibilidades são imensas, e ficarão admirados com a facilidade com que estes problemas são resolvidos.
Graficamente, um grafo é normalmente representado da seguinte forma:
Vértices são pontos ou círculos; Arcos são linhas entre eles.
Usando o primeiro exemplo, V={1, 2, 3, 4, 5, 6} e E = {(1,3), (1,6), (2,5), (3,4), (3,6)}. Cada vértice (também chamado “nó”) é um membro do conjunto V. Cada arco é um membro do conjunto E.
Como é de esperar, tendo aplicações tão variadas, o grafo adapta-se às nossas necessidades. Assim, existem vários tipos de grafos. Aliados a isso, existem termos comummente usados para descrever um grafo, ou parte dele. Vou listar alguns (entre eles os mais comuns):
- Vértice isolado - Um vértice é considerado isolado se não possuir nenhuma ligação a outro vértice - Grafo trivial (ou ponto) - Grafo sem arestas e um único nó. - Laço (ou loop/self-loop) - Um arco é um laço se em ambas as extremidades estiver o mesmo vértice. (nenhum dos grafos apresentados possui laços) - Grafo simples - Um grafo é simples se não contiver laços nem arcos repetidos em E. - Vértices adjacentes - Dois vértices (u e v) são adjacentes se existir um arco que possui uma extremidade em u e outra em v. Os vizinhos de um vértice são todos os vértices adjacentes a ele.
Sem grandes esforços, podemos chegar a um algoritmo simples:
Um flood fill pode ser feito basicamente de 2 maneiras: em profundidade (dfs - depth-first search) ou em largura (bfs - breadth-first search) (simplificando, pois existem mais alternativas como o “dfs with iterative deepening” ou “breadth-first scanning”).
- Breadth-first search Podemos ver a pesquisa em largura como uma torneira aberta num chão de tijoleira: a primeira a ser molhada é a que se encontra no local onde está a torneira. Todas as que estão à volta serão as próximas, e por aí em diante, numa expansão a partir de um dado centro. Este algoritmo normalmente não levanta muitos problemas a implementar, quer iterativa como recursivamente: temos o nosso vector de elementos visitados (e por visitar) e uma lista dos elementos que acabaram de ser visitados (recentemente). Para cada um destes últimos, adicionamos a uma nova lista os seus vizinhos ainda não visitados. Depois de termos feito isto com todos os elementos, passamos para a nova lista (e podemos esquecer a antiga). - Depth-first search O algoritmo de pesquisa em profundidade normalmente são mais complicados de compreender, porque dão trabalho a implementar iterativamente e muita gente não está familiarizada com a recursividade. Contudo, após esse problema estar ultrapassado, é ainda mais fácil de implementar do que o bfs.
Vamos ver o dfs como um rato à procura de um queijo perdido num labirinto: ele escolhe um caminho, e mal encontra um beco sem saída volta para trás e vira pelo primeiro caminho ainda não pesquisado. É exactamente este funcionamento que vamos implementar, desta vez em pseudo-código.
Partindo do vértice inicial, V.
função dfs(v) marcar v como visitado para todos os vértices i adjacentes a v se i não tiver sido visitado dfs(i)
- Problemas modelo Street Race [ International Olympiads in Informatics ‘95 ] Dados: um grafo direccionado, um ponto inicial e um ponto final. Encontrar todos os pontos “p” que um caminho do ponto inicial para o ponto final deve atravessar obrigatoriamente.
Análise: O algoritmo mais simples é remover cada ponto e verificar se o ponto final ainda é alcançado a partir do ponto inicial.
The Castle [ International Olympiads in Informatics ‘94 ] Dados: um mapa de um castelo (uma matriz) onde “#” representa uma parede e “.” uma casa em branco. Descobrir qual o tamanho da maior sala do castelo, após deitar abaixo uma das paredes.
Análise: Ao ser fornecida a matriz temos uma representação implícita do grafo, cada casa sendo um vértice. Não precisamos de a transformar numa matriz/lista de adjacência, podemos usá-la para saber quais os vértices adjacentes. Mais uma vez, deitamos uma parede abaixo e verificamos o tamanho da maior sala.
Fontes : Wikipedia e USACO Training Program (http://train.usaco.org)
Miguel Araújo
Vamos agora ver uma outra forma de serialização, embora use o mesmo mecanismo que o exemplo anterior. Neste exemplo os objectos serão serializados para um ByteArray. Esta técnica pode ser bastante útil para envio de grandes quantidades de objectos pela rede.
Para isso vamos usar novamente a classe Exemplo1 e uma nova classe de teste semelhante à classe Teste1 usada anteriormente.
public class Teste1 {
public static void main( String args []) { Exemplo1 e1 = new Exemplo1(001,"White"); Exemplo1 e2 = new Exemplo1(002,"Magician");
System .out.println(e1.toString());
System .out.println(e2.toString());
ObjectOutputStream out; ObjectInputStream in;
try { out = new ObjectOutputStream ( new FileOutputStream ( System .getProperty("user .dir")+ File .separator+"Exemplo1.bin")); out.writeObject(e1); out.writeObject(e2); out.flush(); out.close(); } catch ( Exception e) { e.printStackTrace(); }
Exemplo1 e3; Exemplo1 e4;
try { in = new ObjectInputStream ( new FileInputStream ( System .getProperty("user. dir")+ File .separator+"Exemplo1.bin")); e3 = (Exemplo1) in.readObject(); e4 = (Exemplo1) in.readObject();
in.close();
System .out.println(e3.toString());
System .out.println(e4.toString()); } catch ( Exception e) { e.printStackTrace(); } } }
[Teste1.java]
Esta classe é constituída apenas pelo método main visto que tem por objectivo demonstrar a serialização de objectos para um ficheiro binário. Começamos por criar alguns objectos, neste caso dois e o conteúdo desses objectos será impresso no ecrã com o auxílio do método toString implementado na classe Exemplo1. Inicializamos um ObjectOutputStream que irá criar um ficheiro binário de nome Exemplo1.bin na directoria actual. Este stream irá permitir serializar os objectos no ficheiro. Agora basta serializar os objectos, serialização essa realizada pelo método writeObject(Object object), que irá guardar o objecto dado como argumento no ficheiro de destino sob a forma binária.
Agora vamos fazer o processo inverso, passar os objectos serializados no ficheiro binário para objectos Java válidos. Este processo é tão simples como o seu inverso. Para isso iremos começar por criar dois novos objectos da classe Exemplo1 sem os inicializar. Em seguida inicializamos um ObjectInputStream que irá ler o ficheiro Exemplo1.bin criado anteriormente. Este stream contém o método readObject() que lê um objecto serializado num ficheiro. Como podemos ver, os objectos retornados pelo método readObject() são guardados nas variáveis e3 e e4 criadas anteriormente. Podemos ver também que nestas mesmas linhas é feito um cast para (Exemplo1). O cast deve ser sempre feito porque, embora os objectos serializados sejam do tipo Exemplo1 o método readObject() retorna o tipo genérico Object, que depois deve ser convertido para o tipo original desse objecto.
Por fim vamos imprimir os objectos e3 e e4 tal como fizemos anteriormente para o e1 e e2 e, se todo o processo correr normalmente, os valores de e1 serão iguais aos de e3 e o mesmo acontece com e2 e e4, o output. Neste caso será algo como o que podemos ver em seguida.
Numero = 1 | Nome = White Numero = 2 | Nome = Magician Numero = 1 | Nome = White Numero = 2 | Nome = Magician
import java.io. ByteArrayInputStream ; import java.io. ByteArrayOutputStream ; import java.io. ObjectInputStream ; import java.io. ObjectOutputStream ;
À semelhança do exemplo anterior nesta classe são criados dois objectos da classe Exemplo1 e em seguida os seus valores são impressos.
As diferenças principais começam agora. Criamos um ByteArrayOutputStream que será usado como buffer para guardar os objectos serializados, e à semelhança do exemplo anterior, inicializamos o ObjectOutputStream, mas ao contrário do primeiro exemplo, em que damos como argumento um FileOutputStream usado para escrever no ficheiro, neste caso damos como argumento o ByteArray criado. Assim cada objecto serializado será guardado no ByteArray e não num ficheiro. Depois, são serializados dois objectos Exemplo1 para o ByteArray que será usado mais tarde.
O ByteArrayOutputStream que criamos pode ser usado para várias coisas recorrendo ao método toByteArray() que retorna uma array de bytes com todos os dados do nosso ByteArrayOutputStream. As utilidades deste array são inúmeras, mas neste caso vamos apenas usá-lo para construir um ObjectInputStream, e assim iremos conseguir recuperar os nossos objectos serializados.
Por fim vamos imprimir os valores dos nossos novos objectos e iremos ver que os valores coincidem tal como no exemplo anterior.
Para terminar este artigo sobre serialização de objectos em Java iremos fazer o processo usando XML ao invés do ficheiro binário como vimos anteriormente. Comecemos por criar uma classe Exemplo2.java.
Numero = 1 | Nome = White Numero = 2 | Nome = Magician Numero = 1 | Nome = White Numero = 2 | Nome = Magician
public class Exemplo2 { private int numero; private String nome;
public Exemplo2 () { }
public Exemplo2(int numero, String nome) { this .numero = numero; this .nome = nome; }
public void setNome( String nome) { this .nome = nome; }
public void setNumero(int numero) {
public class Teste2 { public static void main( String args []) { Exemplo1 e1 = new Exemplo1(001,"White");
Exemplo1 e2 = new Exemplo1(002,"Magician");
System .out.println(e1.toString()); System .out.println(e2.toString());
ByteArrayOutputStream buffer = new ByteArrayOutputStream (); ObjectOutputStream out; ObjectInputStream in;
try { out = new ObjectOutputStream (buffer); out.writeObject(e1); out.writeObject(e2); out.flush(); out.close(); } catch ( Exception e) { e.printStackTrace(); }
Exemplo1 e3; Exemplo1 e4;
try { in = new ObjectInputStream ( new ByteArrayInputStream (buffer.toByteArray() )); e3 = (Exemplo1) in.readObject(); e4 = (Exemplo1) in.readObject();
in.close();
System .out.println(e3.toString()); System .out.println(e4.toString());
} catch ( Exception e) { e.printStackTrace(); } } }
[Teste.java]