Az alábbi egyszerű példán keresztül bemutatjuk, hogyan lehet a perzisztens
tulajdonságokat implementálni a Java programozási nyelven. A példa
alapötlete Eric Williamstől származik (williams@sky.net). Az eltárolandó
osztályokat és az Iro , Olvaso alkalmazásokat mi írtuk,
hogy bemutassuk: a módszer elég jól alkalmazható osztály-hierarchiák esetében is.
A választott tároló objektum egy adatfolyam, amely megfelel
a DataInput , ill.DataOutput interfésznek, a formátum
pedig a DataInputStream ill.DataOutputStream
által használt hordozható bináris alak, ami elé kiírjuk az osztály nevét.
A perzisztens tulajdonságokat a import PersistentInput; import PersistentOutput; import java.io.IOException; public interface Persistent { public void write(PersistentOutput out) throws IOException; public void read(PersistentInput in) throws IOException; }A PersistentOutput.java és PersistentInput.java interfészek a DataOutput és DataInput kiterjesztései. Az elemi
típusok kiírását és beolvasását végző metódusokat kiegészíti a Persistent
interfésznek megfelelő objektumok kiírásával és beolvasásával. A két interfészt
a PersistentInputStream.java és
PersistentOutputStream.java
implementálja.
A public class Gepjarmu implements Persistent { /* A Persistent interfész megvalósítása */ public void write(PersistentOutput out) throws IOException { out.writeUTF(rendszam); out.writeUTF(tulajdonos); } public void read(PersistentInput in) throws IOException { rendszam = in.readUTF(); tulajdonos = in.readUTF(); } }Az osztályokat az Iro.java és Olvaso.java osztályokkal tesztelhetjük.
Forráskódok: DataInput
kiterjesztése. Az elemi típusok beolvasását végző metódusokat kiegészíti
a Persistent interfésznek megfelelő objektumok beolvasásával.
DataInputStream segítségével megvalósítja a PersistentInput
interfész metódusait.
PersistentInput , output műveletekre.
PersistentInputStream , output műveletekre.
Gepjarmu leszármazottja.
Gepjarmu leszármazottja.
|
A perzisztencia nyelvi szintű támogatására a JavaSoft egy speciális eszközt
fejlesztett ki: az Object Serialization API-t. Ez a programozói
interfész a Java 1.0x verzióknak még nem volt része, a Java 1.1 verzió viszont
tartalmazza a java.io csomag részeként. Az segítségével tetszőleges
osztályhoz tartozó objektumok perzisztenssé tehetők.
Az alábbi program jól demonstrálja az Object Serialization viselkedését
(kölcsönösen) egymásra hivatkozó objektumok esetében. A import java.io.*; public class Ciklikus implements Serializable { ... }A CiklikusOlvaso egyetlen objektumot hoz létre általános
Object típussal. Az Object Serialization eltárolta az elmentett
objektum(ok) típusát, így nincsen arra szükség, hogy a pontos típust
deklaráljuk, az újrakreálás mindenképpen az eredeti típust és az objektum
teljes hivatkozási láncát állítja helyre.
Forráskódok: Ciklikus osztály,
és a CiklikusIro valamint CiklikusOlvaso teszt
osztályok.
|
Az előző fejezetben ismertetett autóbusz-teherautó példát kissé módosítottuk,
hogy bemutathassuk az Object Serialization néhány eszközét. Az általános
Gepjarmu bázisosztály formálisan implementálja a Serializable
interfészt, hogy jogosult legyen a szerializációra. Az osztály
tulajdonos mezője protected transient minősítésűvé vált,
hogy a bizalmas tulajdonos információ ne kerüljön kiírásra a szerializáció során.
Mivel beolvasáskor az objektum tulajdonos mezője null
értékre állítódna, a Gepjarmu osztály
(forrás: Gepjarmu.java)
definiál egy readObject metódust, hogy a tulajdonos
mezőnek saját alapértelmezését állítsa be ismeretlen
nevű attribútum értéket.
import java.io.*; public class Gepjarmu implements Serializable { /* A tulajdonos alapértelmezett értékének beállítása */ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); tulajdonos = ISMERETLEN; } }Az Autobusz osztály (Autobusz.java)
implementálója úgy dönt, hogy a
Gepjarmu bázisosztállyal szemben ő mégis propagálni
szeretné a transient minősítésű tulajdonos
attribútumot, ezért saját writeObject és
readObject |
Egy osztály szerializálásakor a kiírt mezőket kétféleképpen adhatjuk meg.
Alapértelmezésként a szerializálható osztály definíciójából indulunk ki,
és az összes mezőt kiírjuk vagy visszaolvassuk a statikus és tranziens mezők
kivételével. Erre láttunk példát az előző fejezetben. A Java 2 egy új
eleme szerint ez az alapértelmezett működés módosítható, ha az osztály ún.
perzisztens mezőket definiál.
Az alábbi példában a /* Teglalap.java: eredeti verzió */ public class Teglalap implements java.io.Serializable { public Teglalap( int ax1, int ay1, int ax2, int ay2) { x1 = ax1; y1 = ay1; x2 = ax2; y2 = ay2; } public void print() { System.out.println( "Téglalap ("+x1+","+y1+","+x2+","+y2+")" ); } private int x1, y1, x2, y2; }A téglalap egy új verziója már egy új belső osztályt használ fel. Ahhoz, hogy az új verzió együtt tudjon működni a régivel, szimulálnia kell a régi formátumot. Ehhez definiálnia kell a writeObject és
readObject metódusait, amik elvégzik a szükséges átalakításokat.
/* Téglalap.java: új verzó */ import java.io.*; public class Teglalap implements java.io.Serializable { public Teglalap( Pont q1, Pont q2) { p1 = q1; p2 = q2; } public void print() { System.out.println(" Téglalap ("+p1.x+","+p1.y+") ("+p2.x+","+p2.y+")" ); } private Pont p1, p2; // SUID az eredeti Teherauto osztályból generálva. static final long serialVersionUID = 5830705690428599645L; // Az interfész mezok definiálása private static final ObjectStreamField[] serialPersistentFields = { new ObjectStreamField("x1", Integer.TYPE), new ObjectStreamField("y1", Integer.TYPE), new ObjectStreamField("x2", Integer.TYPE), new ObjectStreamField("y2", Integer.TYPE), }; private void writeObject(ObjectOutputStream out) throws IOException { ObjectOutputStream.PutField fields = out.putFields(); fields.put("x1", p1.x); fields.put("y1", p1.y); fields.put("x2", p2.x); fields.put("y2", p2.y); out.writeFields(); } private void readObject(ObjectInputStream s) throws IOException { ObjectInputStream.GetField fields = null; try { fields = s.readFields(); } catch (Exception ClassNotFoundException) { throw new IOException(); } int x1 = (int) fields.get("x1", 0); int y1 = (int) fields.get("y1", 0); int x2 = (int) fields.get("x2", 0); int y2 = (int) fields.get("y2", 0); p1 = new Pont( x1, y1); p2 = new Pont( x2, y2); } public static class Pont { public Pont( int ax, int ay) { x = ax; y = ay; } public int x, y; } }A két verzió képes az együttélésre. Az EredetiIro.java, a FejlesztettIro.java és Olvaso.java programok kölcsönösen írják/olvassák egymást.
Forráskódok: Teglalap osztály.
Teglalap verziót.
Teglalap osztály, implementálásához a Pont belső osztályt
használjuk.
Eredeti könyvtárban levő Olvaso programmal.
|
A helyettesítés szintén a Java 2-ben megjelenő új eszköz. Az
Object visszatérő típusú, writeReplace
metódus használatával a szerializálás alatt álló objektum delegálhat
maga helyett egy másik objektumot, ami kiíródik az adatfolyamra.
Ez a helyettesítő objektum esetleg más típusú is lehet, mint a helyettesített.
A következő példában visszatérünk jól ismert gépjármű-osztályos példánkhoz.
A felhasználói igények időközben létrehozták a import java.io.*; public class Mikrobusz extends Gepjarmu { public Mikrobusz(String r, String t, int u) { super( r, t ); ulesek = u; } public void print() { System.out.println( "Mikrobusz, rendszam="+rendszam+ " tulajdonos="+tulajdonos+" ulesek szama="+ulesek); } private int ulesek; /* A Mikrobusz helyett Autobusz eltárolása */ public Object writeReplace() throws ObjectStreamException { return new Autobusz( super.rendszam, super.tulajdonos, ulesek - 1); } }Forráskódok: Gepjarmu
leszármazottja.
Gepjarmu
leszármazottja.
Gepjarmu
leszármazottja. Szerializáláskor maga helyett egy újonnan létrehozott
Autobusz objektumot ír ki.
Parkolo -t és a hivatkozott objektumokat.
Parkolo -t és az objektumokat.
|