Serializable的序列化与反序列化

使用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……

时间: 2024-10-08 09:58:19

Serializable的序列化与反序列化的相关文章

71 Serializable(序列化和反序列化)

对象的输出流:ObjectOutputStream  把对象输出到文件存储起来,我们称作为序列化对象的输入流:ObjectInputStream   把对象从文件中读取出来,我们称作为反序列化 ObjectOutputStream    构造方法:        ObjectOutputStream()            为完全重新实现 ObjectOutputStream 的子类提供一种方法,让它不必分配仅由 ObjectOutputStream 的实现使用的私有数据.        Ob

Serializable接口序列化与反序列化

参考链接 我们在编写实现Serializable接口的类的时候,IDE会提示:需要增加一个Serial Version ID. 为什么要增加? 它是怎么计算出来的? 有什么用? 类实现Serializable接口的目的是为了可持久化,比如网络传输或本地存储.实现序列化很简单: 1 public class Person implements Serializable { 2 private String name; 3 /*name属性的getter/setter方法省略*/ 4 } 这里以Ja

对象序列化和反序列化应该写为一个工具类

对象序列化和反序列化工具类 package com.umu.util; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectInputStream; import java.io.Object

XML序列化与反序列化+自定义XML注解框架XmlUtils

背景 前面一篇总结了Serializable的序列化与反序列化,现在接着总结XML.主要内容:XML基本的序列化与反序列化方法.一些注意事项.以及自定义了一个XML注解框架(简洁代码,解放双手). XML的序列化与反序列化 先与Serializable进行简单的对比: Serializable存储的文件,打开后无法正常查看,安全性高.xml文件可通过文本编辑器查看与编辑,可读性高(浏览器会格式化xml文件,更方便查看),安全性低: Serializable文件通过了签名,只能在自己的程序中反序列

序列化与反序列化总结(Serializable和Parcelable)

序列化是指将对象的状态信息转换为可以存储或传输的形式的过程. 在Java中创建的对象,只要没有被回收就可以被复用,但是,创建的这些对象都是存在于JVM的堆内存中,JVM处于运行状态时候,这些对象可以复用, 但是一旦JVM停止,这些对象的状态也就丢失了. 在实际生活中,需要将对象持久化,需要的时候再重新读取出来,通过对象序列化,可以将对象的状态保存为字节数组,需要的时候再将字节数组反序列化为对象. 对象序列化可以很容易的在JVM中的活动对象和字节数组(流)之间转换,广泛用于RMI(远程方法调用)以

Java对象的序列化和反序列化Serializable

1.什么是序列化和反序列化 Serialization(序列化)是一种将对象以一连串的字节描述的过程:反序列化deserialization是一种将这些字节重建成一个对象的过程. 2.什么情况下需要序列化 a)当你想把的内存中的对象保存到一个文件中或者数据库中时候: b)当你想用套接字在网络上传送对象的时候: c)当你想通过RMI传输对象的时候: 3.如何实现序列化 将需要序列化的类实现Serializable接口就可以了,Serializable接口中没有任何方法,可以理解为一个标记,即表明这

序列化与反序列化之Parcelable和Serializable浅析

转载请注明出处(万分感谢!): http://blog.csdn.net/javazejian/article/details/52665164 出自[zejian的博客] 本篇小部分内容摘自android开发艺术探索 ??在日常的应用开发中,我们可能需要让某些对象离开内存空间,存储到物理磁盘,以便长期保存,同时也能减少对内存的压力,而在需要时再将其从磁盘读取到内存,比如将某个特定的对象保存到文件中,隔一段时间后再把它读取到内存中使用,那么该对象就需要实现序列化操作,在java中可以使用Seri

Serializable 指示一个类可以序列化;ICloneable支持克隆,即用与现有实例相同的值创建类的新实例(接口);ISerializable允许对象控制其自己的序列化和反序列化过程(接口)

Serializable : 序列化是指将对象实例的状态存储到存储媒体的过程.在此过程中,先将对象的公共字段和私有字段以及类的名称(包括类所在的程序集)转换为字节流,然后再把字节流写入数据流.在随后对对象进行反序列化时,将创建出与原对象完全相同的副本. 在 面向对象的环境中实现序列化机制时,必须在易用性和灵活性之间进行一些权衡.只要您对此过程有足够的控制能力,就可以使该过程在很大程度上自动进行.例 如,简单的二进制序列化不能满足需要,或者,由于特定原因需要确定类中那些字段需要序列化. 基本序列化

使用Serializable接口进行JAVA的序列化和反序列化

OBJECT STREAMS – SERIALIZATION AND DESERIALIZATION IN JAVA EXAMPLE USING SERIALIZABLE INTERFACE Hitesh Garg | November 7, 2014 | io | 9 Comments In the previous java tutorials I have discussed about basic of java streams, byte streams, then a modifie