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

目的
类实现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的值将会是什么呢?仍然是缘分五月,因为序列化时保存的是它,因此反序列化得到的依然是它
  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值