Gravar List<object> Java em ficheiros

luismauricio

Power Member
Boas tardes estou com um pequeno problema e precisava que me ajudassem a resolver.
Precisava de Gravar uma lista de objectos em linguagem Java para ficheiros e posteriormente ler os mesmos.
Para gravar eu fiz isto:

try {
FileOutputStream fos = new FileOutputStream("FornecedoresList.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(folist);
oos.close();
}
catch (Exception e) { e.printStackTrace(); }

mas não sei se é possível e não sei como resolver o problema da leitura dos dados depois.
 
Experimenta serializar a lista para Xml, e guardas o ficheiro.
Depois é só "desserializar" para voltar a ler para memória.
Só sei fazer isso em C#, não sei como é em java.
 
Na assinatura da classe, deve estar "implements Serializable". À partida esse código funciona bem, depois da dita implementação da interface.
 
Na assinatura da classe, deve estar "implements Serializable". À partida esse código funciona bem, depois da dita implementação da interface.


Exacto, isso tem que implementar o Serializable:

Se for uma classe independente sem interface, nem herança.

PHP:
import java.io.Serializable; 

(public) class XPTO implements Serializable
{                                                                   

    static final long serialVersionUID = 0L;
    ...
}
A variável estática é obrigatória, pode ser qualquer inteiro long, no caso deixei 0L.

Se for uma interface e uma classe que a implementa

PHP:
import java.io.Serializable; 

(public) Interface iXPTO extends Serializable
{                                                                   
   ...
}
PHP:
(public) Class cXPTO implements iXPTO
{
    static final long serialVersionUID = 0L;
    ...
}
Não é necessário a classe importar o java.io.Serializable, a interface faz isso, embora tenha que ter a varável static da praxe.
 
Última edição:
Desculpem lá mas podem me explicar como se eu fosse mesmo mesmo burro ( lol ).
Eu tenho uma lista de objectos do tipo cliente com alguns atributos e queria guardar essa lista num ficheiro para depois mais tarde poder aceder.
Podem me por mesmo o código. obrigado
 
Experimenta
Código:
....class Cliente implements Serializable { ......
e vê se consegues reconstruir a a lista a partir do ficheiro, e nao esquecer que muito provavelmente tens de fazer cast ao object Cliente que vais ler do ficheiro
Código:
cliente = (Cliente)ois.readObject()....
ois: ObjectInputStream.
 
Boas

Depois de ter ido buscar a minha retro escavadora, vou fazer aqui um post. :P

Preciso de fazer a mesma coisa, ou seja, ter uma lista de clientes, em que cada um tem vários atributos (Nome, Morada, telem, etc...)

Como o tópico é de à 2 anos atrás, queria saber se há uma maneira mais recente para o fazer, ou se tem de ser assim.
 
Tou a usar o NetBeans

Defini uma class Cliente.java, que tem este código:

PHP:
public class Cliente {

    String nome;
    String tel;
    String mail;

    public void Cliente(){

          nome = "";
          tel = "";
          mail = "";
    }

     public void Cliente(String nome, String tel, String mail){

          this.nome = nome;
          this.tel = tel;
          this.mail = mail;
     }
}

Depois criei uma classe ListaClientes.java, que não tenho a certeza se está correcto, mas como não dá erros no compilador...:

PHP:
import java.util.*;

public class ListaClientes {

    List lista = new LinkedList();    // Doubly-linked list

    Cliente nameArray[] = {new Cliente()};
    List<Cliente> names = Arrays.asList(nameArray);

}

Agora, era preciso poder guardar a lista no disco. Tou a usar as listas porque me parece ser a melhor maneira para adicionar clientes, alterar dados de um cliente, ler os dados de um cliente, etc...
 
A classe Cliente tem ali aqueles 2 métodos, que pelo aspecto deveriam ser os construtores, e aquilo não está bem feito. Os construtores não têm tipo de retorno. Para além disso as variáveis de instância devem ser sempre private. Isso deveria estar assim:

Código:
import java.util.*;
import java.io.*;

public class Cliente implements Serializable {

    private String nome;
    private String tel;
    private String mail;

    public Cliente() { this("","",""); }
    
    public Cliente(String nome, String tel, String mail) {
        this.nome=nome;
        this.tel=tel;
        this.mail=mail;
    }
    
    public Cliente(Cliente c) {
        this.nome=c.getNome();
        this.tel=c.getTel();
        this.mail=c.getMail();
    }
    
    public String getNome() { return this.nome; }
    public String getTel() { return this.tel; }
    public String getMail() { return this.mail; }
    
    public String toString() {
        StringBuilder s = new StringBuilder();
        s.append("-- Cliente --\n");
        s.append("Nome: ");
        s.append(this.nome);
        s.append("\nTelefone: ");
        s.append(this.tel);
        s.append("\nMail: ");
        s.append(this.mail);
        s.append("\n");
        return s.toString();
    }

    public Cliente clone() { return new Cliente(this); }
}

Acrescentei uns métodos que são úteis mais à frente. Nota a implementação da interface Serializable de modo a permitir que as instâncias de Cliente possam ser escritas para ficheiro.

Na classe da lista deves ter isto:

Código:
import java.util.*;
import java.io.*;

public class ListaClientes implements Serializable {
    private List<Cliente> lista;

    public ListaClientes() { this.lista = new ArrayList<Cliente>(); }

    public ListaClientes(Collection<? extends Cliente> col) {
        this();
        for (Cliente c : col)
            this.lista.add(c.clone());
    }

    public void addCliente(Cliente c) { this.lista.add(c.clone()); }
    
    public String toString() {
        StringBuilder s = new StringBuilder();
        s.append("-- Lista de Clientes --\n");
        for (Cliente c : this.lista)
            s.append(c.toString());
        return s.toString();
    }
    
    public void gravaObjStream(String fich) {
        ObjectOutputStream oout = null;
        try {
            oout = new ObjectOutputStream(new FileOutputStream(fich));
            oout.writeObject(this);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (oout!=null) {
                    oout.flush();
                    oout.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

Aqui tens o método que te grava uma lista de cliente num ficheiro de objectos (e não num ficheiro de texto, para esse usas um tipo de stream diferente).

Aqui tens a classe de teste:

Código:
import java.util.*;
import java.io.*;

public class Main {
    public static void main(String[] args) {
        ListaClientes l = new ListaClientes();
        l.addCliente(new Cliente("joao","2342556424","[email protected]"));
        l.addCliente(new Cliente("ze","245675675674564","[email protected]"));
        l.gravaObjStream("file.obj");
        ListaClientes nova = new ListaClientes();
        ObjectInputStream ooin = null;
        try {
            ooin = new ObjectInputStream(new FileInputStream("file.obj"));
            nova = (ListaClientes)ooin.readObject();
        } catch (IOException e) { e.printStackTrace(); }
        catch (ClassNotFoundException e) { e.printStackTrace(); }
        finally {
            try {
                if (ooin!=null)
                    ooin.close();
                } catch (IOException e) { e.printStackTrace(); }
        }
        System.out.println(nova);
   }
}

Como podes ver, carregaste com sucesso a lista previamente guardada em ficheiro.

Quanto à estrutura de dados que usaste ser lista ou assim, isso depende muito do teu objectivo, se por acaso pretendesses não ter repetidos, um Set já seria melhor, etc.
 
MUITO Obrigado!! Está impecável, vou agora adaptar isto ao meu código. :p

Quanto ao facto de haver clientes iguais, eles vão ter nº cliente, portanto nunca haverá iguais. Como é que eu posso fazer para eles serem criados de forma crescente? Isto dará:

adicionar um campo int num_cliente ao Cliente, adicionar esta função na lista "public int tamanho(){return lista.size();}" e no campo quando se chama o construtor do cliente fazer "Cliente(l.tamanho(),"blabla",...)"

Só outra coisa.

Tentei utilizar na interface gráfica uma Jlist, que era para listar os clientes e depois de seleccionar um deles, carregar num botão "abrir" que mostrava os dados todos do cliente, mas não consegui por aquilo a adicionar linhas novas na jlist. Sei que em VB, e mesmo em java utilizando a AWT list é realmente fácil da fazer. Mas as Jlist são diferentes...

Deu-me a entender que aquilo tem de receber um vector na construção da jlist...

Cumps ;)
 
Para o 1º problema, a melhor solução é criar uma variável de classe que guarde o nº de instâncias de Cliente actualmente criadas e que seja incrementada sempre que é criada uma delas:

Código:
import java.util.*;
import java.io.*;

public class Cliente implements Serializable {

    public static int numClientes = 0;
    
    public static int getNumClientes() { return numClientes; }
    
    public static void incNumClientes() { numClientes++; }

    private String nome;
    private String tel;
    private String mail;

    public Cliente() { this("","",""); }
    
    public Cliente(String nome, String tel, String mail) {
        this.nome=nome;
        this.tel=tel;
        this.mail=mail;
        this.incNumClientes();
    }

...
Depois para testar:

Código:
System.out.println(Cliente.getNumClientes());
Para o 2º problema vê por aqui: http://java.sun.com/docs/books/tutorial/uiswing/components/list.html
 
Mas eu no 1º caso não quero saber o numero de clientes totais. Eu quero é que cada cliente tenha um nº:

Nº Cliente: 1
Nome: Alberto

Nº Cliente: 2
Nome: João

etc

PS: Consegui pôr a dar, mas reparei que utilizando o Clone() ele me incrementa 2 números. Acho que é por ele criar 2 Clientes, e leva-me a pensar que assim se eu precisar de criar um cliente para comparação, por exemplo, ele me vai aumentar o nº de clientes sem eu querer.
 
Última edição:
Sim, mas tu disseste que os queres criar de forma ascendente, daí que precises, a cada momento, de saber quantos já existem para atribuíres um nº que esteja de acordo. Por exemplo, se souberes que já existem 6 clientes, então o próximo a criar terá o nº 7.

Esse pormenor realmente advém do clone() uma vez que este método invoca o construtor, que por sua vez incrementa o nº de clientes.
Se tiveres a certeza de que não vais alterar as instâncias de Cliente que adicionas à lista (isto é, se não crias a instância à parte, adicionas à lista e depois fazes alguma operação com a instância sem ser a que colocaste na lista), então podes tirar o clone. Tirando o clone, o que fazes é uma shallow copy (e não uma deep copy como estás a fazer agora), ou seja, o que colocas na lista é apenas a referência para a instância criada, pelo que qualquer alteração que faças na instância fora da lista, vai surtir efeito na lista (daí aquele parêntesis que fiz). Se optares por não retirar o clone, o que podes fazer é no próprio clone invocar um método static que definirás que te faz o decremento do nº de clientes (à semelhança do incNumClientes()).
 
Última edição:
Não está a funcionar. O problema do clone consegui resolver fazendo uma função com numClientes--.

Mas o problema é que ele não guarda no ficheiro o numero de clientes. Fazendo a execução seguida, por exemplo, criar a lista->adicionar clientes->guardar->abrir->adicionar clientes, funciona.
Mas depois de fechar o programa ele perde a variavel, e começa do zero.

-- Lista de Clientes --
-- Cliente --
N Cliente: 1
Nome: joao
Telefone: [email protected]
Mail:
-- Cliente --
N Cliente: 2
Nome: ze
Telefone: [email protected]
Mail:

//Aqui enviou para o ficheiro e abriu outra vez, para adicionar mais 2 clientes.
-- Cliente --
N Cliente: 3
Nome: Dany
Telefone: [email protected]
Mail:
-- Cliente --
N Cliente: 4
Nome: Dany12
Telefone: [email protected]
Mail:
//Aqui terminou o programa. Fiz o código para abrir o ficheiro e adicionar mais 2 clientes. A numeração começou do 1.
-- Cliente --
N Cliente: 1
Nome: Dany
Telefone: [email protected]
Mail:
-- Cliente --
N Cliente: 2
Nome: Dany12
Telefone: [email protected]
Mail:

Alguma ideia?
 
Última edição:
Obrigado baderus, por acaso, também me ajudou, já agora.

Se eu tiver um set dentro da minha classe "serializavel", um Set de objectos de classe, ele também salva esse set? Os objectos de esse set têm tb de ser serializaveis, certo?

Já agora, isso está-me a gravar num formato meio esquisto, quando o abro, não dá para gravar num formato XML?
 
É verdade, as variáveis static não são serializáveis. Mas repara, se o problema é esse, ao leres a lista do ficheiro, calculas o seu tamanho e atribuis esse valor à variável de classe numClientes. Fazes algo tipo:

Código:
// ler a lista do ficheiro
Cliente.setNumClientes(nova.tamanho());
Onde o setNumClientes seria um método estático da classe Cliente que alterava o valor de numClientes.

Se eu tiver um set dentro da minha classe "serializavel", um Set de objectos de classe, ele também salva esse set? Os objectos de esse set têm tb de ser serializaveis, certo?
Sim, o set é guardado, tal como aqui é guardada a lista.
Sim, os objectos têm também eles de ser serializáveis.
Já agora, isso está-me a gravar num formato meio esquisto, quando o abro, não dá para gravar num formato XML?
Para gravares num formato XML podes usar uma API própria, recomendo a nova adição ao Mustang: JAXB (Java API for XML Binding). Página 29
 
Está como a aço!

Isto é para um trabalho, em que o sistema é client-server. Já pus uma janela com uma formulário em que adiciona os dados do cliente através deste sistema no servidor.

Agora para actualizar a lista na Jlist, eu estava a pensar invocar um método na classe lista. Por exemplo este metodo:

PHP:
    public String[] ListaClien (){
    String [] str = {""};
    for(int i=0;i<lista.size();i++)
        str[i] = lista.get(i).getNum()+" - "+lista.get(i).getNome();
    return str;
    }

Assim o programa criava a jlist a partir deste vector. Que te parece Baderous?
E mais uma vez, muito obrigado pela ajuda ;)
 
Se te dá mais jeito, não vejo porque não. Mete só o nome do método a começar por minúscula para estar de acordo com as regras de escrita de código Java.
 
Uma duvida

Quero iniciar a jlist, e fiz isto na classe criada pelo netbeans na jframe:

PHP:
public GestaoClientes() {
    initComponents();
    String temp[]=Cliente_LN.act_listaClientes.actualiza_lista(numero_funcionario);
    }

A função actualiza_lista é esta:

PHP:
public class act_listaClientes {

    Cliente_Com.SocketClient socket23;

public String[] actualiza_lista(int num_funcionario){

   try{
   String pedido = "003#"+num_corretor;
   String outraresposta = socket23.Socke(pedido); //Linha socket

   outraresposta = Cliente_Com.Protocolo_Cliente.processarResposta(outraresposta);
   return outraresposta.split("#");

   }catch (Exception e){}

   return null;
}
}

O problema, que já não é 1ª vez que ma aparece, é que no "GestaoClientes()" não consigo aceder à outra função. Pus a "actualiza_lista(int num_corretor)" como "static", mas depois da-me erro na "Linha socket", diz que a variável é non-static e não pode ser acedida em contexto static.
Reparei que metento static atrás da variável "Cliente_Com.SocketClient socket23" resolve, mas será que é correto fazer isto?
 
Back
Topo