JavaRush /Blog Jawa /Random-JV /Serialization minangka. Bagean 1
articles
tingkat

Serialization minangka. Bagean 1

Diterbitake ing grup
Sepisanan, serialisasi katon kaya proses sing ora pati penting. Pancen, apa sing luwih gampang? Ngumumaké kelas kanggo ngleksanakake antarmuka java.io.Serializable- lan iku. Sampeyan bisa serialize kelas tanpa masalah. Serialization minangka.  Bagean 1 - 1Secara teoritis, iki bener. Ing laku, ana akeh subtleties. Padha gegandhengan karo kinerja, kanggo deserialization, kanggo safety kelas. Lan karo akeh aspek liyane. Subtleties kasebut bakal dibahas. Artikel iki bisa dipérang dadi bagéan ing ngisor iki:
  • Subtleties saka mekanisme
  • Kenapa perlu?Externalizable
  • Kinerja
  • nanging ing sisih liyane
  • Keamanan Data
  • Serialisasi obyekSingleton
Ayo pindhah menyang bagean pisanan -

Subtleties saka mekanisme

Kaping pisanan, pitakonan cepet. Carane akeh cara kanggo nggawe obyek serializable? Praktek nuduhake yen luwih saka 90% pangembang njawab pitakonan iki kanthi cara sing padha (nganti tembung) - mung ana siji cara. Kangge, ana loro. Ora saben wong ngelingi sing nomer loro, apa maneh ngomong apa wae sing bisa dingerteni babagan fitur kasebut. Dadi apa cara iki? Saben uwong ngelingi sing pisanan. Iki minangka implementasine sing wis kasebut java.io.Serializablelan ora mbutuhake gaweyan. Cara liya uga minangka implementasi antarmuka, nanging beda: java.io.Externalizable. Ora kaya java.io.Serializable, ngemot rong cara sing kudu ditindakake - writeExternal(ObjectOutput)lan readExternal(ObjectInput). Cara kasebut ngemot logika serialisasi / deserialisasi. Komentar.SerializableIng ngisor iki , aku kadhangkala bakal ngrujuk menyang serialisasi kanthi implementasine minangka standar, lan implementasine Externalizableminangka lengkap. liyanekomentar. Aku sengaja ora ndemek saiki opsi kontrol serialization standar kaya define readObjectlan writeObject, amarga Aku cara iki rada salah. Cara kasebut ora ditetepake ing antarmuka Serializablelan, nyatane, alat kanggo ngatasi watesan lan nggawe serialisasi standar fleksibel. ExternalizableCara sing nyedhiyakake keluwesan wis dibangun wiwit wiwitan . Ayo takon siji pitakonan liyane. Carane serialization standar bener bisa, nggunakake java.io.Serializable? Lan kerjane liwat API Refleksi. Sing. kelas wis parsed minangka pesawat saka kothak, saben kang ditulis kanggo stream output. Aku iku cetha sing operasi iki ora optimal ing syarat-syarat kinerja. Kita bakal ngerti sepira persise mengko. Ana prabédan utama liyane antarane rong cara serialization kasebut. Yaiku, ing mekanisme deseralisasi. Nalika digunakake, Serializabledeserialization kedadeyan kaya mangkene: memori diparengake kanggo obyek, sawise kolom kasebut diisi nilai saka stream. Konstruktor obyek ora disebut. Ing kene kita kudu nimbang kahanan iki kanthi kapisah. Oke, kelas kita bisa serial. Lan wong tuwane? Rampung opsional! Menapa malih, yen sampeyan oleh warisan kelas saka Object- tiyang sepah mesthi ora serializable. Lan sanajan Objectkita ora ngerti apa-apa babagan lapangan, bisa uga ana ing kelas induk kita dhewe. Apa sing bakal kelakon kanggo wong-wong mau? Dheweke ora bakal mlebu aliran serialisasi. Nilai apa sing bakal ditindakake nalika deserialization? Ayo deleng conto iki:
package ru.skipy.tests.io;

import java.io.*;

/**
 * ParentDeserializationTest
 *
 * @author Eugene Matyushkin aka Skipy
 * @since 05.08.2010
 */
public class ParentDeserializationTest {

    public static void main(String[] args){
        try {
            System.out.println("Creating...");
            Child c = new Child(1);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            c.field = 10;
            System.out.println("Serializing...");
            oos.writeObject(c);
            oos.flush();
            baos.flush();
            oos.close();
            baos.close();
            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);
            System.out.println("Deserializing...");
            Child c1 = (Child)ois.readObject();
            System.out.println("c1.i="+c1.getI());
            System.out.println("c1.field="+c1.getField());
        } catch (IOException ex){
            ex.printStackTrace();
        } catch (ClassNotFoundException ex){
            ex.printStackTrace();
        }
    }

    public static class Parent {
        protected int field;
        protected Parent(){
            field = 5;
            System.out.println("Parent::Constructor");
        }
        public int getField() {
            return field;
        }
    }

    public static class Child extends Parent implements Serializable{
        protected int i;
        public Child(int i){
            this.i = i;
            System.out.println("Child::Constructor");
        }
        public int getI() {
            return i;
        }
    }
}
Iku transparan - kita duwe kelas induk non-serializable lan kelas anak serializable. Lan iki kedadeyan:
Creating...
Parent::Constructor
Child::Constructor
Serializing...
Deserializing...
Parent::Constructor
c1.i=1
c1.field=5
Tegese, sajrone deserialization, konstruktor tanpa paramèter saka kelas NON-serializable wong tuwa diarani . Lan yen ora ana konstruktor kasebut, kesalahan bakal kedadeyan sajrone deserialization. Konstruktor obyek anak, sing kita deserializing, ora disebut, kaya sing kasebut ing ndhuwur. Iki carane mekanisme standar tumindak nalika digunakake Serializable. Nalika nggunakake, Externalizablekahanan beda. Kaping pisanan, konstruktor tanpa paramèter diarani, banjur metode readExternal diarani obyek sing digawe, sing bener maca kabeh data. Mulane, kelas apa wae sing ngetrapake antarmuka Externalizable kudu duwe konstruktor umum tanpa paramèter! Kajaba iku, amarga kabeh turunan saka kelas kasebut uga bakal dianggep ngleksanakake antarmuka Externalizable, dheweke uga kudu duwe konstruktor tanpa parameter! Ayo dadi luwih. Ana modifier lapangan kaya transient. Iku tegese lapangan iki ora kudu serialized. Nanging, sing sampeyan ngerti, instruksi iki mung mengaruhi mekanisme serialization standar. Nalika digunakake, Externalizableora ana sing ngganggu serialisasi lapangan iki, uga nyuda. Yen lapangan diumumake transient, banjur nalika obyek deserialized, iku njupuk ing Nilai standar. Titik liyane sing rada subtle. Kanthi serialisasi standar, kolom sing duwe modifier staticora serialized. Mulane, sawise deserialization lapangan iki ora ngganti regane. Mesthi, sajrone implementasine, Externalizableora ana sing ngganggu serialisasi lan deserialize lapangan iki, nanging aku menehi saran supaya ora nindakake iki, amarga iki bisa mimpin kanggo kasalahan subtle. Bidang sing nganggo modifier finaldiarani serial kaya kolom biasa. Kanthi siji pangecualian - ora bisa deserialized nalika nggunakake Externalizable. Amarga final-поляpadha kudu initialized ing konstruktor, lan sawise iku bakal mokal kanggo ngganti Nilai saka lapangan iki ing readExternal. Patut, yen sampeyan kudu serialize obyek sing wis final-lapangan, sampeyan mung kudu nggunakake serialization standar. Titik liyane sing akeh wong ora ngerti. Serialisasi standar njupuk urutan ing lapangan sing diumumake ing kelas. Ing kasus apa wae, iki kedadeyan ing versi sadurunge; ing JVM versi 1.6 saka implementasine Oracle, urutan kasebut ora penting maneh, jinis lan jeneng lapangan penting. Komposisi metode kasebut bisa uga mengaruhi mekanisme standar, sanajan kasunyatane lapangan bisa tetep padha. Kanggo nyegah iki, ana mekanisme ing ngisor iki. Kanggo saben kelas sing ngetrapake antarmuka Serializable, siji lapangan ditambahake ing tahap kompilasi -private static final long serialVersionUID. Lapangan iki ngemot pengenal versi unik saka kelas serialized. Iki diitung adhedhasar isi kelas - kolom, urutan deklarasi, metode, urutan deklarasi. Mulane, kanthi owah-owahan ing kelas, lapangan iki bakal ngganti nilai. Lapangan iki ditulis kanggo stream nalika kelas serialized. Miturut cara, iki mbok menawa mung cilik dikenal kanggo kula nalika statica -lapangan serialized. Sajrone deserialization, nilai lapangan iki dibandhingake karo kelas ing mesin virtual. Yen nilai ora cocog, pangecualian kaya iki dibuwang:
java.io.InvalidClassException: test.ser2.ChildExt;
    local class incompatible: stream classdesc serialVersionUID = 8218484765288926197,
                                   local class serialVersionUID = 1465687698753363969
Nanging, ana cara kanggo, yen ora ngliwati, banjur ngapusi mriksa iki. Iki bisa migunani yen set lapangan kelas lan urutane wis ditetepake, nanging cara kelas bisa diganti. Ing kasus iki, serialization ora ing resiko, nanging mekanisme standar ora ngidini data kanggo deserialized nggunakake bytecode saka kelas dipunéwahi. Nanging, kaya sing dakkandhakake, dheweke bisa diapusi. Yaiku, nemtokake lapangan ing kelas kanthi manual private static final long serialVersionUID. Ing asas, regane lapangan iki bisa dadi apa wae. Sawetara wong luwih seneng nyetel iku padha karo tanggal kode diowahi. Sawetara malah nggunakake 1L. Kanggo entuk nilai standar (sing diwilang internal), sampeyan bisa nggunakake sarana serialver sing kalebu ing SDK. Sawise ditetepake kanthi cara iki, nilai lapangan bakal tetep, mula deserialization bakal diidini. Menapa malih, ing versi 5.0, kira-kira ing ngisor iki katon ing dokumentasi: dianjurake supaya kabeh kelas serializable nyatakake lapangan iki kanthi tegas, amarga pitungan standar sensitif banget marang rincian struktur kelas, sing bisa beda-beda gumantung saka implementasine compiler. lan kanthi mangkono nyebabake InvalidClassExceptionakibat sing ora dikarepke. Iku luwih apik kanggo ngumumake lapangan iki minangka private, amarga iku mung nuduhake kelas sing diumumake. Senajan modifier ora kasebut ing specification. Ayo saiki nimbang aspek iki. Ayo kita duwe struktur kelas iki:
public class A{
    public int iPublic;
    protected int iProtected;
    int iPackage;
    private int iPrivate;
}

public class B extends A implements Serializable{}
Ing tembung liyane, kita duwe kelas sing diwarisake saka wong tuwa sing ora bisa serial. Iku bisa kanggo serialize kelas iki, lan apa sing perlu kanggo iki? Apa sing bakal kedadeyan karo variabel kelas induk? Jawaban iki. Ya, Bsampeyan bisa nggawe seri saka kelas. Apa sing dibutuhake kanggo iki? Nanging kelas kudu Aduwe konstruktor tanpa paramèter, publicutawa protected. Banjur, sajrone deserialization, kabeh variabel kelas Abakal diinisialisasi nggunakake konstruktor iki. Variabel kelas Bbakal diwiwiti kanthi nilai saka aliran data serial. Secara teoritis, bisa ditetepake ing kelas Bmetode sing dakkandhakake ing wiwitan - readObjectlan writeObject, - ing wiwitan kanggo nindakake (de-)serialization saka variabel kelas Bliwat in.defaultReadObject/out.defaultWriteObject, banjur (de-)serialization saka variabel sing kasedhiya. saka kelas A(ing kasus kita iki iPublic, iProtectedlan iPackage, yen Bing paket padha A). Nanging, miturut pendapatku, luwih becik nggunakake serialisasi lengkap kanggo iki. Titik sabanjure sing dakkarepake yaiku serialisasi pirang-pirang obyek. Ayo kita duwe struktur kelas ing ngisor iki:
public class A implements Serializable{
    private C c;
    private B b;
    public void setC(C c) {this.c = c;}
    public void setB(B b) {this.b = b;}
    public C getC() {return c;}
    public B getB() {return b;}
}
public class B implements Serializable{
    private C c;
    public void setC(C c) {this.c = c;}
    public C getC() {return c;}
}
public class C implements Serializable{
    private A a;
    private B b;
    public void setA(A a) {this.a = a;}
    public void setB(B b) {this.b = b;}
    public B getB() {return b;}
    public A getA() {return a;}
}
Serialization minangka.  Bagean 1 - 2Apa sing kedadeyan yen sampeyan nggawe seri saka kelas A? Iku bakal nyeret bebarengan Kayata saka kelas B, kang, siji, bakal nyeret bebarengan Kayata Csing wis referensi kanggo Kayata A, siji sing padha karo kang kabeh diwiwiti. Lingkaran setan lan rekursi tanpa wates? Begjanipun, ora. Ayo ndeleng kode test ing ngisor iki:
// initiaizing
A a = new A();
B b = new B();
C c = new C();
// setting references
a.setB(b);
a.setC(c);
b.setC(c);
c.setA(a);
c.setB(b);
// serializing
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(a);
oos.writeObject(b);
oos.writeObject(c);
oos.flush();
oos.close();
// deserializing
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));
A a1 = (A)ois.readObject();
B b1 = (B)ois.readObject();
C c1 = (C)ois.readObject();
// testing
System.out.println("a==a1: "+(a==a1));
System.out.println("b==b1: "+(b==b1));
System.out.println("c==c1: "+(c==c1));
System.out.println("a1.getB()==b1: "+(a1.getB()==b1));
System.out.println("a1.getC()==c1: "+(a1.getC()==c1));
System.out.println("b1.getC()==c1: "+(b1.getC()==c1));
System.out.println("c1.getA()==a1: "+(c1.getA()==a1));
System.out.println("c1.getB()==b1: "+(c1.getB()==b1));
Apa sing kita lakoni? We nggawe Kayata saka kelas A, Blan C, menehi pranala menyang saben liyane, lan banjur serialize saben wong. Banjur kita deserialize maneh lan mbukak seri mriksa. Apa sing bakal kelakon minangka asil:
a==a1: false
b==b1: false
c==c1: false
a1.getB()==b1: true
a1.getC()==c1: true
b1.getC()==c1: true
c1.getA()==a1: true
c1.getB()==b1: true
Dadi, apa sing bisa sampeyan sinau saka tes iki? Pisanan. Referensi obyek sawise deseralisasi beda karo referensi sadurunge. Ing tembung liyane, sajrone serialisasi / deserialisasi obyek kasebut disalin. Cara iki kadhangkala digunakake kanggo kloning obyek. Kesimpulan kapindho luwih penting. Nalika serializing / deserializing macem-macem obyek sing duwe referensi salib, referensi kasebut tetep bener sawise deserialization. Ing tembung liyane, yen sadurunge serialization padha nuding siji obyek, banjur sawise deserialization uga bakal nuduhake siji obyek. Tes cilik liyane kanggo konfirmasi iki:
B b = new B();
C c = new C();
b.setC(c);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(b);
oos.writeObject(c);
oos.writeObject(c);
oos.writeObject(c);
oos.flush();
oos.close();
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));
B b1 = (B)ois.readObject();
C c1 = (C)ois.readObject();
C c2 = (C)ois.readObject();
C c3 = (C)ois.readObject();
System.out.println("b1.getC()==c1: "+(b1.getC()==c1));
System.out.println("c1==c2: "+(c1==c2));
System.out.println("c1==c3: "+(c1==c3));
Objek kelas Bnduweni referensi kanggo obyek kelas C. Nalika serialized, biku serialized bebarengan karo Kayata saka kelas С, sawise kang padha Kayata c serialized kaping telu. Apa sing kedadeyan sawise deserialization?
b1.getC()==c1: true
c1==c2: true
c1==c3: true
Nalika sampeyan bisa ndeleng, kabeh papat obyek deserialized bener makili siji obyek - referensi kanggo iku padha. Persis kaya sadurunge serialisasi. Titik menarik liyane - apa bakal kelakon yen kita bebarengan ngleksanakake Externalizablelan Serializable? Kaya ing pitakonan kasebut - gajah lawan paus - sapa sing bakal ngalahake sapa? Bakal ngatasi Externalizable. Mekanisme serialisasi pisanan mriksa anane, lan mung banjur anane. SerializableDadi yen kelas B, sing ngleksanakake Serializable, warisan saka kelas A, sing ngleksanakake Externalizable, lapangan kelas B ora bakal serial. Titik pungkasan yaiku warisan. Nalika marisi saka kelas sing ngetrapake Serializable, ora ana tindakan tambahan sing kudu ditindakake. Serialisasi bakal ngluwihi kelas anak uga. Nalika marisi saka kelas sing ngleksanakake Externalizable, sampeyan kudu ngilangi metode readExternal lan writeExternal kelas induk. Yen ora, lapangan kelas anak ora bakal serialized. Ing kasus iki, sampeyan kudu ngelingi nelpon cara induk, yen ora, kolom induk ora bakal serial. * * * Kita mbokmenawa wis rampung karo rincian. Nanging, ana siji masalah sing durung kita deleng, sing sifate global. Yaiku-

Napa sampeyan kudu Externalizable?

Apa kita kudu serialization majeng ing kabeh? Jawaban iki prasaja. Kaping pisanan, menehi keluwesan luwih akeh. Kapindho, asring bisa menehi hasil sing signifikan babagan volume data serial. Katelu, ana aspek kayata kinerja, sing bakal kita pirembagan ing ngisor iki . Kabeh katon jelas kanthi keluwesan. Pancen, kita bisa ngontrol serialization lan deserialization pangolahan kita pengin, kang ndadekake kita bebas saka owah-owahan ing kelas (kaya aku ngandika ing ndhuwur, owah-owahan ing kelas bisa nemen mengaruhi deserialization). Mulane, aku arep ngomong sawetara tembung babagan gain ing volume. Ayo kita duwe kelas ing ngisor iki:
public class DateAndTime{

  private short year;
  private byte month;
  private byte day;
  private byte hours;
  private byte minutes;
  private byte seconds;

}
Liyane ora penting. Kothak bisa digawe saka jinis int, nanging iki mung bakal nambah efek saka conto. Senajan ing kasunyatan lapangan bisa diketik intkanggo alasan kinerja. Ing kasus apa wae, titik kasebut jelas. Kelas kasebut nuduhake tanggal lan wektu. Iku menarik kanggo kita utamané saka sudut pandang serialization. Mbok menawa sing paling gampang ditindakake yaiku nyimpen stempel wektu sing prasaja. Iku saka jinis dawa, i.e. nalika serialized bakal njupuk 8 bita. Kajaba iku, pendekatan iki mbutuhake cara kanggo ngowahi komponen dadi siji nilai lan bali, i.e. - mundhut ing produktivitas. Kauntungan saka pendekatan iki yaiku tanggal edan sing bisa pas karo 64 bit. Iki minangka wates safety sing ageng, paling asring ora dibutuhake ing kasunyatan. Kelas sing diwenehi ing ndhuwur bakal njupuk 2 + 5 * 1 = 7 bita. Plus overhead kanggo kelas lan 6 lapangan. Apa ana cara kanggo ngompres data iki? Temenan. Detik lan menit ana ing kisaran 0-59, yaiku. kanggo makili wong, 6 bit cukup tinimbang 8. Jam - 0-23 (5 bit), dina - 0-30 (5 bit), sasi - 0-11 (4 bit). Total, kabeh tanpa njupuk menyang akun taun - 26 bit. Isih ana 6 bit kanggo ukuran int. Secara teoritis, ing sawetara kasus iki bisa cukup kanggo setahun. Yen ora, nambah bait liyane nambah ukuran lapangan data kanggo 14 bit, kang menehi sawetara 0-16383. Iki luwih saka cukup ing aplikasi nyata. Secara total, kita wis nyuda ukuran data sing dibutuhake kanggo nyimpen informasi sing dibutuhake dadi 5 bita. Yen ora nganti 4. Kerugian kasebut padha karo kasus sadurunge - yen sampeyan nyimpen tanggal sing dikemas, banjur metode konversi dibutuhake. Nanging aku pengin nindakake kanthi cara iki: simpen ing kolom sing kapisah lan serialize ing wangun rangkep. Iki tegese nggunakake Externalizable:
// data is packed into 5 bytes:
//  3         2         1
// 10987654321098765432109876543210
// hhhhhmmmmmmssssssdddddMMMMyyyyyy yyyyyyyy
public void writeExternal(ObjectOutput out){
    int packed = 0;
    packed += ((int)hours) << 27;
    packed += ((int)minutes) << 21;
    packed += ((int)seconds) << 15;
    packed += ((int)day) << 10;
    packed += ((int)month) << 6;
    packed += (((int)year) >> 8) & 0x3F;
    out.writeInt(packed);
    out.writeByte((byte)year);
}

public void readExternal(ObjectInput in){
    int packed = in.readInt();
    year = in.readByte() & 0xFF;
    year += (packed & 0x3F) << 8;
    month = (packed >> 6) & 0x0F;
    day = (packed >> 10) & 0x1F;
    seconds = (packed >> 15) & 0x3F;
    minutes = (packed >> 21) & 0x3F;
    hours = (packed >> 27);
}
Bener, iku kabeh. Sawise serialisasi, kita entuk overhead saben kelas, rong lapangan (tinimbang 6) lan 5 bita data. Kang wis Ngartekno luwih. Kemasan liyane bisa ditinggalake menyang perpustakaan khusus. Conto sing diwenehake prasaja banget. Tujuan utamane yaiku kanggo nuduhake carane serialisasi majeng bisa digunakake. Senajan gain bisa ing volume data serialized adoh saka kauntungan utama, ing mratelakake panemume. Kauntungan utama, saliyane kanggo keluwesan ... (alus pindhah menyang bagean sabanjure ...) Link menyang sumber: Serialisasi minangka iku
Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION