Nesnelerin Seri Hale Getirilmesi (Serialization)

Serilization bir nesnenin nesne olarak bütünün diske yada başka bir Stream'e yazılmasına Serilization, diskten yada başka bir Stream'den okunmasına ise deSerilization denilmektedir. Java'da bir sınıfın seri hale getirilebilmesi için, Serilaziable arayüzünü implemente etmesi gerekmektedir. Serilazible arayüzü işaretleme(marker) arayüzdür.

Anahtar Not: Benzer olan .Net platformunda, işaretleme arayüzleri kullanılmamaktadır, bunun yerine bu işlem attribute sınıfları ile gerçekleşmektedir. Attribute sınıflar Java'da ki annotationlara benzetilebilir. Annotationlar Java'ya daha sonra eklendiği için Serilaziable olması korunmuştur. Seri etme işlemi ObjectOutputStream ve ObjectInputStream sınıfları ile yapılmaktadır.

Serilizable arayüzümü implemente eden sınıflar nesnenin versiyon numarasına göre de çalışabildiğinden, SerialVersionUID isimli bir elemanı olması iyi bir tekniktir. Bu konu ileride ele alınacaktır.

Basit bir nesneyi yazan uygulama şu şekilde tasarlanabilir:

/*----------------------------------------------------------------------------------------------------------------------
    Bir nesnesin seri edilmesi
----------------------------------------------------------------------------------------------------------------------*/
package org.csystem;

import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class App {
    public static void main(String[] args) 
    {    
        try (FileOutputStream fos = new FileOutputStream("people.dat", true)) {
            ObjectOutputStream  oos = new ObjectOutputStream(fos);

            oos.writeObject(new Person("Batu", 123, false));
        }
        catch (Exception ex) {
            System.out.println(ex.getMessage());
        }
    }
}

class Person implements Serializable {
    private static final long serialVersionUID = 1L; //Versiyon numarası
    private String m_name;
    private int m_number;
    private boolean m_married;
    public Person(String name, int number, boolean married) {
        super();
        m_name = name;
        m_number = number;
        m_married = married;
    }
    public String getName()
    {
        return m_name;
    }
    public void setName(String name)
    {
        m_name = name;
    }
    public int getNumber()
    {
        return m_number;
    }
    public void setNumber(int number)
    {
        m_number = number;
    }
    public boolean isMarried()
    {
        return m_married;
    }
    public void setMarried(boolean married)
    {
        m_married = married;
    }
    @Override
    public String toString()
    {
        return String.format("[%d]-%s-%s", m_number, m_name, m_married ? "Evli" : "Bekar");
    }

}

Seri edilmiş bir nesnenin alınması(deserilization) işlemi aşağıdaki gibi yapılabilir.

/*----------------------------------------------------------------------------------------------------------------------
    Ser edilmiş bir nesnenin geri alınması (deserialization)
----------------------------------------------------------------------------------------------------------------------*/
package org.csystem;

import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.io.Serializable;

public class App {
    public static void main(String[] args) 
    {    
        try (FileInputStream fos = new FileInputStream("people.dat")) {
            ObjectInputStream  ois = new ObjectInputStream(fos);

            Person p = (Person)ois.readObject();

            System.out.println(p);
        }
        catch (Exception ex) {
            System.out.println(ex.getMessage());
        }
    }
}

class Person implements Serializable {
    private static final long serialVersionUID = 1L; //Versiyon numarası
    private String m_name;
    private int m_number;
    private boolean m_married;
    public Person(String name, int number, boolean married) {
        super();
        m_name = name;
        m_number = number;
        m_married = married;
    }
    public String getName()
    {
        return m_name;
    }
    public void setName(String name)
    {
        m_name = name;
    }
    public int getNumber()
    {
        return m_number;
    }
    public void setNumber(int number)
    {
        m_number = number;
    }
    public boolean isMarried()
    {
        return m_married;
    }
    public void setMarried(boolean married)
    {
        m_married = married;
    }
    @Override
    public String toString()
    {
        return String.format("[%d]-%s-%s", m_number, m_name, m_married ? "Evli" : "Bekar");
    }

}

Seri edilecek ya da alınacak nesnelerin Serilazible arayüzünü implemente etmemiş olmaları durumunda Exception oluşur.

Sınıf Çalışması: Bir client ve client server bir program yazınınız client program server programa bir person nesnesini serilization ile göndersin, server program ise deserilization ile bir veritabınına eklesin. Gene client tan gelen başka bir komut yardımıyla tüm eklenmiş kişiler nesne seri etme yöntemiyle aktarılsın.

Çözümü: 006-NetworkObjectSerialization

ObjectOutputStream ve ObjectInputStream sınıfları ile temel türlere ilişkin yazma veya okuma yapan metotlarda bulunmaktadır.

Sınıf Çalışması: Bir önceki sınıf çalışmasında client'ın gönderdiği dataları bir Collection sınıfa kaydediniz, client'ın gönderdiği kod numarasına göre, tüm eklenmiş olan Person nesnelerini client'a geri yollayınız.

Çözümü: 007-NetworkObjectSerializationDetail

Sınıf Çalışması: Yukarıda kullanılan Person sınıfının elemanlarını alabilecek HTML form oluşturunuz, ve servlet server programı üzerinde seri etme mekanizması ile formdan gelen kişi bilgilerini bir dosyaya kaydediniz.

001-SamplePeopleInfoSerialization

Bir Sınıf İçerisinde Seri Edilmeyen Elemanlar

Programcı isterse sınıfın veri elemanlarının bazılarını seri etmiycek şekilde bildirebilir. Bunun için seri edilecek elemanlara ilişkin custom serilization yapabilir yada seri etmek istemediği elemanın başına transient anahtar sözcğünü yazar. transient anahtar sözcüğü Java'ya daha sonra eklenmiştir. Dolayısıyla daha eski kodlarda custom serilization kullanılmaktadır.

package org.csystem;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class App {
    public static void main(String[] args) 
    {
        Sample s = new Sample(10, "ankara", 3.4F);

        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("samples.dat"))) {
            oos.writeObject(s);
        }
        catch (Exception ex) {
            System.err.println(ex.getMessage());
        }

        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("samples.dat"))) {
            Sample s1 = (Sample)ois.readObject();

            System.out.printf("m_a=%d%n", s1.getA());
            System.out.printf("m_s=%s%n", s1.getS());
            System.out.printf("m_b=%f%n", s1.getB());
        }
        catch (Exception ex) {
            System.err.println(ex.getMessage());
        }



    }
}


class Sample implements Serializable {
    private static final long serialVersionUID = 1L;
    private int m_a;
    transient private String m_s;
    transient private float m_b;
    public Sample(int a, String s, float b)
    {
        super();
        m_a = a;
        m_s = s;
        m_b = b;
    }
    public int getA()
    {
        return m_a;
    }
    public void setA(int a)
    {
        m_a = a;
    }
    public String getS()
    {
        return m_s;
    }
    public void setS(String s)
    {
        m_s = s;
    }
    public float getB()
    {
        return m_b;
    }
    public void setB(float b)
    {
        m_b = b;
    }


}

Sınıf Türünden Veri Elemanı Olan Bir Sınıf Nesnenin Seri Hale Getirilmesi

Bir sınıfın başka bir sınıf türünden veri elemanı olması durumunda doğrudan seri etme işlemi yapabilmek için veri elemanı olan sınıfında serilaziable olması gerekmektedir. Veri elemanı olan sınıf serializable arayüzünü implemente etmemişse, bu durumda bunu kullanan programcı custom serialization yaparak seri hale getirilmesini istediği elemanları belirleyebilir. Ayrıca transient anahtar sözcüğünü sınıfın veri elemanı olan referansta bildirirse bu durumda nesne seri hale getirilemez. Seri edilememe durumunda java.io.NotSerializableException türünden nesne fırlatılır.

package org.csystem;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class App {
    public static void main(String[] args) 
    {
        Sample s = new Sample();

        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("samples.dat"))) {
            oos.writeObject(s);
        }
        catch (Exception ex) {
            System.err.println(ex.getMessage());
        }

        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("samples.dat"))) {
            Sample s1 = (Sample)ois.readObject();


        }
        catch (Exception ex) {
            System.err.println(ex.getMessage());
        }    

    }
}

class Sample implements Serializable {
    private Mample m_mample;

    public Sample()
    {
        m_mample = new Mample();
    }

}

class Mample {

}

Serialization İşleminde Taban Sınıf\/Türemiş Sınıf İlişkisi

Seri hale getirmede taban sınıf türemiş sınıf arasında seri etme durumunda aşağıdaki şekilden anlaşılabilir.

Aşağıdaki gibi bir türetme şeması için

A sınıfı = Father B sınıfı = Son

1.A sınıfı serializable arayüzünü destekliyor ise B sınıfı desteklemese zaten destekliyor kabul edilebilir. Bu durumda bir sorun oluşmaz.

/*----------------------------------------------------------------------------------------------------------------------
    Taban sınıfın Serializable olması durumu
----------------------------------------------------------------------------------------------------------------------*/
package org.csystem;

import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class App {
    public static void main(String[] args) 
    {
        B b = new B();

        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("samples.dat"))) {
            oos.writeObject(b);
        }
        catch (Exception ex) {
            System.err.println(ex.getMessage());
        }    

    }
}

class A implements Serializable {

}

class B extends A {

}

2.A sınıfı serializable arayüzünü implemente etmemiş, B sınıfı implemente etmiş olma durumu. Bu durumda taban sınıfın default constructor bulunması gerekmektedir, eğer default constructor olmazsa bu durumda taban sınf kısmı seri hale getirilebilir fakat deserialization işlemi yapılamaz. Bu durumda programcı yalnız custom serialization işlemini yaparsa deseri yapabilir.

*----------------------------------------------------------------------------------------------------------------------
    Taban sınıf türemiş sınıf serialization işlemleri
----------------------------------------------------------------------------------------------------------------------*/
package org.csystem;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class App {
    public static void main(String[] args) 
    {
        B b = new B(10);

        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("samples.dat"))) {
            oos.writeObject(b);
        }
        catch (Exception ex) {
            System.err.println(ex.getMessage());
        }    

        try (ObjectInputStream oos = new ObjectInputStream(new FileInputStream("samples.dat"))) {
            oos.readObject();
        }
        catch (Exception ex) {
            System.err.println(ex.getMessage());
        }

    }
}

class A {
    public A(int val)
    {}

}

class B extends A implements Serializable {
    public B(int val)
    {
        super(val);
    }

}

Taban sınıf serializable arayüzünü implemente etmişse, fakat türemiş sınıf türünden bir nesneyi gösteriyorsa ve seri etme işlemi taban sınıf türünden referansla yapılmışsa ne olacak ?

Bu durumda nesnenin heap'teki tamamı seri edilecektir.

package org.csystem;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class App {
    public static void main(String[] args) 
    {
        B b = new B();

        b.X = 10;
        b.Y = 20;
        A a = b;

        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("samples.dat"))) {
            oos.writeObject(a);
        }
        catch (Exception ex) {
            System.err.println(ex.getMessage());
        }    

        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("samples.dat"))) {
            B b1 = (B)ois.readObject();
            System.out.println(b1.X);
            System.out.println(b1.Y);
        }
        catch (Exception ex) {
            System.err.println(ex.getMessage());
        }        
    }
}

class A implements Serializable {
    public int X;    
}

class B extends A {
    public int Y;
}

Sınıf Çalışması: Shape sınıfından türetilmiş Rectangle, Line ve Circle isimli sınıflar tasarlayınız. Rasgele nesneler üreterek seri ediniz. Seri edilmiş nesneleri uygun türlerine deserialize ediniz

import java.io.EOFException;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Random;

public class App {
    public static void main(String[] args) 
    {
        Random r = new Random();

        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("shapes.dat"))) {
            for (int i = 0; i < 5; ++i) {
                Shape s = getRandomShape(r);

                oos.writeObject(s);
                oos.flush();
            }
        }         
        catch (Exception ex) {
            System.err.println(ex.getMessage());
        }

        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("shapes.dat"))) {            
            try {
                while (true) {
                    Shape curShape = (Shape)ois.readObject();

                    if (curShape instanceof Rectangle) {
                        Rectangle rec = (Rectangle)curShape;

                        rec.message();
                    }
                    else if (curShape instanceof Circle) {
                        Circle c = (Circle)curShape;

                        c.message();
                    }
                    else if (curShape instanceof Line) {
                        Line line = (Line)curShape;

                        line.message();
                    }                
                }
            }
            catch (EOFException ex) {

            }

        }
        catch (Exception ex) {
            ex.printStackTrace();
        }

    }

    public static Shape getRandomShape(Random r)
    {
        int id = r.nextInt(3) + 1;

        return  Factory.getShape(id);        
    }
}

class Factory {
    public static Shape getShape(int id)
    {
        switch (id) {
        case 1:
            return new Rectangle();
        case 2:
            return new Circle();
        case 3:
            return new Line();
        }

        return null;        
    }
}

class Shape implements Serializable {
    private static final long serialVersionUID = 1L;
    //...

}

class Rectangle extends Shape {
    public void message()
    {
        System.out.println("Rectangle");        
    }
} 

class Circle extends Shape {
    public void message()
    {
        System.out.println("Circle");        
    }
}

class Line extends Shape {
    public void message()
    {
        System.out.println("Line");        
    }
}

Anahtar Not: Farklı ObjectOutputStream ile yazılan nesneler aynı ObjectInputStream ile okunamazlar. Bu duruma programcının dikkat etmesi gerekir.

Custom Serialization

Bilindiği gibi Serializable arayüzünü implemente etmiş bir sınıf için seri edilmesi istenmeyen bir veri elemanların bildiriminde transient anahtar sözcüğü yazılabilmektedir. Peki programcı transient anahtar sözcüğünü kullanmadan bu işlemi nasıl yapacaktır? Bunun için readObject ve writeObject metotları yazılmalıdır. Bu metotların bildirimleri şu şekildedir.

```java private void writeObject(ObjectOutputStream out) throws IOException;

private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException;```

Örnek:

```java \/---------------------------------------------------------------------------------------------------------------------- transient anahtar sözcüğü kullanmadan custom serialization ----------------------------------------------------------------------------------------------------------------------\/ package org.csystem;

import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable;

public class App { public static void main(String[] args) {
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("samples.dat"))) {
Sample s = new Sample(10, 3.4, 5.6F);

        oos.writeObject(s);
    }
    catch (Exception ex) {
        System.err.println(ex.getMessage());
        ex.printStackTrace();
    }

    try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("samples.dat"))) {
        Sample s = (Sample)ois.readObject();

        System.out.println(s);
    }
    catch (Exception ex) {
        System.err.println(ex.getMessage());
        ex.printStackTrace();
    }        
}

}

class Sample implements Serializable { private int m_x; private double m_y; private float m_z;

private void writeObject(ObjectOutputStream out) throws IOException
{
    out.writeInt(m_x);
    out.writeDouble(m_y);
}

private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException
{
    m_x = in.readInt();
    m_y = in.readDouble();
}

public Sample(int x, double y, float z)
{
    super();
    m_x = x;
    m_y = y;
    m_z = z;
}

public int getX()
{
    return m_x;
}

public void setX(int x)
{
    m_x = x;
}

public double getY()
{
    return m_y;
}

public void setY(double y)
{
    m_y = y;
}



public float getZ()
{
    return m_z;
}



public void setZ(float z)
{
    m_z = z;
}

@Override
public String toString()
{
    return String.format("x:%d\ty=%f\tz=%f", m_x, m_y, m_z);
}

}```

```java \/---------------------------------------------------------------------------------------------------------------------- Custom serialization ----------------------------------------------------------------------------------------------------------------------\/ package org.csystem;

import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable;

public class App { public static void main(String[] args) {
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("samples.dat"))) {
Sample s = new Sample(10, 3.4, 5.6F);

        oos.writeObject(s);
    }
    catch (Exception ex) {
        System.err.println(ex.getMessage());
        ex.printStackTrace();
    }

    try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("samples.dat"))) {
        Sample s = (Sample)ois.readObject();

        System.out.println(s);
    }
    catch (Exception ex) {
        System.err.println(ex.getMessage());
        ex.printStackTrace();
    }        
}

}

class Mample { private double m_y; private float m_z; public Mample(double y, float z) {
m_y = y; m_z = z; } public double getY() { return m_y; } public void setY(double y) { m_y = y; } public float getZ() { return m_z; } public void setZ(float z) { m_z = z; }

} class Sample implements Serializable { private int m_x; private Mample m_mample;

private void writeObject(ObjectOutputStream out) throws IOException
{
    out.writeInt(m_x);
    out.writeDouble(m_mample.getY());
}

private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException
{
    m_x = in.readInt();
    m_mample = new Mample(in.readDouble(), 0);    
}

public Sample(int x, double y, float z)
{        
    m_x = x;
    m_mample = new Mample(y, z);                
}

public int getX()
{
    return m_x;
}

public void setX(int x)
{
    m_x = x;
}

public double getY()
{
    return m_mample.getY();
}

public void setY(double y)
{
    m_mample.setY(y);
}

public float getZ()
{
    return m_mample.getZ();
}



public void setZ(float z)
{
    m_mample.setZ(z);
}


@Override
public String toString()
{
    return String.format("x:%d\ty=%f\tz=%f", m_x, m_mample.getY(), m_mample.getZ());
}

}```

Burada dikkat edilirse Mample sınıfı serializable arayüzünü implemente etmemiştir ancak readObject ve writeObject metotlarının yazılmasıyla doğrudan Mample değil bizim istediğimiz elemanlar seri edilmiştir. Custom serialization her zaman önceliklidir. Elemanlar ister transient olsun ister olmasın readObject ve writeObject metotları yazılarak seri hale getirme işlemleri yapılacaktır. Serialization mekanizması genel olarak yavaş bir mekanizmadır. Programcı açısından yavaşlık çok önemli değilse kolay kaydetme ve geri alma gibi işlemlerde kullanılabilir.

results matching ""

    No results matching ""