/ Forside / Teknologi / Udvikling / Java / Nyhedsindlæg
Login
Glemt dit kodeord?
Brugernavn

Kodeord


Reklame
Top 10 brugere
Java
#NavnPoint
molokyle 3688
Klaudi 855
strarup 740
Forvirret 660
gøgeungen 500
Teil 373
Stouenberg 360
vnc 360
pmbruun 341
10  mccracken 320
serialisering
Fra : Morten Nedertoft


Dato : 22-01-01 20:31

Hej

Jeg sidder og taenker over hvorfor

   private synchronized void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException

ikke bare er public. Er der en der har svaret?

mvh. Morten N

 
 
Niels Ull Harremoës (23-01-2001)
Kommentar
Fra : Niels Ull Harremoës


Dato : 23-01-01 22:26


"Morten Nedertoft" <mmn@pr-group.sdu.dk> skrev i en meddelelse
news:3A6C8A7A.1347CE69@pr-group.sdu.dk...
> Hej
>
> Jeg sidder og taenker over hvorfor
>
> private synchronized void writeObject(java.io.ObjectOutputStream s)
> throws java.io.IOException
>
> ikke bare er public. Er der en der har svaret?

Muligvis, fordi denne metode slet ikke behøver at findes. Det er jo op til
den, der implementerer standardbibliotekerne for en given platform, hvordan
han implementerer serialisering. Og det kunne jo være, at han bare brugte en
hurtig native implementering til at gemme objekterne.

En anden grund til at metoden ikke findes, er at jo færre metoder, der er i
Object, jo bedre. Ved de fleste implementeringer af JVM'en, giver hver
eneste metode i Object en ekstra indgang (antagelig 4 bytes) i
funktionstabellen for hver eneste klasse! Det gør faktisk en forskel i det
lange løb.

Den væsentligste grund er nok, at du aldrig selv bør kalde writeObject. Hvis
du gør det, går du uden om den logik, der checker at hvert objekt kun bliver
skrevet een gang til en stream, uanset hvor mange gange den forekommer i en
datastruktur. Der er også andre ting, som fx skrivning af
klasse-information, som kortsluttes.

Et eksempel: Hvis writeObject var public, ville følgende implementation jo
være fristende:

public class MyVector {
public MyObject values[];
....
public synchronized void writeObject(ObjectOutputStream s) {
if (values == null) s.write(-1);
else {
s.write(values.length);
for(int i = 0; i < values.length;i++) MyObject.writeObject(s);
};
// more stuff, including that which caused us to define a writeObject in
the first place.
}

// corresponding readObject
....
}

Men hvad sker der så, hvis man fx havde

MyVector v = new MyVector();
MyObject o = new MyObject();
v.add(o);
v.add(o);
someOutStream.write(v);

Nu er der to referencer til det samme MyObject i vektoren. Men fordi du
kalder writeObject selv, vil det blive skrevet to gange når vi serialiserer
ud i en stream - du giver ikke stream'en en chance for at opdage, at det er
det samme objekt. Og når du så læser det ind igen, får du nu to forskellige
objekter - dvs. ikke den samme datastruktur som du skrev. Ups!


>
> mvh. Morten N



Morten Nedertoft (24-01-2001)
Kommentar
Fra : Morten Nedertoft


Dato : 24-01-01 11:52

"Niels Ull Harremoës" wrote:
>
> "Morten Nedertoft" <mmn@pr-group.sdu.dk> skrev i en meddelelse
> news:3A6C8A7A.1347CE69@pr-group.sdu.dk...
> > Hej
> >
> > Jeg sidder og taenker over hvorfor
> >
> > private synchronized void writeObject(java.io.ObjectOutputStream s)
> > throws java.io.IOException
> >
> > ikke bare er public. Er der en der har svaret?
>
> Muligvis, fordi denne metode slet ikke behøver at findes. Det er jo op til
> den, der implementerer standardbibliotekerne for en given platform, hvordan
> han implementerer serialisering. Og det kunne jo være, at han bare brugte en
> hurtig native implementering til at gemme objekterne.

Man kan da bare lade vaere med at overskrive Object-klassens
writeObject.

> En anden grund til at metoden ikke findes, er at jo færre metoder, der er i
> Object, jo bedre. Ved de fleste implementeringer af JVM'en, giver hver
> eneste metode i Object en ekstra indgang (antagelig 4 bytes) i
> funktionstabellen for hver eneste klasse! Det gør faktisk en forskel i det
> lange løb.

Er det noget du har laest, eller er det noget du selv har fundet ud af?

> Den væsentligste grund er nok, at du aldrig selv bør kalde writeObject. Hvis
> du gør det, går du uden om den logik, der checker at hvert objekt kun bliver
> skrevet een gang til en stream, uanset hvor mange gange den forekommer i en
> datastruktur. Der er også andre ting, som fx skrivning af
> klasse-information, som kortsluttes.

Man kunne jo aendre signaturen for writeObject i Object-klassen, og lade
den returnere en boolsk vaerdi for om objektet allerede er blevet
serialiseret. Saa kunne man implementere writeObject noget a la:

public boolean writeObject(ObjectOutStream s) throws IOException
{
if (!super.writeObject(s))
{.....}
}

> >
> > mvh. Morten N

Niels Ull Harremoës (24-01-2001)
Kommentar
Fra : Niels Ull Harremoës


Dato : 24-01-01 19:12

"Morten Nedertoft" <mmn@pr-group.sdu.dk> skrev i en meddelelse
news:3A6EB3D6.8FF32753@pr-group.sdu.dk...
> "Niels Ull Harremoës" wrote:
> >
> > "Morten Nedertoft" <mmn@pr-group.sdu.dk> skrev i en meddelelse
> > news:3A6C8A7A.1347CE69@pr-group.sdu.dk...
> > > Hej
> > >
> > > Jeg sidder og taenker over hvorfor
> > >
> > > private synchronized void writeObject(java.io.ObjectOutputStream s)
> > > throws java.io.IOException
> > >
> > > ikke bare er public. Er der en der har svaret?
> >
> > Muligvis, fordi denne metode slet ikke behøver at findes. Det er jo op
til
> > den, der implementerer standardbibliotekerne for en given platform,
hvordan
> > han implementerer serialisering. Og det kunne jo være, at han bare
brugte en
> > hurtig native implementering til at gemme objekterne.
>
> Man kan da bare lade vaere med at overskrive Object-klassens
> writeObject.
Ja, men det er jo slet ikke sikkert, at det overhovedet er nødvendigt at
lave det funktionskald - det hele kunne jo være native. Nå, men pyt, det er
nokikke den væsentligste grund.
>
> > En anden grund til at metoden ikke findes, er at jo færre metoder, der
er i
> > Object, jo bedre. Ved de fleste implementeringer af JVM'en, giver hver
> > eneste metode i Object en ekstra indgang (antagelig 4 bytes) i
> > funktionstabellen for hver eneste klasse! Det gør faktisk en forskel i
det
> > lange løb.
>
> Er det noget du har laest, eller er det noget du selv har fundet ud af?
Det er noget jeg slutter mig til ud fra min viden om hvordan man
implementerer oversættere og fortolkere. En anden detalje er, at for at gøre
Java så simpelt som muligt at lære, bør Object have så få metoder som
muligt. Jeg kan komme på flere andre metoder, som kunne være praktiske - fx.
en isLocal() ifb. med RMI.

> > Den væsentligste grund er nok, at du aldrig selv bør kalde writeObject.
Hvis
> > du gør det, går du uden om den logik, der checker at hvert objekt kun
bliver
> > skrevet een gang til en stream, uanset hvor mange gange den forekommer i
en
> > datastruktur. Der er også andre ting, som fx skrivning af
> > klasse-information, som kortsluttes.
>
> Man kunne jo aendre signaturen for writeObject i Object-klassen, og lade
> den returnere en boolsk vaerdi for om objektet allerede er blevet
> serialiseret. Saa kunne man implementere writeObject noget a la:
>
> public boolean writeObject(ObjectOutStream s) throws IOException
> {
> if (!super.writeObject(s))
> {.....}
> }

Ja. Men hvad hvis man glemte det? Jo mere programmøren er nødt til at gøre,
jo større er sandsynligheden for at han laver fejl. Et godt API prøver at
forebygge fejl fra den, der bruger det.

Et andet eksempel:

Antag vi har tre klasser, A, B og C. B extender A, og C extender B.
A har en writeObject, B er bare markeret som Serializable og har et par
private felter, C skal også have en writeObject. Hvordan vil du implementere
writeObject i klasse C? Husk, da du skrev B anede du ikke, at du en dag
ville lave C. Og du bør jo heller ikke være nødt til at tænke over, om A nu
serialiserer med writeObject eller med standard serialiseringen.

Hvis man bruger den kode du foreslog ovenfor, får du ikke skrevet felterne i
B.

public boolean writeObject(ObjectOutStream s) throws IOException
{
if (!super.writeObject(s)) // Ups! kalder A's writeObject
{.....}
}

Det er derfor, at der i specifikationen af writeObject explicit står, at du
ikke behøver gemme værdier i forældreklasser - serialiseringsmekanismen
løber igennem hierarkiet og kalder enten writeObject eller gemmer
ikke-transient felter for hver kalsse efter tur.

Har du i øvrigt læst specifikationens afsnit om serialisering
(http://java.sun.com/j2se/1.3/docs/guide/serialization/spec/serialTOC.doc.ht
ml) - den gennemgår
en hel del af detaljerne omkring serialisering. Jeg så en gang en bog, der
gik lidt bag om designet af Java og standard API'et - men jeg kan desværre
ikke huske hvad den hed eller for

Filip Larsen (25-01-2001)
Kommentar
Fra : Filip Larsen


Dato : 25-01-01 00:49

Morten Nedertoft skrev

> Jeg sidder og taenker over hvorfor
> private synchronized void writeObject(java.io.ObjectOutputStream s)
> throws java.io.IOException
> ikke bare er public. Er der en der har svaret?

Metoden writeObject har en ret præcis semantik. Fx. skal writeObject for en bestemt klasse ikke bekymre sig om hverken super- eller subklassers serialiseringsbehov. Det klarer Javas serialiseringsmekanisme automatisk.

Det betyder samtidig, at det ikke giver særlig god mening at have metoden som andet end private, da man for at serialisere et objekt skal kalde alle tilstedeværende writeObject metoder i klassehierarkiet. Hvis disse var andet end private, hvad ville den præcise mening af at kalde writeObject på en subklasse hvis baseklasse også har en writeObject metode? Ifølge normal Java kaldesemantik vil kun subklassens metode blive kaldt. Hvis man først har valgt at lade det være valgfrit for hver enkelt klasse om den har brug for en writeObject metode, så har man faktisk inskrænket sig til kun at tale om private metoder.

Man kunne selvfølgelig have lavet serialiseringsmekanismen anderledes så den fx. krævede at read/writeObject metoder altid var tilstede og de altid kædede super metoderne rigtigt, hvilken fx. (næsten) er måden hvorpå man konstruere objekter. Men designerne bag mekanismen ville gerne gøre det nemt at bruge den, således at fx. en almindelig "dum" attributklasse (lidt svarende til en C++ struct) bare uden videre skulle kunne serialiseres. Ved at bruge reflection gjorde designerne det til en leg at lave serialiserbare klasser.

Faktisk kan man få det bedste af begge verdener, da der ud over Serializable også er et Externalizable interface man kan vælge at implementere istedet. Dermed skal man implementere to public metoder, hhv. read- og writeExternal der i modsætning til read- og writeObject skal være implementeret og som dermed påtager sig det "fulde ansvar" for serialiseringen af klassen og alle dens superklasser. Det anbefales normalt at gøre rejseivrige klasser Externalizable istedet for Serializable, da førstnævnte normalt resulterer i en hurtigere serialisering og en mindre stream.

Ydermere er der også et vist sikkerhedsaspekt i at lade writeObject være private. Man kan så fx. ikke delvist overskrive et objekt der allerede er initialiseret med "følsomme" data. Man kan ganske vist komme med en stream, men det er ikke helt lige så slemt, da man dermed også skal kunne komme med alle data i streamen først. Det kan bemærkes, at der af samme grund advares kraftigt mod at benytte Externalizable for klasser med sådan følsom data.


Se i øvrigt også Java Object Serialization Specification der er en normal del af J2SE dokumentationen, fx. via http://java.sun.com/j2se/1.3/docs/guide/serialization/index.html.


Mvh,
---
Filip Larsen <filip.larsen@mail.dk>



Morten Nedertoft (25-01-2001)
Kommentar
Fra : Morten Nedertoft


Dato : 25-01-01 09:20

Filip Larsen wrote:
>
> Morten Nedertoft skrev
>
> > Jeg sidder og taenker over hvorfor
> > private synchronized void writeObject(java.io.ObjectOutputStream s)
> > throws java.io.IOException
> > ikke bare er public. Er der en der har svaret?
>
> Metoden writeObject har en ret præcis semantik. Fx. skal writeObject for en bestemt klasse ikke bekymre sig om hverken super- eller subklassers serialiseringsbehov. Det klarer Javas serialiseringsmekanisme automatisk.
>
> Det betyder samtidig, at det ikke giver særlig god mening at have metoden som andet end private, da man for at serialisere et objekt skal kalde alle tilstedeværende writeObject metoder i klassehierarkiet. Hvis disse var andet end private, hvad ville den præcise mening af at kalde writeObject på en subklasse hvis baseklasse også har en writeObject metode? Ifølge normal Java kaldesemantik vil kun subklassens metode blive kaldt. Hvis man først har valgt at lade det være valgfrit for hver enkelt klasse om den har brug for en writeObject metode, så har man faktisk inskrænket sig til kun at tale om private metoder.

Jeg forstaar ikke hvordan ObjectOutputStream baerer sig ad med at kalde
en privat metode. Er det tilladt via java.lang.reflect.Method?
Du skriver, at Serializable goer det til en leg at serialisere. Det
giver jeg dig ret i, men jeg havde nok alligevel foretrukket en
mekanisme der var implementeret uden mystik. Maaske er det bare brugen
af "reflektion" jeg dybest set ikke kan vaenne mig til.

Tak for bidragene Niels og Filip.

mvh. Morten

Filip Larsen (26-01-2001)
Kommentar
Fra : Filip Larsen


Dato : 26-01-01 09:01

Morten Nedertoft skrev

> Jeg forstaar ikke hvordan ObjectOutputStream baerer sig ad med at kalde
> en privat metode. Er det tilladt via java.lang.reflect.Method?

Ja, hvis koden der kalder har de rette permissions. Jeg vil gætte på, at ObjectOutput klassen, når den skal afgøre om objektet har en writeObject metode, benytter sig af en metode såsom java.lang.Class.getDeclaredMethod der netop smider en SecurityException hvis ikke den kaldende kode har adgang til "declared memebers".

Der er dog også andre sikkerhedsmekanismer i brug for at få serialisering til at virke uden at give generelle sikkerhedshuller. Detaljer til husbehov kan man, som sædvanligt, finde under de relevante "feature guides" i dokumentationen.


> Du skriver, at Serializable goer det til en leg at serialisere. Det
> giver jeg dig ret i, men jeg havde nok alligevel foretrukket en
> mekanisme der var implementeret uden mystik.

Ved at bruge Externalizable styrer du (næsten) det hele selv. Og skulle du have meget specielle behov, så kan du jo bare "opfinde" din egen mekanisme. Mig bekendt, så findes der fx. allerede et par XML-serialiseringsmekanismer fra forskellige producenter.


> Maaske er det bare brugen af "reflektion" jeg dybest set ikke
> kan vaenne mig til.

En yderst nyttig funktionalitet, der gør det størrelsesordener nemmere at lave visse typer af services (som fx. serialisering, persistens, bønne-værktøjer, run-time valg af services, mm.). Prøv at læs lidt i dokumentation og leg med et par eksempler, og du vil finde at det er overraskende nemt at gå til.


Mvh,
---
Filip Larsen <filip.larsen@mail.dk>



Søg
Reklame
Statistik
Spørgsmål : 177560
Tips : 31968
Nyheder : 719565
Indlæg : 6408941
Brugere : 218888

Månedens bedste
Årets bedste
Sidste års bedste