您的位置:首页 > 其它

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

2017-05-28 01:54 549 查看

目的

类实现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的值将会是什么呢?仍然是缘分五月,因为序列化时保存的是它,因此反序列化得到的依然是它
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐