Serializable的序列化与反序列化

来源:互联网 发布:windows版本号查询 编辑:程序博客网 时间:2024/05/16 19:43

使用Serializable序列化,只要实现Serializable接口即可。一般情况下都会显示设置静态成员变量serialVersionUID为固定值。序列化时使用ObjectOutputStream写入,反序列化时使用ObjectInputStream读出。

如此简单,谁都会。但这是我碰到复杂点的情况,特作以下总结:

1、Serializable可继承:父类实现了序列化,子类也会自动实现序列化

PersonBean.java:

public class PersonBean implements Serializable{    private static final long serialVersionUID = 1L;    private String name;    private int age;    public PersonBean(String name, int age) {        this.name = name;        this.age = age;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public int getAge() {        return age;    }    public void setAge(int age) {        this.age = age;    }    @Override    public String toString() {        return name + "$$$" + age;    }}

ProgrammerBean.java:

public class ProgrammerBean extends PersonBean {    private String language;    public ProgrammerBean(String name, int age, String language) {        super(name, age);        this.language = language;    }    public String getLanguage() {        return language;    }    public void setLanguage(String language) {        this.language = language;    }    @Override    public String toString() {        return getName() + "@@@" + getAge() + "@@@" + language;    }}

SerialTest.java:

package com.example;import com.example.serialBean.PersonBean;import com.example.serialBean.ProgrammerBean;import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;/** * Created by Ralap on 2016/7/11. */public class SerialTest {    private static final String filePath = System.getProperty("user.dir");    private static final String fileName = "serialTest.st";    public static void test() {        // 1、Serializable可继承        testExtends();        // 2、transient和static修饰变量,使其不参加序列化        // 3、含有不可序列化的对象,自定义writeObject()和readObject()        // 4、集合序列化    }    private static void testExtends() {        PersonBean personBean = new PersonBean("Boss", 38);        ProgrammerBean programmerBean = new ProgrammerBean("hacker", 29, "Java");        printfn("==========Before Serialize :==========");        printfn(personBean.toString());        printfn(programmerBean.toString());        File file = createFile();        if (file == null) {            printfn("create file fail");            return;        }        // 序列化        serialize(file, personBean, programmerBean);        // 反序列化        Object[] objs = new Object[2];        unserialize(file, objs);        PersonBean perBean = (PersonBean) objs[0];        ProgrammerBean proBean = (ProgrammerBean) objs[1];        // 打印        printfn("==========After Unserialize:==========");        printfn(perBean.toString());        printfn(proBean.toString());    }    /**     * 序列化     *     * @param file 序列化目标文件     * @param objs 序列化对象     */    private static void serialize(File file, Object... objs) {        ObjectOutputStream objectOS = null;        try {            FileOutputStream fos = new FileOutputStream(file);            objectOS = new ObjectOutputStream(fos);            for (Object obj : objs) {                objectOS.writeObject(obj);            }            objectOS.flush();        } catch (FileNotFoundException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        } finally {            if (objectOS != null) {                try {                    objectOS.close();                } catch (IOException e) {                    e.printStackTrace();                }            }        }    }    /**     * 反序列化     * @param file 反序列化文件     * @return 反序列化出来的对象数组     */    private static void unserialize(File file, Object[] objs) {        ObjectInputStream objectIS = null;        try {            FileInputStream fis = new FileInputStream(file);            objectIS = new ObjectInputStream(fis);            Object obj;            int offset = 0;            while ((obj = objectIS.readObject()) != null) {                objs[offset++] = obj;                if (offset >= objs.length) {                    return;                }            }        } catch (FileNotFoundException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        } catch (ClassNotFoundException e) {            e.printStackTrace();        } finally {            if (objectIS != null) {                try {                    objectIS.close();                } catch (IOException e) {                    e.printStackTrace();                }            }        }    }    private static void printf(String s) {        System.out.printf("%s", s);    }    private static void printfn(String s) {        System.out.printf("%s\n", s);    }    /**     * 创建文件     * 存在:删除后新建。不存在:新建     */    private static File createFile() {        File file = new File(filePath, fileName);        try {            if (file.exists()) {                if (!file.delete()) {                    return null;                }            }            if (!file.createNewFile()) {                return null;            }        } catch (IOException e) {            e.printStackTrace();        }        return file;    }}

结果:
这里写图片描述

2、static和transient修饰的变量,将不参加序列化

序列化只对类中的filed字段属性进行序列化,且序列化的只是类的实例对象的属性类型及值,所以method方法和static修饰的属性将不被序列化。如果要让非static属性也不序列化,使用transient。

扩展:另一个修饰变量符volatile,易变的意思。一般用于多线程中,因为每个线程都会有自己独立的内存空间,共享变量会从主内存拷贝一份到自己的内存中,操作的是自己内存中的数值,在进入线程或退出同步代码块时,才与共享中的成员变量进行比对、同步,这样可能导致其他线程获取到的不是最新值。所以,用volatile来修饰共享成员变量,在每次使用变量时都会强迫从共享内存中重新读取共享变量的值。

static:静态的,被所有对象共享,非某个对象私有,不会被持久化
transient:代表瞬间的意思,表示不会被持久化

把上面ProgrammerBean和SerialTest进行修改

ProgrammerBean.java

public class ProgrammerBean extends PersonBean {    private String language;    private static String belongIndustry = "Linux"; // static修饰    transient private boolean hasGF; // transient修饰。boolean默认值是false,注意比较    public ProgrammerBean(String name, int age, String language) {        super(name, age);        this.language = language;    }    public String getLanguage() {        return language;    }    public void setLanguage(String language) {        this.language = language;    }    public static String getBelongIndustry() {        return belongIndustry;    }    public static void setBelongIndustry(String belongIndustry) {        ProgrammerBean.belongIndustry = belongIndustry;    }    public boolean isHasGF() {        return hasGF;    }    public void setHasGF(boolean hasGF) {        this.hasGF = hasGF;    }    @Override    public String toString() {        return getName() + "^^^" + getAge() + "^^^" + language + "^^^" + belongIndustry + "^^^" + hasGF;    }}

SerialTest.java修改部分

    public static void test() {        // 1、Serializable可继承//        testExtends();        // 2、transient和static修饰变量,使其不参加序列化        testNotSerial();        // 3、含有不可序列化的对象,自定义writeObject()和readObject()        // 4、集合序列化    }    private static void testNotSerial() {        PersonBean personBean = new PersonBean("Boss", 38);        ProgrammerBean programmerBean = new ProgrammerBean("hacker", 29, "Java");        programmerBean.setHasGF(true);        printfn("==========Before Serialize :==========");        printfn(personBean.toString());        printfn(programmerBean.toString());        File file = createFile();        if (file == null) {            printfn("create file fail");            return;        }        // 序列化        serialize(file, personBean, programmerBean);        // 序列化后修改static属性值        ProgrammerBean.setBelongIndustry("Android");        // 反序列化        Object[] objs = new Object[2];        unserialize(file, objs);        PersonBean perBean = (PersonBean) objs[0];        ProgrammerBean proBean = (ProgrammerBean) objs[1];        // 打印        printfn("==========After Unserialize:==========");        printfn(perBean.toString());        printfn(proBean.toString());    }

结果:
这里写图片描述

3、使用自定义序列化使不可序列化的类序列化

一些类没有实现Serializable,就不能序列化。如ProgrammerBean中包含了一个不可序列化的类InterestBean。

ProgrammerBean.java

public class ProgrammerBean extends PersonBean {    private String languages;    InterestBean interest;    public ProgrammerBean(String name, int age, String languages) {        super(name, age);        this.languages = languages;    }    public String getLanguages() {        return languages;    }    public void setLanguages(String languages) {        this.languages = languages;    }    public InterestBean getInterest() {        return interest;    }    public void setInterest(InterestBean interest) {        this.interest = interest;    }    @Override    public String toString() {        return getName() + "^^^" + getAge() + "^^^" + languages + ":::" + interest.getType() + "^^^" + interest.getNames();    }}

InterestBean.java

public class InterestBean{    private String type;    private String names;    public InterestBean(String type, String names) {        this.type = type;        this.names = names;    }    public String getType() {        return type;    }    public void setType(String type) {        this.type = type;    }    public String getNames() {        return names;    }    public void setNames(String names) {        this.names = names;    }}

SerialTest.java部分修改(从别人那学习到更漂亮的序列化代码,因此作了修改)

package com.example;import com.example.serialBean.InterestBean;import com.example.serialBean.ProgrammerBean;import java.io.File;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;/** * Created by Ralap on 2016/7/11. */public class SerialTest {    private static final String filePath = System.getProperty("user.dir");    private static final String fileName = "serialTest.st";    public static void test() {        // 1、Serializable可继承//        testExtends();        // 2、transient和static修饰变量,使其不参加序列化//        testNotSerial();        // 3、含有不可序列化的对象,自定义writeObject()和readObject()        testCustomSerial();        // 4、集合序列化    }    private static void testCustomSerial() {        ProgrammerBean programmerBean = new ProgrammerBean("hacker", 29, "Java");        InterestBean interest = new InterestBean("gril", "Gril God");        programmerBean.setInterest(interest);        printfn("==========Before Serialize :==========");        printfn(programmerBean.toString());        File file = createFile();        if (file == null) {            printfn("create file fail");            return;        }        // 序列化        serialize(file, programmerBean);        // 反序列化        ProgrammerBean proBean = unserialize(file);        // 打印        printfn("==========After Unserialize:==========");        printfn(proBean.toString());    }    /**     * 序列化     *     * @param file 序列化目标文件     * @param object 序列化对象     */    private static <T> void serialize(final File file, final T object) {        ObjectOutputStream objectOS = null;        try {            FileOutputStream fos = new FileOutputStream(file);            objectOS = new ObjectOutputStream(fos);            objectOS.writeObject(object);            objectOS.flush();        } catch (IOException e) {            e.printStackTrace();        } finally {            if (objectOS != null) {                try {                    objectOS.close();                } catch (IOException e) {                    e.printStackTrace();                }            }        }    }    /**     * 反序列化     * @param file 反序列化文件     * @return 反序列化出来的对象数组     */    private static <T> T unserialize(final File file) {        ObjectInputStream objectIS = null;        T retObj = null;        try {            FileInputStream fis = new FileInputStream(file);            objectIS = new ObjectInputStream(fis);            retObj =  (T)objectIS.readObject();        } catch (IOException | ClassNotFoundException e) {            e.printStackTrace();        } finally {            if (objectIS != null) {                try {                    objectIS.close();                } catch (IOException e) {                    e.printStackTrace();                }            }        }        return retObj;    }}

上面在写入和读出的时候都会报不可序列化异常:NotSerializableException

如果这是自己的类,实现一下Serializable就OK了。但万一这是别人封装好的,不能修改,那怎么办?这时就可以使用自定义的序列化方法。在类中
【1】给该属性添加修饰符transient,为不可序列化。
【2】加上下面三个方法(一般前面两个就可以)并实现之:

// Serializable接口中没有抽象方法,这些方法不是重写接口的方法,且他们都是private,但序列化时会自动调用这里的方法。这就是机制private void writeObject(ObjectOutputStream out) throws IOException {}private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException{}private void readObjectNoData() throws ObjectStreamException{}

修改后的ProgrammerBean.java

public class ProgrammerBean extends PersonBean {    private String languages;    transient private InterestBean interest;    public ProgrammerBean(String name, int age, String languages) {        super(name, age);        this.languages = languages;    }    public String getLanguages() {        return languages;    }    public void setLanguages(String languages) {        this.languages = languages;    }    public InterestBean getInterest() {        return interest;    }    public void setInterest(InterestBean interest) {        this.interest = interest;    }    @Override    public String toString() {        return getName() + "^^^" + getAge() + "^^^" + languages + ":::" + interest.getType() + "^^^" + interest.getNames();    }    private void writeObject(ObjectOutputStream out) throws IOException {        // 先使用默认写入,会自动把可序列化的序列化        out.defaultWriteObject();        out.writeUTF(interest.getType());        out.writeUTF(interest.getNames());    }    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException{        // 先使用默认读出,会自动把可序列化的读出        in.defaultReadObject();        // interest默认是null,这里必须新建。且读出与写入顺序必须一致        interest = new InterestBean(in.readUTF(), in.readUTF());     }    private void readObjectNoData() throws ObjectStreamException    {        throw new InvalidObjectException("No Stream Data");    }}

这样结果就出来:
这里写图片描述

4、集合的序列化

ArrayList实现了Serializable接口,完全可以序列化。这个完全当做是验证测试

SerialTest.java部分修改

public class SerialTest {    private static final String filePath = System.getProperty("user.dir");    private static final String fileName = "serialTest.st";    public static void test() {        // 1、Serializable可继承//        testExtends();        // 2、transient和static修饰变量,使其不参加序列化//        testNotSerial();        // 3、含有不可序列化的对象,自定义writeObject()和readObject()//        testCustomSerial();        // 4、集合序列化        testListSerial();    }    private static void testListSerial() {        List<ProgrammerBean> list = new ArrayList<>();        for (int i = 0; i < 5000; i++) {            ProgrammerBean programmerBean = new ProgrammerBean("hacker" + i, 29, "Java");            programmerBean.setInterest(new InterestBean("gril", "Gril God" + i));            list.add(programmerBean);        }//        printfn("==========Before Serialize :==========");//        printfn(programmerBean.toString());        File file = createFile();        if (file == null) {            printfn("create file fail");            return;        }        // 序列化        serialize(file, list);        // 反序列化        List<ProgrammerBean> programmerList = unserialize(file);        // 打印        printfn("==========After Unserialize:==========");        for (ProgrammerBean pro : programmerList) {            printfn(pro.toString());        }    }

结果:
这里写图片描述
……
这里写图片描述

serialTest.st文件的大小:247 KB (252,987 字节)

我们目前使用的是XML(json因故暂时不考虑),XML也可以实现序列化与反序列化,具体有什么区别,还没研究,但先来简单计算下文件大小:

①Serializable中,UTF-8编码格式,都是英文字母或数字,每个字母或数字占一个字节,一个bean中的有意义的数据(属性值)大概是33个字节,33*5000 = 165,000字节。其他的占空间的都是包名+类名,属性类型。

②XML中,有意义的数据不变,也是165,000字节,其他的主要是tag占空间。保守假设每个tag名称为5字节,格式是<tag01>value</tag01>,也就是说每个属性值还要是15字节,每个bean共有6个属性。这样一个bean就是7*15 = 105字节,tag总空间:105*5000 = 525,000字节。

So,从大小上来说,还是Serializable节省空间。代码还不用写xml这样复杂的序列化与反序列化,看到那么多Bean,简单重复的操作,真想写个框架(反射+注解)改掉它。老大说xml执行效率高,也许吧,下次验证下就知道了。

下篇进攻XML……


补充(2016/7/12 18:00):
上面第一种情况是父类序列化,子类会自动实现序列化。如果父类不序列化,子类需要序列化。如上,PersonBean不实现Serializable,而ProgrammerBean实现Serializable,其他保持原样不变。这样,子类对象在序列化时正常,但反序列化时会报以下异常:

java.io.InvalidClassException: com.example.serialBean.ProgrammerBean; no valid constructor    at java.io.ObjectStreamClass$ExceptionInfo.newInvalidClassException(ObjectStreamClass.java:150)    at java.io.ObjectStreamClass.checkDeserialize(ObjectStreamClass.java:768)    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1772)    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1350)    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:370)

这表示,在反序列化时检测的时候抛的异常,提示该类没有有效的构造方法。
解决办法:在父类中添加无参构造方法。具体原因,不详,百度也没找到满意的答案(希望哪位大神帮忙解惑下)。这让我想起了以前一位老师说过,永远给出无参构造方法。

1 0
原创粉丝点击