Java序列化的几种方式

 

序列化和反序列化
序列化:可以将对象转化成一个字节序列,便于存储。
反序列化:将序列化的字节序列还原
优点:可以实现对象的”持久性”, 所谓持久性就是指对象的生命周期不取决于程序。

原生序列化方式

序列化方式一: 实现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()方法。注意这里是添加,不是重写或者覆盖。但是添加的这两个方法必须有相应的格式。

  1. 方法必须要被private修饰 —–>才能被调用
  2. 第一行调用默认的defaultRead/WriteObject() —–>隐式序列化非static和transient
  3. 调用read/writeObject()将获得的值赋给相应的值 —–>显式序列化
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序列化

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<User> friends = new ArrayList<>();
        u.setUserName("张三");
        u.setPassWord("123456");
        u.setUserInfo("张三是一个很牛逼的人");
        u.setFriends(friends);
 
        User f1 = new User();
        f1.setUserName("李四");
        f1.setPassWord("123456");
        f1.setUserInfo("李四是一个很牛逼的人");
 
        User f2 = new User();
        f2.setUserName("王五");
        f2.setPassWord("123456");
        f2.setUserInfo("王五是一个很牛逼的人");
 
        friends.add(f1);
        friends.add(f2);
 
        ObjectMapper mapper = new ObjectMapper();
        Long t1 = System.currentTimeMillis();
        byte[] writeValueAsBytes = null;
        for (int i = 0; i < 10; i++) {
            writeValueAsBytes = mapper.writeValueAsBytes(u);
        }
        System.out.println("json serialize: " + (System.currentTimeMillis() - t1) + "ms; 总大小:" + writeValueAsBytes.length);
        Long t2 = System.currentTimeMillis();
        User user = mapper.readValue(writeValueAsBytes, User.class);
        System.out.println("json deserialize: " + (System.currentTimeMillis() - t2) + "ms; User: " + user);
 
    }
}

运行结果

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序列化

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<User> friends = new ArrayList<>();
        u.setUserName("张三");
        u.setPassWord("123456");
        u.setUserInfo("张三是一个很牛逼的人");
        u.setFriends(friends);
 
        User f1 = new User();
        f1.setUserName("李四");
        f1.setPassWord("123456");
        f1.setUserInfo("李四是一个很牛逼的人");
 
        User f2 = new User();
        f2.setUserName("王五");
        f2.setPassWord("123456");
        f2.setUserInfo("王五是一个很牛逼的人");
 
        friends.add(f1);
        friends.add(f2);
 
        //序列化  
        Long t1 = System.currentTimeMillis();
        String text = null;
        for(int i = 0; i<10; i++) {
            text = JSON.toJSONString(u); 
        }
        System.out.println("fastJson serialize: " +(System.currentTimeMillis() - t1) + "ms; 总大小:" + text.getBytes().length);
        //反序列化  
        Long t2 = System.currentTimeMillis();
        User user = JSON.parseObject(text, User.class);
        System.out.println("fastJson serialize: " + (System.currentTimeMillis() -t2) + "ms; User: " + user);
    }
}

运行结果

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]]]

 

4、ProtoBuff序列化

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<User> friends;
 
    public Integer getUserId() {
        return userId;
    }
 
    public void setUserId(Integer userId) {
        this.userId = userId;
    }
 
    public String getUserName() {
        return userName;
    }
 
    public void setUserName(String userName) {
        this.userName = userName;
    }
 
    public String getPassWord() {
        return passWord;
    }
 
    public void setPassWord(String passWord) {
        this.passWord = passWord;
    }
 
    public String getUserInfo() {
        return userInfo;
    }
 
    public void setUserInfo(String userInfo) {
        this.userInfo = userInfo;
    }
 
    public List<User> getFriends() {
        return friends;
    }
 
    public void setFriends(List<User> friends) {
        this.friends = friends;
    }
 
    @Override
    public String toString() {
        return "User [userId=" + userId + ", userName=" + userName + ", passWord=" + passWord + ", userInfo=" + userInfo
                + ", friends=" + friends + "]";
    }
 
}

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<User> studentClassCodec = ProtobufProxy.create(User.class, false);
 
        User u2 = new User();
        List<User> friends = new ArrayList<>();
        u2.setUserName("张三");
        u2.setPassWord("123456");
        u2.setUserInfo("张三是一个很牛逼的人");
        u2.setFriends(friends);
 
        User f1 = new User();
        f1.setUserName("李四");
        f1.setPassWord("123456");
        f1.setUserInfo("李四是一个很牛逼的人");
 
        User f2 = new User();
        f2.setUserName("王五");
        f2.setPassWord("123456");
        f2.setUserInfo("王五是一个很牛逼的人");
        friends.add(f1);
        friends.add(f2);
 
        Long stime_jpb_encode = System.currentTimeMillis();
        byte[] bytes = null;
        for(int i = 0; i<10; i++) {
            bytes = studentClassCodec.encode(u2);
        }
        System.out.println("jprotobuf序列化耗时:" + (System.currentTimeMillis() - stime_jpb_encode) + "ms; 总大小:" + bytes.length);
 
        Long stime_jpb_decode = System.currentTimeMillis();
        User user = studentClassCodec.decode(bytes);
        Long etime_jpb_decode = System.currentTimeMillis();
        System.out.println("jprotobuf反序列化耗时:"+ (etime_jpb_decode-stime_jpb_decode) + "ms; User: " + user);
    }
 
}

运行结果

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]]]

 

  • 6
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值