• Revista PROGRAMAR: Já está disponível a edição #53 da revista programar. Faz já o download aqui!

Nazgulled

Como guardar classes para ficheiro com variáveis estáticas?

19 mensagens neste tópico

Estava eu todo contentinho a por meu trabalho a funcionar às mil maravilhas todo organizadinho quando me deparei com um problema que me estragou o esquema todo. No fundo, tenho uma classe com um conjunto de métodos estáticos que irão trabalhar com as variáveis estáticas. Esta classe serve apenas para controlo(validações) e apresentação de menus, nada mais.

Ou seja, tenho por um exemplo um HashSet<String> (estático) onde guarda os códigos inseridos. Posteriormente quando pedir ao utilizador para inserir um novo código, verifico se este existe no HashSet e apresento um erro no ecrã a dizer que o código é inválido.

Esta ideia estava a funcionar muito bem até eu ter que gravar o estado (e posteriormente carrega-lo) do trabalho. Todo este trabalho é baseado numa classe principal que irá conter variáveis de instância de outras classes e essas classes variáveis de instância para outras classes, etc... Ou seja, a ideia é apenas serializar o objecto do classe principal e já está gravada a sessão. Carrega-la é igualmente fácil.

O problema é que a classe de controlo não está associada em lado nenhum na classe principal e mesmo que tivesse não ia adiantar de nada pois pelos meus testes, as variáveis estáticas não são serializadas, ou seja, não são gravadas no ficheiro (usando o ObjectOutputStream). Isto significa que quando eu gravasse a sessão, saisse do programa, voltasse a entrar e carregasse a sessão, as variáveis estáticas de controlo iriam estar a zeros/vazias, pelo que o utilizador iria puder escrever um código que já existe algures no programa, só não existe nas variáveis de controlo.

Acho que encontrei algo relativamente a guardar objectos com variáveis de instância mas isso não era recomendado ou diziam que não fazia muito sentido. É assim muito grave fazê-lo?

Que outras soluções acham que existe que me ajude a dar a volta ao problema?

Tenham em mente que o código do trabalho já está um pouco complexo/grande, pelo que será complicado mudar radicalmente a estrutura do código nesta altura do campeonato (entrega do trabalho para dia 29 e ainda me falta bastante).

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

As variáveis estáticas ou transient não são serializáveis.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Assim à primeira vista fiquei a pensar em guardares isso num ficheiro XML ou numa base de dados....

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

@Baderous

Já cheguei a essa conclusão e por isso vim aqui à procura de alternativas para ao carregar a sessão, eu possa, de certa forma, restaurar os valores das variáveis estáticas...

@vitortomaz

Para o tipo de trabalho que é e para o tempo que tenho para fazer o que me falta, não é a melhor solução... ;)

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Para o tipo de trabalho que é e para o tempo que tenho para fazer o que me falta, não é a melhor solução... ;)

tira de estático, crias um singleton e geres concorrência no acesso ao HashSet... milagres não tou a ver... :)

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

A primeira coisa que posso dizer é que estás a usar variáveis de classe para aquilo que elas não foram feitas.

São variáveis de classe e pelo que descreveste, o comportamento que queres é o de variáveis de intância.

Duas opções, implementas o teu próprio método writeObject e readObject, isto é, implementas o mecanismos de serialização para substituir o que tens por omissão. Não é tão difícil como parece, a única coisa que tens de acrescentar ao de omissão é a escrita dos valores que estão nas variáveis estáticas.

Ou implementas um padrão singleton que substitua as tuas invocações estáticas. Onde tiveres uma invocação a uma variável estática, colocas uma invocação ao teu objecto de controlo que contém variáveis de instância. Esta é a minha recomendação.

O padrão singleton adequa-se ao que pretendes fazer e é simples de implementar.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites
Duas opções, implementas o teu próprio método writeObject e readObject, isto é, implementas o mecanismos de serialização para substituir o que tens por omissão. Não é tão difícil como parece, a única coisa que tens de acrescentar ao de omissão é a escrita dos valores que estão nas variáveis estáticas.

Não percebi bem, podes explicar melhor? Ou o que estas a dizer é que tanto no método de escrita como leitura tenho de gravar/carregar, manualmente, os valores das variáveis estáticas?

Ou implementas um padrão singleton que substitua as tuas invocações estáticas. Onde tiveres uma invocação a uma variável estática, colocas uma invocação ao teu objecto de controlo que contém variáveis de instância. Esta é a minha recomendação.

O padrão singleton adequa-se ao que pretendes fazer e é simples de implementar.

Eu sei que já se falou várias vezes disto por aqui, mas não podes dar um exemplo simples?  :-[

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Eu sei que já se falou várias vezes disto por aqui, mas não podes dar um exemplo simples? :-[

public sealed class Singleton

{

    static Singleton instance=null;

    static readonly object lockObj = new object();

    Singleton()

    {

    }

    public static Singleton Instance

    {

        get

        {

            lock (lockObj )

            {

                if (instance==null)

                {

                    instance = new Singleton();

                }

                return instance;

            }

        }

    }

}

o lockObj serve para tornar Thread-Safe

traduzindo isto, sempre que quiseres fazer alguma coisa da classe Singleton usas a propriedade Instance para obter a única instância que pode existir e sobre ela é que chamas métodos

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

O padrão Singleton aproxima-se bastante do que queres fazer, ter uma classe de controlo à qual podes aceder de qualquer parte do teu código.

O código base pode ser implementado da seguinte forma:

public class Singleton {

    private static Singleton sg = new Singleton();
    
    private Singleton() {
        //construtor escondido para evitar novas instancias
    }
    
    public static Singleton getS() {
        return sg;
    }
}

Terás de adaptar para acomodar variáveis que não sejam estáticas, para que possam ser guardadas e não andarmos às voltas ;)

public class Singleton {
    private Object atributo1;
    private Object atributo2;
    private Object atributo3;
    private Object atributo4;
    private Hashtable tabela;
    private static Singleton sg = new Singleton();
    
    private Singleton() {
        //construtor escondido para evitar novas instancias
    }
    
    public static Singleton getS() {
        return sg;
    }
}

Terás de ter atenção que para acederes aos valores da hashtable ou outros atributos terás de passar pela variavel estática sg. Tens de criar métodos de acordo.

Este tipo de código deverá servir para o que queres, mas arriscaria dizer que tens um erro na forma como modelaste o problema para precisares disto.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

epá, meti código em C#  ;)

só queria acrescentar uma coisinha ao Knitter, provavelmente estás a usar só uma thread, mas cada vez é mais importante o código thread-safe, ou seja, código que mesmo em situações de concorrência está correctamente escrito.

Se vais ter um singleton deverás ter em consideração acessos concorrentes ás variáveis, problemas de visibilidade... ina pá, um mundo...

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Sinceramente não estou a ver como usar isto para o que quero fazer...

Tanto quanto eu li, Singleton serve para se criar apenas uma instância de determinado objecto mas isso é, de certa forma, irrelevante para mim... Porque eu já tentei fazer o que queria com uma classe normal, sem nada estático e não consegui fazer de forma que o código ficasse como deve ser (leia-se: bem estruturado e com todas as regras que o prof quer para uma classe). Para além disse, tive montes de problemas a alterar todo o código que tenho para que ficasse a funcionar e da maneira que tenho tudo actualmente (tudo estático) funciona às mil maravilhas...

Eu só precisava era de uma forma de guardar os valores das variáveis para quando sair do programa e voltar a entrar, puder recarregar esses valores...

E se para usar o Singleton tenho de por as variáveis sem ser estáticas, não sei se me ajuda muito... :/

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

(...) para que ficasse a funcionar e da maneira que tenho tudo actualmente (tudo estático) funciona às mil maravilhas...

Até podia estar a funcional, mas reitero que estaria mal analisado. Estás a usar uma coisa para algo que ela não foi feita.

Eu só precisava era de uma forma de guardar os valores das variáveis para quando sair do programa e voltar a entrar, puder recarregar esses valores...

Se era apenas isso que precisavas então, mais uma razão para dizer que isso estava mal.

Usa as properties do java, é para isso que elas servem.

Properties p = new Properties();

p.put()

p.putBoolean()

(...)

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Desconhecia propriedades em Java mas isso envolve dois problemas para mim:

1) Tenho de as estar a aprender e implementa-las no meu código não me parece que vá ser a melhor solução pois algo me diz que vou ter de mudar radicalmente o meu código e como disse, não tenho tempo para isso.

2) A ideia era gravar a sessão num único ficheiro e pelo que acabei de ler (assim muito rápido), as propriedades iriam ficar gravadas num ficheiro à parte.

Anyway, tentei implementar o Singleton no meu código, usando variáveis sem ser estáticas para guardar os valores de controlo. Criei uma instância do singleton na classe principal e quando gravei o objecto da classe principal e voltei a carrega-lo reparei que o valor privado (não estático) do singleton não foi gravado... Nem precisava de carregar, podia abrir o ficheiro gravado num editor de texto que se vê facilmente se ele guardou lá os valores ou não (mesmo que o ficheiro tenha um conjunto de caracteres que não se percebe).

Se me deres uns minutos, vou preparar um exemplo com o meu código e apresento já aqui...

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Classe start.java:

public class Start {

public static Example main() {
	Example example = new Example();

	// Criação das mais diversas instâncias

	return example;
    }

}

Esta classe e a forma como eu chamo este main, tem mesmo de ser assim (devido ao trabalho que estou a fazer).

Classe Singleton.java:

import java.util.*;
import java.io.*;

public class Singleton implements Serializable {
private HashSet<String> codigos;

private static Singleton sg = new Singleton();

private Singleton() {
	codigos = new HashSet<String>();
}

public static Singleton getInstance() {
	return sg;
}

public void addCodigo(String codigo) {
	this.codigos.add(codigo);
}
}

Classe Example.java:

import java.util.*;
import java.io.*;

public class Example implements Serializable {
private String teste;
private Singleton sg;

public Example() {
	this.teste = "Olá Mundo!";
}

public Singleton getSG() {
	return this.sg.getInstance();
}

public static void main(String[] args) {

	Example example = Start.main();

    		example.getSG().addCodigo("ABC");
    		example.getSG().addCodigo("DEF");
    		example.getSG().addCodigo("XYZ");

	try {
    			example.gravaObjecto("teste.dat");
	} catch(Exception e) {
		System.out.println(e.getMessage());
	}
}

public void gravaObjecto(String ficheiro) throws IOException {
	ObjectOutputStream objOutputStream = new ObjectOutputStream(new FileOutputStream(ficheiro));
	objOutputStream.writeObject(this);
	objOutputStream.close();
}
}

Output do ficheiro gravado:

¬í sr ExampleÀ…àðÎ#Ù L sgt LSingleton;L testet Ljava/lang/String;xppt Olá Mundo!

Não se vê "ABC", "DEF", nem "XYZ" em lado algum, mas o "Olá Mundo!" está lá...

O que me está a escapar?

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Não deve ser disso mas mete um objOutputStream.flush() antes do close

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Isso já se estava a discutir no outro tópico, dá lá uma olhadela ;)

BTW, adicionei o seguinte código ao Singleton:

protected Object readResolve() { return this.sg; }

Segundo li, para quando fizer "deserialize" ele devolver sempre a mesma instância...

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Com o HashSet na classe Example já serializa...  :hmm:

import java.util.*;
import java.io.*;

public class Example implements Serializable {
/**
 * 
 */
private static final long serialVersionUID = 1L;
private String teste;

public Example() {
	this.teste = "Olá Mundo!";
}


private HashSet<String> codigos = new HashSet<String>();


public void addCodigo(String codigo) {
	this.codigos.add(codigo);
}



public static void main(String[] args) {

	Example example = new Example();

    		example.addCodigo("ABC");
    		example.addCodigo("DEF");
    		example.addCodigo("XYZ");

	try {
    			example.gravaObjecto("teste.dat");
	} catch(Exception e) {
		System.out.println(e.getMessage());
	}
}

public void gravaObjecto(String ficheiro) throws IOException {
	ObjectOutputStream objOutputStream = new ObjectOutputStream(new FileOutputStream(ficheiro));
	objOutputStream.writeObject(this);
	objOutputStream.close();
}
}

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Esqueçam isto, acabei por fazer de outra maneira mais simples porque não estou para me chatear com isto e já tenho mesmo pouco tempo para acabar o resto do trabalho... Se tivesse tempo, ainda era capaz de ser uma boa solução, mas infelizmente não tenho. Ando todos os dias a fazer noitadas a ver se acabo isto a tempo de entregar o trabalho completo.

Só para finalizar, acabei por criar variáveis de instância na classe principal (o que me leva a outra dúvida mas isso vai para um tópico diferente) e desta forma é fácil gravar os objectos para o disco e posteriormente recarrega-los...

Obrigado a todos pela ajuda.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Bem, este problema está a andar em voltas e voltas.

Neste caso, realmente precisavas de um flush, estive à procura de mais documentação e não encontrei nada de especial sobre se esta stream faz flush automático ou não, peço desculpa se induzi alguém em erro noutro tópico.

Um problema com essa implementação de Singleton, a variável é estática, não vais ser lida correctamente. O que vai acontecer é que os dados da hashset vão ser lidos mas a variável sg dentro da classe Singleton vai estar a null. Terás de adaptar o teu código a essa situação.

Depois deverias aceder sempre à variável estática quando precisares de aceder aos dados da hashset, example.getSG().addCodigo("ABC"); mudas para Singleton.getInstance().addCodigo("ABC").

Um exemplo de uso de propriedades no teu caso:

package nazgulled;

import java.io.FileReader;
import java.io.IOException;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
*
* @author Knitter
*/
public class Settings {

    private static Properties p;

    private Settings() {
    }
    
    public static void createProperties(String settingsFile) {
        p = new Properties();
        FileReader fr = null;
        try {
            fr = new FileReader(settingsFile);
            p.load(fr);
        } catch (IOException ex) {
            Logger.getLogger(Settings.class.getName()).log(Level.SEVERE, "Loading of properties failed", ex);
        } finally {
            if(fr != null) {
                try {
                    fr.close();
                } catch (IOException ex) {
                    //ignore
                }
            }
        }
    }
    
    public static Properties getSettings() {
        if(p == null) {
            throw new RuntimeException("Settings not loaded, use createProperties first.");
        }
        return p;
    }
}

Uso das propriedades, pode ser usado em qualquer classe do programa, apenas invocando o método estático getSettings().

package nazgulled;

import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
*
* @author Knitter
*/
public class Example2 {

    public static void main(String[] args) {
        //Ficheiro tem de existir primeiro
        File f = new File("test.properties");
        try {
            f.createNewFile();
        } catch (IOException ex) {
            Logger.getLogger(Example2.class.getName()).log(Level.SEVERE, null, ex);
        }
        
        
        Settings.createProperties("test.properties");
        Settings.getSettings().put("Value1", "ABCD");
        Settings.getSettings().put("Value2", "BCD");
        Settings.getSettings().put("Value3", "CD");

        System.out.println(Settings.getSettings().getProperty("Value1", "Default Value"));
        System.out.println(Settings.getSettings().getProperty("Value2", "Default Value"));
        System.out.println(Settings.getSettings().getProperty("Value3", "Default Value"));

        //Sem valor por omissao
        System.out.println(Settings.getSettings().getProperty("Value1"));
        System.out.println(Settings.getSettings().getProperty("Value2"));
        System.out.println(Settings.getSettings().getProperty("Value3"));

    }
}

O código para criar o ficheiro é desnecessário desde que tenhas o ficheiro já criado. Pode também ser passado para o método que cria as propriedades.

Se pretendes guardar propriedades da aplicação então estar seria uma opção mais correcta.

Mas andamos aqui tanto às voltas que nem sei o que realmente queres fazer. Eventualmente podes até ter as propriedades como variáveis normais dentro de uma classe principal da tua aplicação.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Crie uma conta ou ligue-se para comentar

Só membros podem comentar

Criar nova conta

Registe para ter uma conta na nossa comunidade. É fácil!


Registar nova conta

Entra

Já tem conta? Inicie sessão aqui.


Entrar Agora