序列化(三) 实例分析深入了解序列化

一.什么是序列化和反序化
对象的序列化是把对象写到一个输出流中。反序列化从这把输入流读取一个对象。

二。为什么要序列化
(1) 把对象持久化到一个文件中。像我们在做单元测试的时候,对于构造好的数据,可以持久化到文件中,这样就不用再从数据库中读取,在数据库中的测试数据很容易被人篡改。
(2) 像RMI,SOCKET,HESSION 等进行网络传输对象的场合,要把对象转成流的形式传递给客户端。而客户端对于取的流(byte[])要进行反序列化。

三。如何进行序列化
实现Serializable 接口

四。Serializable的作用
序列化运行时使用一个称为 serialVersionUID 的版本号与每个可序列化类相关联,该序列号在反序列化过程中用于验证序列化对象的发送者和接收者是否为该对象加载了与序列化兼容的类。如果接收者加载的该对象的类的 serialVersionUID 与对应的发送者的类的版本号不同,则反序列化将会导致 InvalidClassException。可序列化类可以通过声明名为 "serialVersionUID" 的字段(该字段必须是静态 (static)、最终 (final) 的 long 型字段)显式声明其自己的 serialVersionUID:

五。Serializable的使用说明
ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;
如果可序列化类未显式声明 serialVersionUID,则序列化运行时将基于该类的各个方面计算该类的默认 serialVersionUID 值,如“Java(TM) 对象序列化规范”中所述。不过,强烈建议 所有可序列化类都显式声明 serialVersionUID 值,原因是计算默认的 serialVersionUID 对类的详细信息具有较高的敏感性,根据编译器实现的不同可能千差万别,这样在反序列化过程中可能会导致意外的 InvalidClassException。因此,为保证 serialVersionUID 值跨不同 java 编译器实现的一致性,序列化类必须声明一个明确的 serialVersionUID 值。还强烈建议使用 private 修饰符显示声明 serialVersionUID(如果可能),原因是这种声明仅应用于直接声明类 -- serialVersionUID 字段作为继承成员没有用处。数组类不能声明一个明确的 serialVersionUID,因此它们总是具有默认的计算值,但是数组类没有匹配 serialVersionUID 值的要求。

六。实例说明

序列化对象

import java.io.Serializable;

public class Customer implements Serializable{

private static final long serialVersionUID = 1L;
private Long userId;
private String name;
private String password;
private int age;

public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}

}



测试代码:


public static void main(String[] args) throws Exception {

ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("D:/objectFile.obj"));

//创建对象
Customer customer = new Customer();
customer.setUserId(1L);
customer.setName("张三");
customer.setPassword("123");
customer.setAge(10);

// 序列化对象
out.writeObject("你好!");
out.writeObject(new Date());
out.writeObject(customer);
out.writeInt(123); // 写入基本类型数据
out.close();

// 反序列化对象
ObjectInputStream in = new ObjectInputStream(new FileInputStream("D:/objectFile.obj"));
System.out.println("obj1=" + (String) in.readObject());
System.out.println("obj2=" + (Date) in.readObject());
Customer obj3 = (Customer) in.readObject();
System.out.println("obj3=" + obj3);
System.out.println("userId :"+obj3.getUserId());
System.out.println("name :"+obj3.getName());
System.out.println("password :"+obj3.getPassword());
System.out.println("age :"+obj3.getAge());
int obj4 = in.readInt();
System.out.println("obj4=" + obj4);
in.close();

}


输出内容:
obj1=你好!
obj2=Thu Jun 23 23:26:11 CST 2011
obj3=user.Customer@c20e24
obj4=123

如果Customer去掉implements Serializable
则会抛出异常:
Exception in thread "main" java.io.NotSerializableException: user.Customer
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1156)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:326)
at demo.Demo1.main(Demo1.java:28)
只有实现seriliable接口才可以进行序列化

先序列化对象,并保存。
在返序列化的时候,修改
	private static final long serialVersionUID = 2L;

则会抛异常:
Exception in thread "main" java.io.InvalidClassException: user.Customer; local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:562)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1583)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1496)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1732)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1329)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:351)
at demo.Demo1.main(Demo1.java:36)
原因就是serialVersionUID 有校验版本兼容性的作用。

七 transient 关键字
对于一些比较敏感信息,不希望被反序化,如password,可以加上transient ,则不会被反序列。其他未加transient属性信息都会正常序列化和反序化。

八 writeObject 和 readObject
像ObjectOutputStream和ObjectInputStream 两个类提供了序列化和反序列化的方法。当然也可以自己重写这两个方法。自己定义序列化和反序列化,这往往涉及一些加密和解密算法,记得大学有一门课程“信息论”的一门课程,提到了一系列加密算法,可以序列化产生的流做处理,保证网络传输的安全性。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值