类实现Serializable接口进行序列化和反序列化操作

来源:互联网 发布:中国家居建材 知乎 编辑:程序博客网 时间:2024/06/06 01:33

目的

类实现Serializable接口的目的主要是为了进行可持久化操作,将临时存储在内存块中的数据转换成可传输数据

SerialVersionUID属性

当我们创建A类的对象a并进行序列化传输时,如果此时我们修改了A类,增加了某些新的属性,这时候如果不对其进行判断而进行反序列化的话,将会导致运行时异常,两者类型不匹配。因此,这里使用SerialVersionUTD属性,该属性用来唯一标识一个类的版本

SerialVersionUID属性的申明方式

1.显式申明
显示声明的格式:    private static final long serialVersionUID = xxxxL;
2.隐式声明
隐式声明依据:    通过包名、类名、继承关系、非私有的公共方法和属性,以及参数、返回值等因素计算出来的,保证这个值是唯一的

测试用例

1.创建序列化类
public class Person implements Serializable{    //显式声明SerialVersionUID;    private static final long serialVersionUID = 55799L;    private String name;    private Integer age;    //省略setter和getter方法}
2.创建序列化操作工具类
public class SerializeUtil{    //传输保存的文件位置    private static String file_name = "d://obj.bin";    //序列化    public void writeObj(Serializable s){        try{            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file_name));            oos.writeObject(s);            System.out.println("序列化成功");            oos.close();        }        catch(Exception e){            e.printStackTrace();            }       }    //反序列化    public Object readObj(){        Object obj = null;        try{            ObjectInputStream input = new ObjectInputStream(new FileInputStream(file_name));            obj = input.readObejct();            input.close();        }        catch(Exception e){            e.printStackTrace();        }           return obj;    }}
3.测试主类
public class Test{    public static void main(String[] args){        //创建持久化对象        Person per1 = new Person();        per1.setName("成兮");        per1.setAge(20);        //进行序列化        SerializeUtil util = new SerializeUtil();        util.writeObj(per1);        //反序列化读取数据        Person per2 = util.readObj();        //测试        System.out.println("我的名字->"+per2.getName()+"    我的年龄->"+per2.getAge());    }}   

部分属性序列化问题

有时候,我们对于一个持久化类中的属性只需要序列化其中一些属性,而不是全部属性,通常我们在不需要序列化的属性前面添加关键字transient关键字,不过这标志着该持久化类将会失去分布式部署的功能
在Serializable接口类中包含了两个私有的核心方法:writeObject()和readObject(),这两个方法用来控制序列化和反序列化的过程,因此我们可以重写这两个方法来实现部分属性序列化的操作
几个核心的方法
1.private void writeObject(ObjectOutputStream out):控制序列化的过程    out.defaultWriteObject():告知JVM按照默认的规则写入对象,该语句一般写在第一行    out.writeXxx:写入相关的数据2.private void readObject(ObjectInputStream in):控制反序列化的过程    in.defaultReadObject():告知JVM按照默认的规则读取对象,该语句一般写在第一行    in.readXxx:读取相关的数据
用例:在一个工资类中包含两个属性,基本工资和绩效工资,在Person类中包含两个属性,代表员工name和工资对象,我们需要满足一个条件:在序列化中只能看到员工的name和基本工资,不能够看到员工的绩效工资,也就是不序列化绩效工资这个属性
实现代码
//Salay工资类public class Salay{    private static final long serialVersionUID = 24567L;    private Double base;    private Double bounse;    //编写构造器    public Salay(Double base,Double bounse){        this.base = base;        this.bounse = bounse;    }    //省略getter和setter方法     ...}//编写Person类public class Person{    private String name;    private Salay salay;    //编写构造器    public Person(String name,Salay salay){        this.name = name;        this.salay = salay;    }    //省略getter和setter方法    ...    //编写序列化控制方法    private void writeObject(ObjectOutputStream out)throws IOException{        out.defaultWriteObject();        //控制序列化的参数        out.writeDouble(salay.getBase());    }    //编写反序列化控制方法    private void readObject(ObjectInputStream in)throws IOException{        in.defaultReadObject();        //控制反序列化的参数        salary = new Salay(in.readDouble(),0);      }}//序列化操作工具类public class SerilizeUtil{    //和前面的一样    ...}//序列化操作类public class test{    public static void main(String[] args){        Salay salay = new Salay(2000,1200);        Person person = new Person("成兮",salay);        SerializeUtil util = new SerializeUtil();        util.writeObj(person);    }}//反序列化操作类public class test2{    public static void main(String[] args){        SerializeUtil util = new SerilizeUtil();        Person person = util.readObj();        System.out.println("person.name->"+person.getName());        System.out.println("person.base->"+person.getSalay().getBase());        System.out.println("person.bounse->"+person.getSalay().getBounse());    }}//最终输出结果:    person.name->成兮    person.base->2000    person.bounse->0

注意

不要在序列化类中使用构造器来初始化不可变变量,否则如果进行序列化之后再在构造器里面修改不可变变量的值,将会修改失败,这种意外通常会导致一些重大的事故,比如数据异常等

代码测试

//序列化类,包含不可变量public class Person implements Serializable{    private static final long serialVersionUID = 12345L;    private String name;    public Person(){        this.name = "缘分五月";    }    public String getName(){        return this.name;    }}//序列化操作工具类public class SerializeUtil{    ...}//序列化测试主类public class test{    public static void main(String[] args){        Person person = new Person();        SerializeUtil util = new SerializeUtil();        util.writeObj(person);        System.out.println("序列化成功");    }}//反序列化测试主类public class test2{    public static void main(String[] args){        SerializeUtil util = new SerializeUtil();        Person person = util.readObj();        System.out.println("反序列化成功,person.name->"+person.getName());    }}
在默认情况下,在反序列化类中的person.name的值将会是缘分五月,而如果在反序列化之前将序列化类的构造器中的name的值修改一下呢?比如改成成兮,这个时候,我们进行反序列化得到的对象的name的值将会是什么呢?仍然是缘分五月,因为序列化时保存的是它,因此反序列化得到的依然是它
阅读全文
1 0
原创粉丝点击