



<html>
<head>
  <title>javabog.dk -  - Serialisering af objekter</title>
  <link rev="stylesheet" type="text/css" href="../typografi.css">
  <meta name="description" content="Lærebog i Java. Af Jacob Nordfalk. Udkommet hos Forlaget Globe">
  <meta name="keywords" content="designmønster, programmering, OOP, objekter, klasser, objektorienteret programmering, Java, JSP, lærebog, UML, IT">
</head>
<body bgcolor="#ffffff">



<a href='http://javabog.dk/'>javabog.dk</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href='kapitel17.jsp'>&lt;&lt; forrige</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href='indhold.jsp'>indhold</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href='kapitel19.jsp'>n&aelig;ste &gt;&gt;</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href='kode/'>programeksempler</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href='../index_OOP.html'>om bogen</a>

<H1 CLASS="western" STYLE="">18 <a name='afsn18'></a>Serialisering
af objekter</H1>
<P LANG="da-DK" CLASS="kapiteloversigt-western">Indhold:</P>
<UL>
  <LI VALUE=1><P LANG="da-DK" CLASS="kapiteloversigt-western">Hente og
  gemme objekter i en fil</P>
  <LI><P LANG="da-DK" CLASS="kapiteloversigt-western">Gemme egne
  klasser - interfacet Serializable og n&oslash;gleordet transient</P>
</UL>
<P LANG="da-DK" CLASS="kapiteloversigt-western">Kapitlet foruds&aelig;ttes
i <a href='kapitel19.jsp'>kapitel 19</a>, RMI.</P>
<P LANG="da-DK" CLASS="kapiteloversigt-western">Foruds&aelig;tter
<a href='kapitel15.jsp'>kapitel 15</a>, Datastr&oslash;mme og filh&aring;ndtering (<a href='kapitel12.jsp'>kapitel 12</a>, Interfaces
og <a href='kapitel7.jsp'>kapitel 7</a> om klassemetoder er en fordel).</P>


<P LANG="da-DK" CLASS="western" STYLE="">N&aring;r
et program afslutter, kan det v&aelig;re, at man &oslash;nsker at
gemme data til n&aelig;ste gang, programmet starter. 
</P>
<P LANG="da-DK" CLASS="western">Man kan selvf&oslash;lgelig skrive
programkode, der gemmer og indl&aelig;ser alle variablerne i de
objekter, der skal huskes, men der findes en nemmere m&aring;de.</P>
<P LANG="da-DK" CLASS="western">Java har en mekanisme, kaldet
<I>serialisering</I><SPAN STYLE="font-style: normal">, der best&aring;r
i, at </SPAN>objekter kan omdannes til en byte-sekvens (med
datastr&oslash;mmen ObjectOutputStream), der f.eks. kan skrives til
en fil<A CLASS="sdfootnoteanc" NAME="sdfootnote1anc" HREF="#sdfootnote1sym"><SUP>1</SUP></A>.
Denne bytesekvens kan senere, n&aring;r man har brug for objekterne
igen, deserialiseres (gendannes i hukommelsen med datastr&oslash;mmen
ObjectInputStream). Dette kunne f.eks. ske, n&aring;r programmet
starter n&aelig;ste gang.</P>
<H2 CLASS="western">18.1 <a name='afsn18.1'></a>Hente og gemme objekter</SPAN></H2>
<P LANG="da-DK" CLASS="western">Her er en klasse med to
klassemetoder, der henter og gemmer objekter i en fil:</P>
<PRE CLASS="kode-western">import java.io.*;
public class Serialisering
{
  public static void <B>gem(Object obj, String filnavn)</B> throws IOException
  {
    FileOutputStream <B>datastr&oslash;m = new FileOutputStream(filnavn)</B>;
    ObjectOutputStream <B>objektstr&oslash;m = new ObjectOutputStream(datastr&oslash;m)</B>;
    <B>objektstr&oslash;m.writeObject(obj)</B>;
    objektstr&oslash;m.close();
  }

  public static Object hent(String filnavn) throws Exception
  {
    FileInputStream datastr&oslash;m = new FileInputStream(filnavn);
    ObjectInputStream <SPAN STYLE="font-weight: medium">objektstr&oslash;m</SPAN> = new ObjectInputStream(datastr&oslash;m);
    Object obj = <SPAN STYLE="font-weight: medium">objektstr&oslash;m</SPAN>.readObject();
    <SPAN STYLE="font-weight: medium">objektstr&oslash;m</SPAN>.close();
    return obj;
  }
}</PRE><P LANG="da-DK" CLASS="western">
Du kan benytte klassen fra dine egne programmer. Her er et program,
der l&aelig;ser en liste fra filen venner.ser<A CLASS="sdfootnoteanc" NAME="sdfootnote2anc" HREF="#sdfootnote2sym"><SUP>2</SUP></A>,
tilf&oslash;jer en indgang og gemmer listen i filen igen.</P>
<PRE CLASS="kode-western">import java.util.*;
public class HentOgGem
{
  public static void main(String[] arg) throws Exception
  {
    ArrayList&lt;String&gt; l;
    try {
<B>      l = (ArrayList&lt;String&gt;) Serialisering.hent(&quot;venner.ser&quot;);</B>
      System.out.println(&quot;L&aelig;st: &quot;+l);
    } catch (Exception e) {
      l = new ArrayList();
      l.add(&quot;Jacob&quot;);
      l.add(&quot;Brian&quot;);
      l.add(&quot;Preben&quot;);
      System.out.println(&quot;Oprettet: &quot;+l);
    }

    l.add(&quot;Ven&quot;+l.size());
<B>    Serialisering.gem(l,&quot;venner.ser&quot;);</B>
    System.out.println(&quot;Gemt: &quot;+l);
  }
}</PRE>
<HR>
<PRE CLASS="kode-western">Oprettet: [Jacob, Brian, Preben]
Gemt: [Jacob, Brian, Preben, Ven3]</PRE><P LANG="da-DK" CLASS="western">
F&oslash;rste gang, programmet k&oslash;rer, opst&aring;r der en
undtagelse, fordi filen ikke findes. Den fanger vi og f&oslash;jer
&quot;Jacob&quot;, &quot;Brian&quot; og &quot;Preben&quot; til
listen. Derp&aring; tilf&oslash;jer vi &quot;Ven3&quot; og gemmer
listen.</P>
<P LANG="da-DK" CLASS="western">N&aelig;ste gang er uddata:</P>
<PRE CLASS="kode-western">L&aelig;st: [Jacob, Brian, Preben, Ven3]
Gemt: [Jacob, Brian, Preben, Ven3, Ven4]</PRE><P LANG="da-DK" CLASS="western">
K&oslash;res programmet igen, ser man, at der hver gang tilf&oslash;jes
en indgang:</P>
<PRE CLASS="kode-western">L&aelig;st: [Jacob, Brian, Preben, Ven3, Ven4]
Gemt: [Jacob, Brian, Preben, Ven3, Ven4, Ven5]</PRE>
<HR>
<PRE CLASS="kode-western">L&aelig;st: [Jacob, Brian, Preben, Ven3, Ven4, Ven5]
Gemt: [Jacob, Brian, Preben, Ven3, Ven4, Ven5, Ven6]</PRE>
<HR>
<PRE CLASS="kode-western">L&aelig;st: [Jacob, Brian, Preben, Ven3, Ven4, Ven5, Ven6]
Gemt: [Jacob, Brian, Preben, Ven3, Ven4, Ven5, Ven6, Ven7]</PRE>
<HR>
<PRE CLASS="kode-western">L&aelig;st: [Jacob, Brian, Preben, Ven3, Ven4, Ven5, Ven6, Ven7]
Gemt: [Jacob, Brian, Preben, Ven3, Ven4, Ven5, Ven6, Ven7, Ven8]</PRE>
<P LANG="da-DK" CLASS="western">Hvis nogle af de serialiserede
objekter indeholder datafelter, der er referencer til andre objekter,
serialiseres disse ogs&aring;. Ovenfor s&aring; vi, at hvis man
serialiserer en liste, bliver elementerne i listen ogs&aring;
serialiseret. Dette g&aelig;lder ogs&aring;, hvis disse elementer
selv indeholder eller er lister og s&aring; fremdeles og s&aring;
kan et helt netv&aelig;rk af objekter, med indbyrdes referencer,
blive serialiseret. Man skal derfor v&aelig;re lidt p&aring;passelig
i sine egne programmer, det kan v&aelig;re, at man f&aring;r for
meget med. 
</P>
<H2 CLASS="western">18.2 <a name='afsn18.2'></a>Serialisering af egne klasser</SPAN></H2>
<P LANG="da-DK" CLASS="western">Det er ikke alle klasser, der m&aring;/kan
serialiseres. For eksempel giver det ikke mening at serialisere
en datastr&oslash;m til en forbindelse over netv&aelig;rket (eller
bare til en fil). Hvordan skulle systemet genskabe en
netv&aelig;rksforbindelse, der har v&aelig;ret gemt p&aring;
harddisken i tre uger? Den anden ende af netv&aelig;rksforbindelsen
vil formentlig v&aelig;re v&aelig;k p&aring; det tidspunkt. 
</P>
<H3 CLASS="western">18.2.1 <a name='afsn18.2.1'></a>Interfacet Serializable</H3>
<P LANG="da-DK" CLASS="western">Serializable bruges til at markere,
at objekter <B>godt m&aring;</B> serialiseres. Hvis en klasse
implementerer Serializable, ved Java, at objekter af denne type godt
kan serialiseres. 
</P>
<P LANG="da-DK" CLASS="western">Derfor implementerer f.eks.
ArrayList, Point, String og andre objekter beregnet til at holde data
Serializable-interfacet, mens f.eks. FileWriter og Socket ikke g&oslash;r,
da de netop ikke m&aring; serialiseres (en Socket repr&aelig;senterer
jo en netv&aelig;rksforbindelse til et program p&aring; en anden
maskine og denne forbindelse ville alligevel v&aelig;re tabt, n&aring;r
objektet blev deserialiseret).</P>
<P LANG="da-DK" CLASS="western">Pr&oslash;ver man at serialisere et
objekt, der ikke implementerer Serializable, kastes undtagelsen
NotSerializableException og serialiseringen afbrydes.</P>
<P LANG="da-DK" CLASS="western">I interfacet Serializable er der ikke
nogen metoder erkl&aelig;ret og det er derfor helt uforpligtende at
implementere. S&aring;dan et interface kaldes ogs&aring; et
<I>markeringsinterface</I>, da det kun tjener til at markere
klasser som, at man kan (eller ikke kan) g&oslash;re noget bestemt
med dem.</P>
<H3 CLASS="western">18.2.2 <a name='afsn18.2.2'></a>N&oslash;gleordet transient</H3>
<P LANG="da-DK" CLASS="western">Ud over, at der kan findes
objekt-typer, som overhovedet ikke kan serialiseres, kan det ogs&aring;
ske, at der er visse dele af et objekts data, man ikke &oslash;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&oslash;gleordet
<B>transient</B><SPAN STYLE="font-weight: medium">.</SPAN></P>
<H3 CLASS="western">18.2.3 <a name='afsn18.2.3'></a>Versionsnummeret p&aring; klassen</H3>
<P LANG="da-DK" CLASS="western">Hvis du senere &aelig;ndrer klassen
og pr&oslash;ver at indl&aelig;se objekter gemt med den gamle udgave
af klassen vil det g&aring; galt! Det kan du l&oslash;se ved at
inds&aelig;tte et <FONT FACE="Courier, monospace"><FONT SIZE=2 STYLE="font-size: 9pt"><SPAN STYLE="font-weight: medium"><SPAN STYLE="background: transparent">serialVersionUID</SPAN></SPAN></FONT></FONT>
i klassen, som vist nedenfor. S&aring; vil Java indl&aelig;se
objektet og s&aelig;tte evt. nye variabler til nul.</P>
<H3 CLASS="western" STYLE="">18.2.4 <a name='afsn18.2.4'></a>Eksempel</H3>
<P LANG="da-DK" CLASS="western">Eksemplet herunder viser en klasse,
der kan serialiseres (implements Serializable), med en transient
variabel (<FONT FACE="Courier, monospace"><FONT SIZE=2 STYLE="font-size: 9pt"><SPAN STYLE="font-weight: medium"><SPAN STYLE="background: transparent">tmp</SPAN></SPAN></FONT></FONT>).
Hvis objektet serialiseres, vil <FONT FACE="Courier, monospace"><FONT SIZE=2 STYLE="font-size: 9pt"><SPAN STYLE="font-weight: medium"><SPAN STYLE="background: transparent">a</SPAN></SPAN></FONT></FONT>
blive gemt, men <FONT FACE="Courier, monospace"><FONT SIZE=2 STYLE="font-size: 9pt"><SPAN STYLE="font-weight: medium"><SPAN STYLE="background: transparent">tmp</SPAN></SPAN></FONT></FONT>
vil ikke.</P>
<P LANG="da-DK" CLASS="western">Af bekvemmelighedsgrunde er der ogs&aring;
lavet en toString()-metode.</P>
<PRE CLASS="kode-western">import java.io.*;
public class Data <B>implements Serializable</B>
{
  public int a;
  public <B>transient</B> int tmp;    <I>// transiente data bliver ikke serialiseret</I>

<I>  // Vigtigt: S&aelig;t versionsnummer s&aring; objekt kan l&aelig;ses selvom klassen er &aelig;ndret!</I>
<B>  private static final long serialVersionUID = 12345</B>; <I>// bare et eller andet nr.</I>

  public String toString()
  { 
    return &quot;Data: a=&quot;+a+&quot; tmp=&quot;+tmp;
  }
}</PRE><P LANG="da-DK" CLASS="western">
Her er et program, der l&aelig;ser en liste af Data-objekter,
tilf&oslash;jer et og gemmer den igen:</P>
<PRE CLASS="kode-western">import java.util.*;
public class HentOgGemData
{
  public static void main(String[] arg) throws Exception
  {
    ArrayList&lt;Data&gt; l;
    try {
<B>      l = (ArrayList&lt;Data&gt;) Serialisering.hent(&quot;data.ser&quot;);</B>
      System.out.println(&quot;L&aelig;st: &quot;+l);
    } catch (Exception e) {
      l = new ArrayList&lt;Data&gt;();
      System.out.println(&quot;Oprettet: &quot;+l);
    }

    Data <B>d = new Data()</B>;
    d.a   = (int) (Math.random()*100);
    d.tmp = (int) (Math.random()*100);
<B>    l.add(d);</B>

    System.out.println(&quot;Gemt: &quot;+l);
<B>    Serialisering.gem(l,&quot;data.ser&quot;);</B>
  }
}</PRE>
<HR>
<PRE CLASS="kode-western">Oprettet: []
Gemt: [Data: a=88 tmp=2]</PRE><P LANG="da-DK" CLASS="western">
K&oslash;res programmet igen, f&aring;s:</P>
<PRE CLASS="kode-western">L&aelig;st: [Data: a=88 tmp=0]
Gemt: [Data: a=88 tmp=0, Data: a=10 tmp=10]</PRE>
<HR>
<PRE CLASS="kode-western">L&aelig;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]</PRE>
<HR>
<PRE CLASS="kode-western">L&aelig;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]</PRE><P LANG="da-DK" CLASS="western">
L&aelig;g m&aelig;rke til, at den transiente variabel <FONT FACE="Courier, monospace"><FONT SIZE=2 STYLE="font-size: 9pt"><SPAN STYLE="font-weight: medium"><SPAN STYLE="background: transparent">tmp</SPAN></SPAN></FONT></FONT>
ikke bliver husket fra gang til gang.</P>
<H2 CLASS="western">18.3 <a name='afsn18.3'></a>Opgaver</SPAN></H2>
<OL>
  <LI><P LANG="da-DK" CLASS="western">K&oslash;r HentOgGemData nogle
  gange og se, at den husker data i en fil. Kig i venner.ser. <BR>Tilf&oslash;j
  et ekstra felt til Data.java, overs&aelig;t og k&oslash;r
  programmet. Hvad sker der? Hvorfor?<BR>Slet <FONT FACE="Courier, monospace"><FONT SIZE=2 STYLE="font-size: 9pt"><SPAN STYLE="font-weight: medium"><SPAN STYLE="background: transparent">serialVersionUID</SPAN></SPAN></FONT></FONT>
  og pr&oslash;v igen. Hvad sker der? Hvorfor?</P>
  <LI><P LANG="da-DK" CLASS="western">&AElig;ndr matadorspillet afsnit
  5.3 s&aring;dan, at felterne og de to spillere gemmes i en fil
  (serialiseret ned i samme datastr&oslash;m), n&aring;r de 20 runder
  er g&aring;et. Lav mulighed for at indl&aelig;se den serialiserede
  fil, s&aring; man kan spille videre p&aring; et senere tidspunkt.</P>
  <LI><P LANG="da-DK" CLASS="western">Udvid programmet til, at
  brugeren angiver filnavnet, der skal hentes/gemmes i.</P>
</OL>
<H2 CLASS="western" STYLE="">18.4 <a name='afsn18.4'></a>Avanceret</SPAN></H2>

	    Dette afsnit er ikke omfattet af Åben Dokumentslicens.<br>
	    Du skal <a href="/index_OOP.html#bestil">købe</a> bogen for at
	    måtte læse dette afsnit.

  <form action="http://javabog.dk/OOP/kapitel18.jsp#afsn18.4">
  <input type='checkbox' name='vis' value='18.4'>Jeg erklærer, at jeg allerede har købt bogen<br />
  <input type='checkbox' name='vis' value='18.4'>Jeg lover at anskaffe den i nær fremtid.<br />
  <input type='submit' value='Vis mig dette afsnit'>
  </form>

	  <H3 CLASS="western">18.4.1 <a name='afsn18.4.1'></a>Serialisere det samme objekt flere gange</H3>

	    Dette afsnit er ikke omfattet af Åben Dokumentslicens.<br>
	    Du skal <a href="/index_OOP.html#bestil">købe</a> bogen for at
	    måtte læse dette afsnit.

  <form action="http://javabog.dk/OOP/kapitel18.jsp#afsn18.4.1">
  <input type='checkbox' name='vis' value='18.4.1'>Jeg erklærer, at jeg allerede har købt bogen<br />
  <input type='checkbox' name='vis' value='18.4.1'>Jeg lover at anskaffe den i nær fremtid.<br />
  <input type='submit' value='Vis mig dette afsnit'>
  </form>

	  <H3 CLASS="western">
18.4.2 <a name='afsn18.4.2'></a>Selv styre serialiseringen af en klasse</H3>

	    Dette afsnit er ikke omfattet af Åben Dokumentslicens.<br>
	    Du skal <a href="/index_OOP.html#bestil">købe</a> bogen for at
	    måtte læse dette afsnit.

  <form action="http://javabog.dk/OOP/kapitel18.jsp#afsn18.4.2">
  <input type='checkbox' name='vis' value='18.4.2'>Jeg erklærer, at jeg allerede har købt bogen<br />
  <input type='checkbox' name='vis' value='18.4.2'>Jeg lover at anskaffe den i nær fremtid.<br />
  <input type='submit' value='Vis mig dette afsnit'>
  </form>

	  
<DIV ID="sdfootnote1">
  <P LANG="da-DK" CLASS="sdfootnote-western"><A CLASS="sdfootnotesym" NAME="sdfootnote1sym" HREF="#sdfootnote1anc">1</A>Eller
  netv&aelig;rket for den sags skyld.</P>
</DIV>
<DIV ID="sdfootnote2">
  <P LANG="da-DK" CLASS="sdfootnote-western"><A CLASS="sdfootnotesym" NAME="sdfootnote2sym" HREF="#sdfootnote2anc">2</A>Man
  bruger ofte filendelsen .ser til serialiserede objekter.</P>
</DIV>

<a href='http://javabog.dk/'>javabog.dk</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href='kapitel17.jsp'>&lt;&lt; forrige</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href='indhold.jsp'>indhold</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href='kapitel19.jsp'>n&aelig;ste &gt;&gt;</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href='kode/'>programeksempler</a>&nbsp;&nbsp;|&nbsp;&nbsp;<a href='../index_OOP.html'>om bogen</a>
<hr>
<font size=-2>http://javabog.dk/ - <b></b> af Jacob Nordfalk.
<br>
  Licens og kopiering under <a href='http://www.linuxbog.dk/licens.html'>&Aring;ben Dokumentlicens</a> (&Aring;DL)
  hvor intet andet er nævnt (79% af værket).
</font>
<br>
Ønsker du at se de sidste 21% af dette værk (226970 tegn)
skal du købe bogen. Så får du pæne figurer og layout, stikordsregister og en trykt bog med i købet.
<!-- netlæser: Wget/1.10, autoHent: true  -->
     

</body>
</html>
