Indhold:
Hente og gemme objekter i en fil
Gemme egne klasser - interfacet Serializable og nøgleordet transient
Kapitlet forudsættes af kapitel 18, RMI.
Forudsætter kapitel 14, Datastrømme og filhåndtering.
Når et program afslutter, kan det være, at man ønsker at gemme data til næste gang, programmet starter.
Man kan selvfølgelig skrive programkode, der gemmer og indlæser alle variablerne i de objekter, der skal huskes, men der findes en nemmere måde.
Java har en mekanisme, kaldet serialisering, der består i, at objekter kan omdannes til en byte-sekvens (med datastrømmen ObjectOutputStream), der f.eks. kan skrives til en fil1. Denne bytesekvens kan senere, når man har brug for objekterne igen, deserialiseres (gendannes i hukommelsen med datastrømmen ObjectInputStream). Dette kunne f.eks. ske, når programmet starter næste gang.
Her er en klasse med to klassemetoder, der henter og gemmer objekter i en fil:
import java.io.*; public class Serialisering { public static void gem(Object obj, String filnavn) throws IOException { FileOutputStream datastrøm = new FileOutputStream(filnavn); ObjectOutputStream p = new ObjectOutputStream(datastrøm); p.writeObject(obj); p.close(); } public static Object hent(String filnavn) throws Exception { FileInputStream datastrøm = new FileInputStream(filnavn); ObjectInputStream p = new ObjectInputStream(datastrøm); Object obj = p.readObject(); p.close(); return obj; } }
Du kan benytte klassen fra dine egne programmer.
Her er et program, der læser en vektor fra filen2 venner.ser, tilføjer en indgang og gemmer vektoren i filen igen.
import java.util.*; public class HentOgGem { public static void main(String arg[]) throws Exception { Vector v; try { v = (Vector) Serialisering.hent("venner.ser"); System.out.println("Læst: "+v); } catch (Exception e) { v = new Vector(); v.addElement("Jacob"); v.addElement("Brian"); v.addElement("Preben"); System.out.println("Oprettet: "+v); } v.addElement("Ven"+v.size()); Serialisering.gem(v,"venner.ser"); System.out.println("Gemt: "+v); } }
Oprettet: [Jacob, Brian, Preben] Gemt: [Jacob, Brian, Preben, Ven3]
Første gang, programmet kører, opstår der en undtagelse, fordi filen ikke findes. Den fanger vi og tilføjer "Jacob", "Brian" og "Preben" til vektoren. Derpå tilføjer vi "Ven3" og gemmer vektoren.
Næste gang er uddata:
Læst: [Jacob, Brian, Preben, Ven3] Gemt: [Jacob, Brian, Preben, Ven3, Ven4]
Køres programmet igen, ser man, at den hver gang tilføjer en indgang:
Læst: [Jacob, Brian, Preben, Ven3, Ven4] Gemt: [Jacob, Brian, Preben, Ven3, Ven4, Ven5]
Læst: [Jacob, Brian, Preben, Ven3, Ven4, Ven5] Gemt: [Jacob, Brian, Preben, Ven3, Ven4, Ven5, Ven6]
Læst: [Jacob, Brian, Preben, Ven3, Ven4, Ven5, Ven6] Gemt: [Jacob, Brian, Preben, Ven3, Ven4, Ven5, Ven6, Ven7]
Læst: [Jacob, Brian, Preben, Ven3, Ven4, Ven5, Ven6, Ven7] Gemt: [Jacob, Brian, Preben, Ven3, Ven4, Ven5, Ven6, Ven7, Ven8]
Hvis nogle af de serialiserede objekter indeholder datafelter, der er referencer til andre objekter, serialiseres disse også. Ovenfor så vi, at hvis man serialiserer en vektor, bliver elementerne i vektoren også serialiseret. Dette gælder også, hvis disse elementer selv indeholder eller er vektorer og så fremdeles, og så kan et helt netværk af objekter med indbyrdes referencer blive serialiseret. Man skal derfor være lidt påpasselig i sine egne programmer, det kan være, at man får for meget med.
Det er ikke alle klasser, der må/kan serialiseres. For eksempel giver det ikke mening at serialisere en datastrøm til en forbindelse over netværket (eller bare til en fil). Hvordan skulle systemet genskabe en forbindelse, der har været gemt på harddisken i tre uger? Den anden ende af netværksforbindelsen vil formentlig være væk på det tidspunkt (og filen kan være flyttet eller slettet).
Serializable-interfacet, der ingen metoder har defineret, bruges til at markere, at objekter godt må serialiseres. Hvis en klasse implementerer Serializable, har man fortalt Java at objekter af denne type godt kan serialiseres.
Prøver man alligevel at serialisere et objekt der ikke er Serializable, opstår der en køretidsfejl. Derfor implementerer f.eks. FileWriter og Socket ikke Serializable.
Ud over, at der kan findes objekt-typer, som overhovedet ikke kan serialiseres, kan det også ske, at der er visse dele af et objekts data, man ikke ønsker serialiseret. Hvis et objekt indeholder midlertidige data (f.eks. fortrydelses-information i et tekstbehandlingsprogram), kan man markere de midlertidige datafelter i klassen med nøgleordet transient.
Eksemplet herunder viser en klasse, der kan serialiseres (implements Serializable), med en transient variabel (tmp). Hvis et Data-objekt serialiseres, vil a blive gemt i byte-sekvensen, men tmp vil ikke.
Af bekvemmelighedsgrunde er der også lavet en toString()-metode.
import java.io.*; public class Data implements Serializable { public int a; public transient int tmp; // transiente data bliver ikke serialiseret public String toString() { return "Data: a="+a+" tmp="+tmp; } }
Her er et program, der læser en vektor af Data-objekter, tilføjer et og gemmer den igen.
import java.util.*; public class HentOgGemData { public static void main(String arg[]) throws Exception { Vector v; try { v = (Vector) Serialisering.hent("data.ser"); System.out.println("Indlæst: "+v); } catch (Exception e) { v = new Vector(); System.out.println("Oprettet: "+v); } Data d = new Data(); d.a = (int) (Math.random()*100); d.tmp = (int) (Math.random()*100); v.addElement(d); System.out.println("Gemt: "+v); Serialisering.gem(v,"data.ser"); } }
Oprettet: [] Gemt: [Data: a=88 tmp=2]
Køres programmet igen fås:
Læst: [Data: a=88 tmp=0] Gemt: [Data: a=88 tmp=0, Data: a=10 tmp=10]
Læst: [Data: a=88 tmp=0, Data: a=10 tmp=0] Gemt: [Data: a=88 tmp=0, Data: a=10 tmp=0, Data: a=52 tmp=96]
Læst: [Data: a=88 tmp=0, Data: a=10 tmp=0, Data: a=52 tmp=0] Gemt: [Data: a=88 tmp=0, Data: a=10 tmp=0, Data: a=52 tmp=0, Data: a=78 tmp=88]
Læg mærke til, at den transiente variabel tmp ikke bliver husket fra gang til gang.
Lav et program, der holder styr på en musiksamling.
Opret en klasse, der repræsenterer en udgivelse (int år,
String navn, String gruppe, String pladeselskab). Programmet skal
huske listen over udgivelser og kunne udskrive den, brugeren skal
kunne tilføje flere, og gemme og hente listen i en fil (vha.
serialisering).
Udvid programmet til, at brugeren angiver filnavnet, der skal hentes/gemmes i.
1Eller netværket for den sags skyld.
2Man bruger ofte filendelsen .ser til serialiserede objekter.