序列化和反序列化
序列化:可以将对象转化成一个字节序列,便于存储。
反序列化:将序列化的字节序列还原
优点:可以实现对象的”持久性”, 所谓持久性就是指对象的生命周期不取决于程序。
序列化方式一: 实现Serializable接口(隐式序列化)
通过实现Serializable接口,这种是隐式序列化(不需要手动),这种是最简单的序列化方式,会自动序列化所有非static和 transient关键字修饰的成员变量。
class Student implements Serializable{ private String name; private int age; public static int QQ = 1234; private transient String address = "CHINA"; Student(String name, int age ){ this.name = name; this.age = age; } public String toString() { return "name: " + name + "\n" +"age: " + age + "\n" +"QQ: " + QQ + "\n" + "address: " + address; } public void SetAge(int age) { this.age = age; } } public class SerializableDemo { public static void main(String[] args) throws IOException, ClassNotFoundException { //创建可序列化对象 System.out.println("原来的对象:"); Student stu = new Student("Ming", 16); System.out.println(stu); //创建序列化输出流 ByteArrayOutputStream buff = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(buff); //将序列化对象存入缓冲区 out.writeObject(stu); //修改相关值 Student.QQ = 6666; // 发现打印结果QQ的值被改变 stu.SetAge(18); //发现值没有被改变 //从缓冲区取回被序列化的对象 ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(buff.toByteArray())); Student newStu = (Student) in.readObject(); System.out.println("序列化后取出的对象:"); System.out.println(newStu); } } |
打印结果:
原来的对象: name: Ming age: 16 QQ: 1234 address: CHINA |
序列化后取出的对象:
name: Ming age: 16 QQ: 6666 address: null |
发现address(被transient)和QQ(被static)也没有被序列化,中途修改QQ的值是为了以防读者误会QQ被序列化了。因为序列化可以保存对象的状态,但是QQ的值被改变了,说明没有被序列化。static成员不属于对象实例,可能被别的对象修改没办法序列化,序列化是序列对象。对于address被反序列化后由于没有对应的引用,所以为null。而且Serializable不会调用构造方法。
PS:细心的可能发现序列化很诱人,可以保存对象的初始信息,在以后可以回到这个初始状态。
序列化方式二:实现Externalizable接口。(显式序列化)
Externalizable接口继承自Serializable, 我们在实现该接口时,必须实现writeExternal()和readExternal()方法,而且只能通过手动进行序列化,并且两个方法是自动调用的,因此,这个序列化过程是可控的,可以自己选择哪些部分序列化
public class Blip implements Externalizable{ private int i ; private String s; public Blip() {} public Blip(String x, int a) { System.out.println("Blip (String x, int a)"); s = x; i = a; } public String toString() { return s+i; } @Override public void writeExternal(ObjectOutput out) throws IOException { // TODO Auto-generated method stub System.out.println("Blip.writeExternal"); out.writeObject(s); out.writeInt(i); // } @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { // TODO Auto-generated method stub System.out.println("Blip.readExternal"); s = (String)in.readObject(); i = in.readInt(); } public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException { System.out.println("Constructing objects"); Blip b = new Blip("A Stirng", 47); System.out.println(b); ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream("F://Demo//file1.txt")); System.out.println("保存对象"); o.writeObject(b); o.close(); //获得对象 System.out.println("获取对象"); ObjectInputStream in = new ObjectInputStream(new FileInputStream("F://Demo//file1.txt")); System.out.println("Recovering b"); b = (Blip)in.readObject(); System.out.println(b); } } |
打印结果为:
Constructing objects Blip (String x, int a) A Stirng47 保存对象 Blip.writeExternal 获取对象 Recovering b Blip.readExternal A Stirng47 |
当注释掉writeExternal和readExternal方法后打印结果为:
Constructing objects Blip (String x, int a) A Stirng47 保存对象 Blip.writeExternal 获取对象 Recovering b Blip.readExternal null0 |
说明:Externalizable类会调用public的构造函数先初始化对象,在调用所保存的内容将对象还原。假如构造方法不是public则会出现运行时错误。
序列化方式三:实现Serializable接口+添加writeObject()和readObject()方法。(显+隐序列化)
如果想将方式一和方式二的优点都用到的话,可以采用方式三, 先实现Serializable接口,并且添加writeObject()和readObject()方法。注意这里是添加,不是重写或者覆盖。但是添加的这两个方法必须有相应的格式。
public class SerDemo implements Serializable{ public transient int age = 23; public String name ; public SerDemo(){ System.out.println("默认构造器。。。"); } public SerDemo(String name) { this.name = name; } private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); stream.writeInt(age); } private void readObject(ObjectInputStream stream) throws ClassNotFoundException, IOException { stream.defaultReadObject(); age = stream.readInt(); } public String toString() { return "年龄" + age + " " + name; } public static void main(String[] args) throws IOException, ClassNotFoundException { SerDemo stu = new SerDemo("Ming"); ByteArrayOutputStream bout = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(bout); out.writeObject(stu); ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bout.toByteArray())); SerDemo stu1 = (SerDemo) in.readObject(); System.out.println(stu1); } } |
打印结果为:
年龄23 Ming |
注释掉stream.writeInt(age)和age= stream.readInt()后:
年龄0 Ming |
方式三结合了显式和隐式序列化,Ming被正常序列化,由于age被trancient修饰,所以需要显式序列化。
Json序列化一般会使用jackson包,通过ObjectMapper类来进行一些操作,比如将对象转化为byte数组或者将json串转化为对象。现在的大多数公司都将json作为服务器端返回的数据格式。比如调用一个服务器接口,通常的请求为xxx.json?a=xxx&b=xxx的形式。Json序列化示例代码如下所示
package serialize; import java.io.IOException; import java.util.ArrayList; import java.util.List; import com.fasterxml.jackson.databind.ObjectMapper; /** * * @author liqqc * */ public class JsonSerialize { public static void main(String[] args) throws IOException { new JsonSerialize().start(); } public void start() throws IOException { User u = new User(); List |
运行结果
json serialize: 55ms; 总大小:341 json deserialize: 35ms; User: User [userId=null, userName=张三, passWord=123456, userInfo=张三是一个很牛逼的人, friends=[User [userId=null, userName=李四, passWord=123456, userInfo=李四是一个很牛逼的人, friends=null], User [userId=null, userName=王五, passWord=123456, userInfo=王五是一个很牛逼的人, friends=null]]] |
fastjson 是由阿里巴巴开发的一个性能很好的Java 语言实现的 Json解析器和生成器。特点:速度快,测试表明fastjson具有极快的性能,超越任其他的java json parser。功能强大,完全支持java bean、集合、Map、日期、Enum,支持范型和自省。无依赖,能够直接运行在Java SE 5.0以上版本
支持Android。使用时候需引入FastJson第三方jar包。FastJson序列化代码示例如下所示
package serialize; import java.util.ArrayList; import java.util.List; import com.alibaba.fastjson.JSON; /** * * @author liqqc * */ public class FastJsonSerialize { public static void main(String[] args) { new FastJsonSerialize().start(); } public void start(){ User u = new User(); List |
运行结果
fastJson serialize: 284ms; 总大小:269 fastJson serialize: 26ms; User: User [userId=null, userName=张三, passWord=123456, userInfo=张三是一个很牛逼的人, friends=[User [userId=null, userName=李四, passWord=123456, userInfo=李四是一个很牛逼的人, friends=null], User [userId=null, userName=王五, passWord=123456, userInfo=王五是一个很牛逼的人, friends=null]]] |
ProtocolBuffer是一种轻便高效的结构化数据存储格式,可以用于结构化数据序列化。适合做数据存储或 RPC 数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。
优点:跨语言;序列化后数据占用空间比JSON小,JSON有一定的格式,在数据量上还有可以压缩的空间。
缺点:它以二进制的方式存储,无法直接读取编辑,除非你有 .proto 定义,否则无法直接读出 Protobuffer的任何内容。
其与thrift的对比:两者语法类似,都支持版本向后兼容和向前兼容,thrift侧重点是构建跨语言的可伸缩的服务,支持的语言多,同时提供了全套RPC解决方案,可以很方便的直接构建服务,不需要做太多其他的工作。 Protobuffer主要是一种序列化机制,在数据序列化上进行性能比较,Protobuffer相对较好。
ProtoBuff序列化对象可以很大程度上将其压缩,可以大大减少数据传输大小,提高系统性能。对于大量数据的缓存,也可以提高缓存中数据存储量。原始的ProtoBuff需要自己写.proto文件,通过编译器将其转换为java文件,显得比较繁琐。百度研发的jprotobuf框架将Google原始的protobuf进行了封装,对其进行简化,仅提供序列化和反序列化方法。其实用上也比较简洁,通过对JavaBean中的字段进行注解就行,不需要撰写.proto文件和实用编译器将其生成.java文件,百度的jprotobuf都替我们做了这些事情了。
一个带有jprotobuf注解的JavaBean如下所示
package serialize; import java.io.Serializable; import java.util.List; import com.baidu.bjf.remoting.protobuf.FieldType; import com.baidu.bjf.remoting.protobuf.annotation.Protobuf; public class User implements Serializable { private static final long serialVersionUID = -7890663945232864573L; @Protobuf(fieldType = FieldType.INT32, required = false, order = 1) private Integer userId; @Protobuf(fieldType = FieldType.STRING, required = false, order = 2) private String userName; @Protobuf(fieldType = FieldType.STRING, required = false, order = 3) private String passWord; @Protobuf(fieldType = FieldType.STRING, required = false, order = 4) private String userInfo; @Protobuf(fieldType = FieldType.OBJECT, required = false, order = 5) private List |
jprotobuf序列化代码示例如下所示
package serialize; import java.io.IOException; import java.util.ArrayList; import java.util.List; import com.baidu.bjf.remoting.protobuf.Codec; import com.baidu.bjf.remoting.protobuf.ProtobufProxy; /** * * @author liqqc * */ public class ProtoBuffSerialize { public static void main(String[] args) throws IOException { new ProtoBuffSerialize().start(); } public void start() throws IOException { Codec |
运行结果
jprotobuf序列化耗时:9ms; 总大小:148 jprotobuf反序列化耗时:0ms; User: User [userId=null, userName=张三, passWord=123456, userInfo=张三是一个很牛逼的人, friends=[User [userId=null, userName=李四, passWord=123456, userInfo=李四是一个很牛逼的人, friends=null], User [userId=null, userName=王五, passWord=123456, userInfo=王五是一个很牛逼的人, friends=null]]] |