Java 序列化
来源:互联网 发布:外国语大学网络教育 编辑:程序博客网 时间:2024/06/11 17:36
Java序列化
Java允许我们在内存中创建可复用的Java对象,但一般情况下,只有当JVM处于运行时,这些对象才可能存在;也即,这些对象的生命周期不会比JVM的生命周期更长。但在实际应用中,就可能要求在JVM停止运行之后能够持久化指定的对象,并在将来重新读取被保存的对象。
使用Java对象序列化,在保存对象时,会把其状态(只是对象状态,不包括类变量)保存为一组字节,以便再将这些字节组装成对象。
除了在持久化对象时会用到对象序列化之外,当使用RMI,或在网络中传递对象时,都会用到对象序列化。
1、 Serializable接口
在Java中,只要一个类实现了Java.io.Serializable接口,那么它就可以被序列化,如下
/*枚举默认继承java.lang.Enum,而该类实现了Serializable接口
* */
public enum Gender
{
FEMALE,
MALE,
OTHER
}
public class Person implements Serializable
{
private static final long serialVersionUID = 1L;
private int id;
private Stringname;
private int age;
private Gendergender;
private Stringinfo;
public Person()
{
}
/*省略setter和getter方法
* */
}
为什么一个类实现了Serializable接口,就可以被序列化呢?其实底层采用的是ObjectOutputStream类来进行序列化的,其中ObjectOutputStream类中最重要的一个方法为writeObject0,部分代码人如下:
private void writeObject0(Object obj, boolean unshared)
throws IOException
{
// …
// remaining cases
if (objinstanceof String)
{
writeString((String)obj,unshared);
}
else if (cl.isArray())
{
writeArray(obj,desc,unshared);
}
else if (obj instanceof Enum)
{
writeEnum((Enum)obj,desc, unshared);
}
else if (obj instanceof Serializable)
{
writeOrdinaryObject(obj,desc,unshared);
}
else
{
// …
}
}
2、 持久化对象
对于实现Serializable接口的类的实例,可以将对象持久化到磁盘中,在持久化时需要用到ObjectOutputStream流,如下
/*持久化*/
File file=new File("/Users/ssl/person.out");
OutputStream outputStream=new FileOutputStream(file);
ObjectOutputStream objectOutputStream=newObjectOutputStream(outputStream);
Person person=new Person();
person.setId(1);
person.setName("ssl");
person.setAge(18);
person.setGender(Gender.MALE);
person.setInfo("ssl");
objectOutputStream.writeObject(person);
objectOutputStream.close();
/*反序列化*/
ObjectInputStream objectInputStream=newObjectInputStream(new FileInputStream(file));
Person p=(Person)objectInputStream.readObject();
objectInputStream.close();
System.out.println(p);
3、 默认序列化机制
如果仅仅只是让某个实现类实现Serializable接口,而没有其他任何处理的话,则就是使用默认序列化机制。使用默认序列化机制,在序列化对象时,不仅会序列化当前对象本身,还会对该对象引用的其他对象也进行序列化(若属性对象没有实现Serializable接口,将会报错),同样地,若引用对象也引用其他的对象,这些对象都会被序列化。所以,如果一个对象包含的成员变量是容器类对象,而这些容器所含的元素也是容器类,那么序列化的过程就会复杂,开销也大。
此外,如果父类实现了Serializable接口,其子类都可以被序列化;若子类实现了Serializable接口,而父类没有,则父类中的属性不能被序列化,子类中的属性仍能够被序列化,结果导致父类的信息丢失。在纯Java环境下,Java序列换能够很好的工作,但是在跨平台的系统中,最好采用通用存储数据结构,如JSON或XML等。
3.1、transient
若是某个属性不想被序列化,则可以加上transient关键字。默认序列化机制就会忽略该字段。
3.2、writeObject、readObject
在序列化和反序列化过程中会调用到writeObject和readObject方法,这两个方法为私有方法,在序列化和反序列化过程中会通过反射机制来调用。所以在序列化类中添加这些方法,可以改变默认的序列化机制。
public class Person implements Serializable
{
private static final long serialVersionUID = 1L;
private int id;
private Stringname;
transient private int age;
private Gendergender;
private Stringinfo;
public Person()
{
}
/*添加writeObject、readObject方法会影响默认的序列化机制*/
private void writeObject(ObjectOutputStream out)throws IOException
{
/*defaultWriteObject会执行默认的序列化机制*/
out.defaultWriteObject();
out.writeInt(age);
}
private void readObject(ObjectInputStream in)throwsClassNotFoundException, IOException
{
in.defaultReadObject();
age=in.readInt();
}
}
3.3、Externalizable接口
无论是使用transient关键字,还是使用writeObject和readObject方法,其实都是基于Serializable接口的序列化。JDK还提供了另一个序列化接口-Externalizable,使用该接口之后,之前基于Serializable接口的序列化机制就将失效。
Externalizable继承Serializable,当使用该接口时,序列化的细节需要由程序员显示完成,重写
public void writeExternal(ObjectOutput out)throws IOException
public void readExternal(ObjectInput in)throws IOException,
ClassNotFoundException
等方法来完成序列化操作。
当使用Externalizable进行序列化时,读取对象时,会调用序列化类无参的构造函数来创建一个新的对象,然后再将被保存对象的字段的值填充到新对象中,所以序列化类最好要提供无参的构造函数(默认的构造函数)。
3.4、readResolve
当我们使用单例模式时,应该是期望某个类的实例是唯一的,但如果该类是可序列化的,那么情况可能会略有不同。
当从文件中反序列化得到的对象与原单例对象并不是一个对象,为了能在序列化过程中仍保持单例的特性,可以在单例类中添加一个readResolve()方法,在该方法中直接返回Person的单例对象。
private Object readResolve();
无论是实现Serializable接口,或者Externalizable接口,当从I/O流中读取对象时,readResolve()方法都会被调用到,实际上就是用readResolve()方法返回的对象直接替换在反序列化过程中创建的对象。
4、 高级认识
将Java对象序列化为二进制文件,是Java序列化的本质。在不部分情况下,开发人员只需要了解被序列化的类实现Serializable接口,使用ObjectInputStream和ObjectOutputStream进行对象的读写。然而,在某些情况下,知道这些是远远不够的,下面列举一些Java序列化中的高级知识。
4.1、序列化ID
序列化可以使Java对象在网络中传输,在A端序列化的对象经过网络传向B端,在B端反序列化为对象,此时要求A段和B段都有被序列化的类或.class文件。若此时,被序列化的类中没有显示指定序列化ID会出现什么情况呢?
虚拟机是否允许序列化,不仅取决于类路径和功能代码是否一致,一个非常重要的一点的是两个类的序列化ID是否一致。
private static final long serialVersionUID =1L;
若两个类的序列化ID不一致,他们之间是无法相互序列化和反序列化的。简单来说,Java的序列化机制是通过在运行时判断类的序列化ID来验证版本一致性的。在进行反序列化时,JVM会把传来字节流中的序列化ID与本地对应的类中序列化ID进行比较。如果相同就认为是一致的,可以进行反序列化;否则就会出现序列化版本不一致的异常。
4.2、静态变量
在序列化时,并不持久化静态变量。那么,在本地序列化和反序列化时,如何获取静态变量?在网络传输时,又如何获取静态变量。
4.3、敏感字段加密
在序列化过程中,虚拟机会试图调用类对象中的writeObject和readObject方法,进行用户自定义的序列化和反序列化。如果没有这些方法,则默认会调用ObjectOutputStream的defaultWriteObject方法以及ObjectInputStream的defaultReadObject方法。基于该过程,我们自定义writeObject和readObject方法,来改变序列化的数值,如用于敏感字段的加密工作。
4.4、序列化存储规则
Java序列化机制为了节省磁盘空间,具有特定的存储规则。当写入文件的为同一个对象时,并不会再将对象的内容进行存储,而只是再次存储一份引用。反序列化时,恢复引用关系。
- Serializable java序列化
- Java对象序列化
- java序列化-Serializable
- Serializable java序列化
- Serializable java序列化
- Java对象序列化
- Java对象序列化
- Java对象序列化
- Serializable java序列化
- JAVA序列化Serializable
- java对象序列化
- Java 对象序列化
- DEMO-JAVA序列化
- Java 对象序列化
- java 序列化
- Java对象序列化
- Serializable java序列化
- java序列化介绍
- 自定义iOS7导航栏背景,标题和返回按钮文字颜色
- iphone的表情保存到数据库失败-- utf8 ——utf8mb4
- CodeForces 556A Case of the Zeros and Ones
- [UVA120]Stacks of Flapjacks[STL][构造]
- ZOJ - 3829 Known Notation
- Java 序列化
- 悼念512汶川大地震遇难同胞——老人是真饿了
- iOS视频网站
- UIImage存为本地文件,UIImage转换为NSData
- 使用XML资源文件来定义颜色
- windows多线程同步和互斥关系
- Hdu 5309 JRY is Fighting 2015 Multi-University Training Contest 2
- android broadcast基础学习
- ios开发:导航栏navigationbar背景渐变