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.
|