Java序列化总结
什么是序列化?
序列化是将对象的状态信息转化为可以存储或传输的形式的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。以后可以通过存储区中读取或反序列化对象的状态重新创建对象。
为什么要序列化?
有两个最重要的原因促使对序列化的使用:一个原因是将对象的状态保持在存储媒体中,以便可以在以后重新创建精确的副本;另一个原因是通过值将对象从一个应用程序域发送到另一个应用程序域中。例如,在网络中传输的数据都必须要序列化。
Java中的序列化
Java中的序列化机制能够将一个实例对象的状态信息写入到一个字节流中,使其可以通过socket进行传输或者持久化存储到数据库或文件系统中,然后 再需要的时候可以读取字节流中的信息重构一个相同的对象。序列化在Java中有着广泛的应用,RMI、Hessian等技术都是以此为基础的。
下面是一些序列化涉及到的内容的例子。
UserInfo类是下面序列化例子中都要用到的一个保存基本信息的类。
public class UserInfo implements Serializable { private static final long serialVersionUID = 1L;
public static String defaultPostcode = "310000";
private int age;
private String name; public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public static String getDefaultPostcode() {
return defaultPostcode;
} public static void setDefaultPostcode(String defaultPostcode) {
UserInfo.defaultPostcode = defaultPostcode;
} public void desSelf() {
System.out.println("Default Postcode: " + getDefaultPostcode());
System.out.println("Age: " + getAge());
System.out.println("Name: " + getName());
}
}
结合UserInfo的内容,先看下面这个main方法。
public static void main(String[] args) throws IOException, ClassNotFoundException {
FileOutputStream fos = new FileOutputStream("temp.out");
ObjectOutputStream oos = new ObjectOutputStream(fos);
UserInfo user = new UserInfo();
user.setAge(25);
user.setName("Tom");
System.out.println("Before Serialize");
user.desSelf();
// 保存对象后修改了DefaultPostcode
UserInfo.setDefaultPostcode("110");
oos.writeObject(user);
oos.flush();
FileInputStream fis = new FileInputStream("temp.out");
ObjectInputStream ois = new ObjectInputStream(fis);
user = (UserInfo)ois.readObject();
System.out.println("After Deserialize");
user.desSelf();
}
在5、6两行设置了age为25,name为Tom,然后输出了UserInfo自己的描述:
Before Serialize
Default Postcode: 310000
Age: 25
Name: Tom
可以看到Age和Name分别是设置的值。defaultPostCode是UserInfo的一个静态变量,它的值是类中指定的310000。然后将这个对象进行了序列化,保存在temp.out中。
在第10行修改了defaultPostcode的值为110,然后反序列化并输出user的描述信息,结果如下:
After Deserialize
Default Postcode: 110
Age: 25
Name: Tom
为什么反序列化后defaultPostcode不是310000而是修改的110呢?因为序列化保存的是对象的状态,而静态变量属于类的状态,在序列化的时候不会被保存。
注意,需要被序列化的对象必须实现Serializable接口,否则在序列化的时候会抛出java.io.NotSerializableException异常。
实现Serializable接口总是会要求添加一个serialVersionUID属性,有以下两种形式:
private static final long serialVersionUID = 5511561554099546149L;
private static final long serialVersionUID = 1L;
它们有什么区别呢?一个是固定的 1L,一个是随机生成一个不重复的 long 类型数据(实际上是使用 JDK
工具生成),在这里有一个建议,如果没有特殊需求,就是用默认的 1L 就可以,这样可以确保代码一致时反序列化成功。那么随机生成的序列化 ID
有什么作用呢,有些时候,通过改变序列化 ID 可以用来限制某些用户的使用。
下面看一个和序列化相关的关键字Transient(在《ArrayList源码分析》中提到过)。
Java的serialization提供了一种持久化对象实例的机制。当持久化对象时,可能有一个特殊的对象数据成员,我们不想用
serialization机制来保存它。为了在一个特定对象的一个域上关闭serialization,可以在这个域前加上关键字transient。
transient是Java语言的关键字,用来表示一个域不是该对象串行化的一部分。当一个对象被串行化的时候,transient型变量的值不包括在串行化的表示中,然而非transient型的变量是被包括进去的。
有点抽象,看个例子应该能明白。
public class UserInfo implements Serializable {
private static final long serialVersionUID = 996890129747019948L;
private String name;
private transient String psw; public UserInfo(String name, String psw) {
this.name = name;
this.psw = psw;
} public String toString() {
return "name=" + name + ", psw=" + psw;
}
} public class TestTransient {
public static void main(String[] args) {
UserInfo userInfo = new UserInfo("张三", "123456");
System.out.println(userInfo);
try {
// 序列化,被设置为transient的属性没有被序列化
ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream(
"UserInfo.out"));
o.writeObject(userInfo);
o.close();
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
try {
// 重新读取内容
ObjectInputStream in = new ObjectInputStream(new FileInputStream(
"UserInfo.out"));
UserInfo readUserInfo = (UserInfo) in.readObject();
//读取后psw的内容为null
System.out.println(readUserInfo.toString());
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
下面说一下上文提到的ArrayList中的writeObject和readObject。先看这两个方法在ArrayList中的具体内容。
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException{
int expectedModCount = modCount;
s.defaultWriteObject();
s.writeInt(elementData.length);
for (int i=0; i<size; i++)
s.writeObject(elementData[i]); if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
} private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
int arrayLength = s.readInt();
Object[] a = elementData = new Object[arrayLength];
for (int i=0; i<size; i++)
a[i] = s.readObject();
}
这两个方法都是private的且没在ArrayList中被调用过,那为什么需要这两个方法呢?
writeObject和readObject并不是在每个类和接口中都会定义,而只是定义在哪些在序列化和反序列化过程中需要特殊处理的类中。
stackoverflow上的解答:http://stackoverflow.com/questions/7467313/why- are-readobject-and-writeobject-private-and-why-would-i-write-transient-vari
也就是说通过这两个方法可以自己去控制序列化和反序列化的过程。下面是这两个方法的一个例子。
private static final long serialVersionUID = 1L; private String password = "pass"; public String getPassword() {
return password;
} public void setPassword(String password) {
this.password = password;
} private void writeObject(ObjectOutputStream out) {
try {
PutField putFields = out.putFields();
System.out.println("原密码:" + password);
password = "encryption";//模拟加密
putFields.put("password", password);
System.out.println("加密后的密码" + password);
out.writeFields();
} catch (IOException e) {
e.printStackTrace();
}
} private void readObject(ObjectInputStream in) {
try {
GetField readFields = in.readFields();
Object object = readFields.get("password", "");
System.out.println("要解密的字符串:" + object.toString());
password = "pass";//模拟解密,需要获得本地的密钥
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} } public static void main(String[] args) {
try {
ObjectOutputStream out = new ObjectOutputStream(
new FileOutputStream("result.obj"));
out.writeObject(new Test());
out.close(); ObjectInputStream oin = new ObjectInputStream(new FileInputStream(
"result.obj"));
Test t = (Test) oin.readObject();
System.out.println("解密后的字符串:" + t.getPassword());
oin.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
writeObject 方法中,对密码进行了加密,在 readObject 中则对 password 进行解密,只有拥有密钥的客户端,才可以正确的解析出密码,确保了数据的安全。
最后说一下序列化的存储规则。
public class Test {
public static void main(String[] args) throws IOException,
ClassNotFoundException {
FileOutputStream fos = new FileOutputStream("temp.out");
ObjectOutputStream oos = new ObjectOutputStream(fos);
UserInfo user = new UserInfo();
user.setAge(25);
user.setName("Tom");
oos.writeObject(user);
oos.flush();
System.out.println(new File("temp.out").length());
oos.writeObject(user);
oos.flush();
oos.close();
System.out.println(new File("temp.out").length()); FileInputStream fis = new FileInputStream("temp.out");
ObjectInputStream ois = new ObjectInputStream(fis);
UserInfo user1 = (UserInfo) ois.readObject();
UserInfo user2 = (UserInfo) ois.readObject();
ois.close();
System.out.println(user1 == user2);
}
}
为什么同一对象写入两次,文件的大小不是写入一次的文件大小的两倍呢?而第三次写入和第二次写入增加的长度是一样的呢?为什么反序列化后的两个对象比较结果是true呢?
Java 序列化机制为了节省磁盘空间,具有特定的存储规则,当写入文件的为同一对象时,并不会再将对象的内容进行存储,而只是再次存储一份引用,上面增加的 5 字节的存储空间就是新增引用和一些控制信息的空间。反序列化时,恢复引用关系,使得user1和user2二者相等,输出 true。该存储规则极大的节省了存储空间。
肯定还有我不知道的内容,望多交流讨论。
http://www.cnblogs.com/hzmark/archive/2013/01/30/Serialization.html
Java序列化总结的更多相关文章
- Java 序列化与反序列化
1.什么是序列化?为什么要序列化? Java 序列化就是指将对象转换为字节序列的过程,而反序列化则是只将字节序列转换成目标对象的过程. 我们都知道,在进行浏览器访问的时候,我们看到的文本.图片.音频. ...
- Java序列化与反序列化
Java序列化与反序列化是什么?为什么需要序列化与反序列化?如何实现Java序列化与反序列化?本文围绕这些问题进行了探讨. 1.Java序列化与反序列化 Java序列化是指把Java对象转换为字节序列 ...
- java序列化
什么是java序列化,如何实现java序列化? 我们有时候将一个java对象变成字节流的形式传出去或者从一个字节流中恢复成一个java对象,例如,要将java对象存储到硬盘或者传送给网络上的其他计算机 ...
- Java 序列化Serializable详解
Java 序列化Serializable详解(附详细例子) Java 序列化Serializable详解(附详细例子) 1.什么是序列化和反序列化Serialization(序列化)是一种将对象以一连 ...
- 关于java序列化中的一个细节
java序列化机制的可以参考很多资料了,最近在看的时候发现了一些问题. 1. 默认的序列化机制,很多书里讲到序列化类只序列化类名,实例变量,不会实例化类变量(static)和瞬态变量(transien ...
- 各种Java序列化性能比较
转载:http://www.jdon.com/concurrent/serialization.html 这里比较Java对象序列化 XML JSON Kryo POF等序列化性能比较. 很多人以 ...
- Java序列化格式详解
RPC的世界,由于涉及到进程间网络远程通信,不可避免的需要将信息序列化后在网络间传送,序列化有两大流派: 文本和二进制. 文本序列化 序列化的实现有很多方式,在异构系统中最常用的就是定义成人类可读的文 ...
- 简述java序列化
1. 什么是Java对象序列化 Java平台允许我们在内存中创建可复用的Java对象,但一般情况下,只有当JVM处于运行时,这些对象才可能存在,即,这些对象的生命周期不会比JVM的生命周期 ...
- 透过byte数组简单分析Java序列化、Kryo、ProtoBuf序列化
序列化在高性能网络编程.分布式系统开发中是举足轻重的之前有用过Java序列化.ProtocolBuffer等,在这篇文章这里中简单分析序列化后的byte数组观察各种序列化的差异与性能,这里主要分析Ja ...
- [转] Java序列化与反序列化
原文地址:http://blog.csdn.net/wangloveall/article/details/7992448 Java序列化与反序列化是什么?为什么需要序列化与反序列化?如何实现Java ...
随机推荐
- 【WEB前端开发最佳实践系列】CSS篇
一.有效组织CSS代码 规划组织CSS代码:组织CSS代码文件,所有的CSS都可以分为2类,通用类和业务类.代码的组织应该把通用类和业务类的代码放在不同的目录中. 模块内部的另一样式规则:样式声明的顺 ...
- 【css系列】创建网页加载进度条
一.最简单或者明显的方式是使用定时器 1.在网页中加入布局覆盖真实网页内容 2.使用定时器确定加载所用时间的长短,其实并不是真正的加载进度实现 <!DOCTYPE html> <ht ...
- package.json字段全解(转)
Name 必须字段. 小提示: 不要在name中包含js, node字样: 这个名字最终会是URL的一部分,命令行的参数,目录名,所以不能以点号或下划线开头: 这个名字可能在require()方法中被 ...
- CF510B Fox And Two Dots(搜索图形环)
B. Fox And Two Dots time limit per test 2 seconds memory limit per test 256 megabytes input standard ...
- [Apio2008]免费道路[Kruscal]
3624: [Apio2008]免费道路 Time Limit: 2 Sec Memory Limit: 128 MBSec Special JudgeSubmit: 1292 Solved: ...
- IOS 6 和 IOS7 UITableViewCell上添加控件的获取
假设每个cell上面都有UIButton,怎么判断哪个Cell上的按钮被按下了呢? IOS6上 -(IBAction)btnClick:(id)sender { UIButton *btn = (UI ...
- MFC修改窗口无标题和标题信息,修改执执行文件图标
一.创建MFC后 窗口显示的是 无标题-工程名 修改方法在网上看到了几种,下面介绍下比较简单的一种: 1.在MianFrame.c文件中找到这个函数 BOOL CMainFrame::PreCreat ...
- 关于kvm虚拟机的克隆方法总结
kvm虚拟机的克隆分为两种情况,第一种kvm宿主机上对虚拟机直接克隆 第二种通过复制配置文件与磁盘文件的虚拟机复制克隆(适用于异机的静态迁移). 现笔者将分别两种kvm虚拟机克隆的的详细操作过程都记录 ...
- iOS开发-数据存储NSCoder
软件中永远绕不开的一个问题就是数据存储的问题,PC的时候一般都是选择在数据库中存储,iOS如果是和后端配合的话,那么不需要考虑数据存储的这个问题,上次写了一下plist的存储,不过数据都是存储一些简单 ...
- R子集subset
> x<-c(6,1,2,3,NA,12) > x[x>5] #x[5]是未知的,因此其值是否大于5也是未知的 [1] 6 NA 12 > subset(x,x& ...