[JAVA] Serializable probleem met een nieuw field

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • Twazerty
  • Registratie: April 2006
  • Laatst online: 22:33

Twazerty

AVCHDCoder developer

Topicstarter
Ik loop nu tegen een probleem aan en ik begrijp niet waarom dit gedrag word vertoont en hoe ik het op moet lossen. Ik zal het uitleggen zodat het probleem duidelijk word. De classes die ik laat zien heb ik even zwaar ingekort en versimpeld aangezien de originele data niet van belang is voor dit probleem.

Ik heb de volgende class met enkele velden:
Java:
1
2
3
4
5
6
7
8
9
10
public class Settings implements Serializable{
    private static final long serialVersionUID = 20L;
    private boolean value1 = true;
    private boolean value2 = true;

    public Settings(){
    }
    
    //Setters en Getters van de velden
}


Nu heb ik van bovenstaande class een object en die serialize ik naar een file genaamd settings.set. Dit bestand sla ik ergens op op de hardeschijf. Als ik vervolgens het settings.set bestand inlees om er weer een object van te maken krijg ik logischerwijs een object waar zowel value1 als value2 beide true zijn. Nu komt het probleem. Ik heb nog steeds het geserialiseerde bestand op mijn computer staan. Nu voeg ik aan mijn class een derde field toe genaamd value3. Ook deze zet ik standaard op true. Dus ik heb dit:
Java:
1
2
3
4
5
6
7
8
9
10
11
public class Settings implements Serializable{
    private static final long serialVersionUID = 20L;
    private boolean value1 = true;
    private boolean value2 = true;
    private boolean value3 = true;

    public Settings(){
    }
    
    //Setters en Getters van de velden
}


Vervolgens open ik mijn settingsfile waar het nieuwe veld (value3) en waarde nog niet bestaat. Ga ik vervolgens de inhoud van het object bekijken zie ik iets wat ik niet verwacht had. Value1 en value2 zijn beide true, maar value3 is false. Voor zover mij bekend is dat de default waarde van een boolean. Op een of andere manier word bij het toevoegen van een field altijd de default value gebruikt. Op deze manier bouw ik mijn object vanuit de geserializeerde file:
Java:
1
2
3
4
5
6
7
8
9
10
            Settings settings = null;
            FileInputStream fis = null;
            ObjectInputStream in = null;

            try {
                fis = new FileInputStream("C:\\Settings.set");
                in = new ObjectInputStream(fis);
                settings = (Settings) in.readObject();
                in.close();
            }


en save ik een object:
Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
        if(targetLocation.exists()){
            targetLocation.delete(); //Ik moet eerst even het bronbestand weggooien omdat ik met onderstaande code geen file kan overschrijven.
        }
       try{
            //Write Object
            FileOutputStream fos = null;
            ObjectOutputStream out = null;
            fos = new FileOutputStream("C:\\Settings.set");
            out = new ObjectOutputStream(fos);
            out.writeObject(settings);
            out.close();
            return true;
        }


Nog even in het kort:
- Serializeer object (met writeObject())
- Voeg field aan class toe
- Deserializeer opgeslagen object (met readObject())
- nieuwste field heeft default waarde ipv ingestelde waarde.

Waarom gebeurd dit? Dit is niet wat ik verwacht had. En hoe moet ik dit probleem nu eenvoudig oplossen. Veel verder als implementeer een eigen readObject kom ik niet met google. Wie kan mij verder op weg helpen?

Ruisende versterker: schakel je subwoofer in.


Acties:
  • 0 Henk 'm!

  • Kwistnix
  • Registratie: Juni 2001
  • Laatst online: 22:34
Da's heel simpel. De initialisatie van die variabelen vind plaats in de constructor en bij deserialisatie van een geserialiseerd object wordt de constructor niet uitgevoerd.

Acties:
  • 0 Henk 'm!

  • Killemov
  • Registratie: Januari 2000
  • Laatst online: 25-09 11:11

Killemov

Ik zoek nog een mooi icooi =)

Het serialiseren van Java-classes wordt vaker ge-/misbruikt om "even gemakkelijk" gegevens op te slaan. De geserialiseerde vorm van een object is onlosmakelijk verbonden met de specifieke class. Je liegt dus eigenlijk tegen de virtual machine door je class wel aan te passen, maar je serialversionUID niet. Als je alleen tussentijds dingen wilt opslaan is dat geen probleem, bij het wijzigen van de class wel degelijk. Beter is om het op een hele andere manier op te slaan, properties of xml bijvoorbeeld.

Hey ... maar dan heb je ook wat!


Acties:
  • 0 Henk 'm!

  • Herko_ter_Horst
  • Registratie: November 2002
  • Niet online
FallenAngel666 schreef op zondag 05 september 2010 @ 00:55:
Da's heel simpel. De initialisatie van die variabelen vind plaats in de constructor en bij deserialisatie van een geserialiseerd object wordt de constructor niet uitgevoerd.
Twee keer fout (of eigenlijk drie keer fout).

Ten eerste vindt de initialisatie in het voorbeeld helemaal niet plaats in de constructor, maar direct bij de declaratie. Ten tweede wordt tijdens deserializatie wel degelijk de (no-args) constructor uitgevoerd. Als deze niet excpliciet voorhanden is - en er is ook geen andere constructor - dan wordt de impliciete constructor aangeroepen. En ten derde tweede heeft dit ook nog eens niets met serializatie te maken, omdat het serializatiemechanisme de fields simpelweg overschrijft.

@TS
Op zich mag je een class wel aanpassen zonder z'n serialVersionUID aan te passen, maar dan krijg je inderdaad het gedrag dat je nu ziet: fields die niet in de opgeslagen serializatie zitten, worden op hun default waarde gezet. Dit gebeurt nog nadat ze door jou op true zijn gezet. Je kunt hier omheen werken door readObject() te implementeren, of - zoals Killemov hierboven al zei - een ander persistentiemechanisme te kiezen. Naast properties en XML is het implementeren van de Externalizable interface ook nog een optie.

Hier staat het hele verhaal m.b.t. versioning uitgelegd (klik op Contents om alles over serialization te weten te komen).

[ Voor 43% gewijzigd door Herko_ter_Horst op 05-09-2010 13:10 ]

"Any sufficiently advanced technology is indistinguishable from magic."


Acties:
  • 0 Henk 'm!

  • Kwistnix
  • Registratie: Juni 2001
  • Laatst online: 22:34
Java:
1
2
3
4
5
6
7
8
9
public class Foo
{
    private boolean bar = true;
    
    public Foo(boolean bar)
    {
    this.bar = bar;
    }
}


Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Foo extends java.lang.Object{
public Foo(boolean);
  Code:
   0:   aload_0
   1:   invokespecial   #10; //Method java/lang/Object."<init>":()V
   4:   aload_0
   5:   iconst_1
   6:   putfield    #13; //Field bar:Z
   9:   aload_0
   10:  iload_1
   11:  putfield    #13; //Field bar:Z
   14:  return

}


En bij deserialisatie wordt alleen de constructor van de eerst niet serialiseerbare super uitgevoerd en die van iedere super daarboven in de type-hiërarchy. In dit geval dus die van java.lang.Object en niet de constructor van de Settings klasse zelf.

Acties:
  • 0 Henk 'm!

  • Herko_ter_Horst
  • Registratie: November 2002
  • Niet online
Je hebt gelijk voor wat betreft het aanroepen van de constructor, ik was in de war met Externalizable (waar een no-args constructor verplicht is). Verder maakt het niet zoveel uit: het serializatiemechanisme overschrijft de fields hoe dan ook.

"Any sufficiently advanced technology is indistinguishable from magic."


Acties:
  • 0 Henk 'm!

  • Kwistnix
  • Registratie: Juni 2001
  • Laatst online: 22:34
Alleen de boolean fields value1 en value2.
Met field3 wordt door het serialisatie mechanisme in het geheel niets gedaan. De initialisatie van dat veld vind namelijk weldegelijk plaats in de constructor van Settings en die wordt niet uitgevoerd. De initialisatie code wordt daar namelijk naar toe gekopiëerd en dat geldt ook voor de body van init blocks (hier niet gebruikt). In volgorde van declaratie en tussen de (al dan niet expliciete) super() call en de rest van de constructor body. De fileds value1 en value2 worden dus door het serialisatie mechanisme uit de byte stream gerestored. Field value3 wordt niet expliciet geïnitialiseerd en behoudt gewoon de default waarde voor dat primitieve type; false.

Acties:
  • 0 Henk 'm!

  • Hydra
  • Registratie: September 2000
  • Laatst online: 06-10 13:59
De min of meer standaard manier om dit soort dingen te lezen en te schrijven is trouwens door middel van de http://download.oracle.co...java/util/Properties.html class. Zoals al gemeld zijn objectserializations niet bedoeld om settings in op te slaan.

https://niels.nu


Acties:
  • 0 Henk 'm!

  • Twazerty
  • Registratie: April 2006
  • Laatst online: 22:33

Twazerty

AVCHDCoder developer

Topicstarter
Hydra schreef op maandag 06 september 2010 @ 13:06:
De min of meer standaard manier om dit soort dingen te lezen en te schrijven is trouwens door middel van de http://download.oracle.co...java/util/Properties.html class. Zoals al gemeld zijn objectserializations niet bedoeld om settings in op te slaan.
Ik ben enigszins bekend met properties maar voor zover mij bekend is kun je geen ArrayLists opslaan in een property. XML achtige dingen heb ik ook al gehad maar ook vanaf gestapt. (was te veel werk maar dat lag eerder aan de gebrekkige kennis denk ik) Dus serializatie was het volgende onderwerp wat ik wou proberen. Ik sla alleen de volgende types op: boolean, int, String en ArrayList<String>

met externalizable krijg ik het niet voor elkaar om waardes van velden later toe te voegen. Dus hetgene wat in mn TS staat.

Ook als ik overschakel naar Externalizable kan ik niet zo 1,2,3 bestaande geserialiseerde objecten terugbouwen nadat ik Externalizable heb geimplementeerd. De types komen dan namelijk niet overeen. Ik zoek dus eigenlijk een (hack) oplossing zodat ik toch kan krijgen wat ik wil hebben. Ik zou natuurlijk altijd nog de boolean om kunnen draaien kwa logica maar dit is wellicht in de toekomst niet zo handig.

Ruisende versterker: schakel je subwoofer in.


Acties:
  • 0 Henk 'm!

  • Kwistnix
  • Registratie: Juni 2001
  • Laatst online: 22:34
In principe kan je een List natuurlijk wel als één property in een Properties structuur opnemen als je de List vertaald naar een CSV String, maar dat schiet het doel een beetje voorbij. Ben je al bekend met java.util.prefs.Preferences? Dat mechanisme biedt een veel flexibelere (hiërarchische) manier van opslag met behulp van nodes (meer info). Of verdiept je in een XML binding oplossing als JAXB of Castor, da's ook een optie.

[ Voor 21% gewijzigd door Kwistnix op 07-09-2010 14:28 ]

Pagina: 1