java的序列化入门

63 篇文章 11 订阅

概念

序列化并不是java专属的,在java中,序列化是指把java对象转为可以在网络上传输的格式,或者可持久化的格式的过程。

个人理解就是转成字符形式,显然java对象本身是不能在网络上传输的,也不能在磁盘中存储,所以不管是转成二进制编码,还是转成JSON格式,还是转成其他什么格式,只要完事还能转回java对象,就算达到目的了。

所以有序列化还得有反序列化,也就是把序列化后的结果转成java对象。


JDK自带的序列化方式

JDK自带的序列化方式,是通过java类实现java.io.Serializable接口来实现的。Serializable接口内部没有需要实现的方法,所以这个接口只是一个标识,有这个标识的java类,JDK可以通过自己的方式把这个类的对象进行序列化和反序列化。

JDK的序列化会把java对象序列化为二进制编码。


serialVersionUID属性

JDK的序列化需要该java类有一个serialVersionUID属性,是privatestatic final long类型,序列化和反序列化的时候这个值必须是一致的,否则反序列化时会报错。开发者可以显式指定这个值,比如设置为1L,Eclipse也可以给协助计算一个特别长的long型值用以设置,也可以不显式指定。

注意:如果不显式指定serialVersionUID,JDK会根据java类的一些信息自己计算出一个来,计算的方式对java类的状态十分敏感,很可能会导致序列化双方计算的结果不同,从而在反序列化时失败,因此使用序列化时一定要显式指定serialVersionUID的值。


父子类序列化

如果父类没有实现java.io.Serializable接口,那么序列化时不会把父类一起序列化。由于反序列化时要优先构造父类(其实java对象构建的时候都是这样),所以会调用父类无参构造方法,父类中的属性会被设为类型初始值,比如int的初始值0,String的初始值null。


writeObject方法和readObject方法

即使是private类型的属性的属性值也会被序列化,如果有需要保密的属性值,需要自行在类中定义writeObject方法和readObject方法进行,就像这样:

package test;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectInputStream.GetField;
import java.io.ObjectOutputStream;
import java.io.ObjectOutputStream.PutField;
import java.io.Serializable;

public class Test implements Serializable{
	
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	public String password;
	
	public String getPassword(){
		return password;
	}
	
	public void setPassword(String password) {
	    this.password = password;
	}
	
	private void writeObject(ObjectOutputStream out) {
	    try {
	        PutField putFields = out.putFields();
	        putFields.put("password", password+"abc");//密码后面加abc字符,权当是加密了
	        out.writeFields();
	        System.out.println("保存到文件的内容:"+password+"abc");
	    } catch (IOException e) {
	        e.printStackTrace();
	    }
	}
	 
	private void readObject(ObjectInputStream in) {
	    try {
	        GetField readFields = in.readFields();
	        Object object = readFields.get("password", "");
	        password = object.toString().substring(0,object.toString().length()-3);
	    } catch (IOException e) {
	        e.printStackTrace();
	    } catch (ClassNotFoundException e) {
	        e.printStackTrace();
	    }
	}
	
	public static void main(String[] args) {
		try {
			Test test1= new Test();
			test1.setPassword("123456");//假设密码是123456
	        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("E:\\大文件\\siaTest"));
	        out.writeObject(test1);
	        out.close();
	        
	        ObjectInputStream oin = new ObjectInputStream(new FileInputStream("E:\\大文件\\siaTest"));
	        Test test2 = (Test) oin.readObject();
	        System.out.println("反序列化后的内容:"+test2.getPassword());
	        oin.close();
	    } catch (FileNotFoundException e) {
	        e.printStackTrace();
	    } catch (IOException e) {
	        e.printStackTrace();
	    } catch (ClassNotFoundException e) {
	        e.printStackTrace();
	    }
	}
	
}

输出的结果是:

保存到文件的内容:123456abc
反序列化后的内容:123456
注意,类中的这两个方法必须是private类型,因为ObjectOutputStream和ObjectInputStream会使用反射,并调用getPrivateMethod方法来获得这两个方法。

如果没有在类中显式指定这两个方法,ObjectOutputStream和ObjectInputStream将使用它们默认的defaultWriteObject()和defaultReadObject()方法。

 

JDK序列化需要注意

1,static类型的属性值无法被序列化,因为该类型的属性属于java类,不属于某个java对象。

2,Transient类型的属性值无法被自动序列化,这种类型的值在反序列化时被设置为类型初始值,比如int的初始值0,String的初始值null。不过可以在自定义的writeObject方法和readObject方法中自行处理。


JSON的序列化方式

JSON的工具类有很多,阿里巴巴的FastJson,Google的Gson,开源的Jackson,dubbo的JSON等等,其作用都在于把对象转为JSON格式。

和JDK自带的序列化模式的比较:

优点:

语言无关,速度更快(特别是FastJson),序列化后的内容比JDK的字节码更小,内容可读等等,现在很多人选择JSON方式来序列化信息。

缺点:

不能保存类的格式,只是属性名和属性值,使用时需要注意。


一些序列化工具和框架

1,Google的protoBuf

protoBuf项目已开源,可以把对象转换为二进制格式,性能较高,序列化后的内容字节数比较小,除了java外还支持其他语言。


2,Kryo

Kryo是针对java的序列化框架,速度比JDK快很多,序列化后文件大小比JDK小一点

maven依赖:

<dependency>

   <groupId>com.esotericsoftware</groupId>

   <artifactId>kryo</artifactId>

   <version>3.0.3</version>

</dependency>

3, FST

fst是完全兼容JDK序列化协议的系列化框架,序列化速度大概是JDK的4-10倍,大小是JDK大小的1/3左右。

maven依赖:

<dependency>

 <groupId>de.ruedigermoeller</groupId>

 <artifactId>fst</artifactId>

 <version>2.04</version>

</dependency>


4, Hessian2

Hessian2是一个轻量级的,自定义描述的二进制RPC协议,是跨语言的。

hessian2在java用来序列化的类是:com.caucho.hessian.io.JavaSerializer(Class,ClassLoader),其中用到了反射技术。

 

从上面的比较来看,JDK自带的序列化貌似是时间最长,效果最差的,这可能也是其他一些序列化方式,特别是java环境下的序列化方式出现的原因吧。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值