这篇文章主要讲解了“何为序列化”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“何为序列化”吧!
序列化就是一种处理对象流
的机制。
即将对象的内容流化,将数据转化成字节流
,以便存储在文件
中或用于在网络
中传输,当然用的最多的肯定就是网络传输
。
从文件
中或网络
上获得序列化后的对象字节流后,根据字节流中所保存的对象状态及描述信息,通过反序列化重建对象
。
大家所常见的序列化/反序列化的方式:Serializable
。
谁敢说没见过Serializable,给我拖出的吊树上打
。
JDK提供的Serializable速度较慢,原因比如:因为加入了序列化版本号,类名等信息,所以导致码流变大,速度变慢等等。
所以我们要来学一些速度快的序列化方式。不仅速度快,占用的空间还小。
这么好的技术,那个妹子没学到,真是可惜了。
通用性:是否只能用于java间序列化/反序列化,是否跨语言、跨平台
性能:分为空间开销和时间开销,序列化后的数据一般用于存储或网络传输,其大小是很重要的一个参数;
当然解析的时间也影响了序列化协议的选择
易用性:API使用是否复杂,是否影响开发效率
可扩展性:实体类的属性变更会不会导致反序列化异常,这通常会在系统升级时会产生,参考性不是很大
Kryo 是一个快速序列化/反序列化工具,其使用了字节码生成机制(底层依赖了 ASM 库),因此具有比较好的运行速度。
Kryo 序列化出来的结果,是其自定义的、独有的一种格式,不再是 JSON 或者其他现有的通用格式;
而且,其序列化出来的结果是二进制的(即 byte[];而 JSON 本质上是字符串 String);
二进制数据显然体积更小,序列化、反序列化时的速度也更快。
Kryo 一般只用来进行序列化(然后作为缓存,或者落地到存储设备之中)、反序列化,而不用于在多个系统、甚至多种语言间进行数据交换 —— 目前 kryo 也只有 java 实现。
像Redis这样的存储工具,是可以安全的存储二进制数据,所以一般项目中可使用Kryo来替代JDK序列化进行存储。
使用场景
:(数据交换或数据持久化)比如使用kryo把对象序列化成字节数组发送给消息队列或者放到redis等nosql中等等应用场景。
<dependency> <groupId>com.esotericsoftware</groupId> <artifactId>kryo</artifactId> <version>4.0.1</version></dependency>
需要注意的是,由于kryo使用了较高版本的asm,可能会与业务现有依赖的asm产生冲突,这是一个比较常见的问题。只需将依赖改成:
<dependency> <groupId>com.esotericsoftware</groupId> <artifactId>kryo-shaded</artifactId> <version>4.0.2</version></dependency>
注意
:由于kryo不是线程安全的,针对多线程情况下的使用,要对kryo进行一个简单的封装设计,从而可以多线程安全的使用序列化和反序列化
/** * 序列化工具(程序调用该接口来实现obj<->byte[]之间的序列化/反序列化) */public interface Serializer{ /** * 序列化 */ public void serialize(Object t,byte[] bytes); /** * 序列化 */ public void serialize(Object obj, byte[] bytes, int offset, int count); /** * 反序列化 */ public <T>T deserialize(byte[] bytes); /** * 反序列化 */ public <T>T deserialize(byte[] bytes, int offset, int count);}
/** * 基于kyro的序列化/反序列化工具 */public class kryoSerializer implements Serializer { // 由于kryo不是线程安全的,所以每个线程都使用独立的kryo final ThreadLocal<Kryo> kryoLocal = new ThreadLocal<Kryo>() { @Override protected Kryo initialValue() { Kryo kryo = new Kryo(); kryo.register(ct, new BeanSerializer<>(kryo, ct)); return kryo; } }; // 序列化threadlocal final ThreadLocal<Output> outputLocal = new ThreadLocal<Output>(); // 反序列化threadlocal final ThreadLocal<Input> inputLocal = new ThreadLocal<Input>(); private Class<?> ct = null; public kryoSerializer(Class<?> ct) { this.ct = ct; } /** * 序列化 */ @Override public void serialize(Object obj, byte[] bytes) { Kryo kryo = getKryo(); Output output = getOutput(bytes); kryo.writeObjectOrNull(output, obj, obj.getClass()); output.flush(); } /** * 序列化 */ @Override public void serialize(Object obj, byte[] bytes, int offset, int count) { Kryo kryo = getKryo(); Output output = getOutput(bytes, offset, count); kryo.writeObjectOrNull(output, obj, obj.getClass()); output.flush(); } /** * 反序列化 */ @SuppressWarnings("unchecked") @Override public <T> T deserialize(byte[] bytes, int offset, int count) { Kryo kryo = getKryo(); Input input = getInput(bytes, offset, count); return (T) kryo.readObjectOrNull(input, ct); } /** * 反序列化 */ @Override public <T> T deserialize(byte[] bytes) { return deserialize(bytes, 0, bytes.length); } /** * 获取kryo */ private Kryo getKryo() { return kryoLocal.get(); } /** * 获取Output并设置初始数组 */ private Output getOutput(byte[] bytes) { Output output = null; if ((output = outputLocal.get()) == null) { output = new Output(); outputLocal.set(output); } if (bytes != null) { output.setBuffer(bytes); } return output; } /** * 获取Output */ private Output getOutput(byte[] bytes, int offset, int count) { Output output = null; if ((output = outputLocal.get()) == null) { output = new Output(); outputLocal.set(output); } if (bytes != null) { output.writeBytes(bytes, offset, count); } return output; } /** * 获取Input */ private Input getInput(byte[] bytes, int offset, int count) { Input input = null; if ((input = inputLocal.get()) == null) { input = new Input(); inputLocal.set(input); } if (bytes != null) { input.setBuffer(bytes, offset, count); } return input; } public Class<?> getCt() { return ct; } public void setCt(Class<?> ct) { this.ct = ct; }
Kryo致力以简单易用的API,序列化过程中主要核心有Kryo
、Output
、Input
。
Output和Input是Kryo的IO
,他们支持以byte array
或者stream
的形式为序列化的dest和反序列化的source。
当使用stream形式进行写出写入时,需要close这些Output和Input。
写出时,当OutputDe buffer是满的时候,就会flush bytes到stream中。
写入时,会从stream中获取bytes到Input buffer中,当填充满时,进行反序列化。
和很多其他的序列化框架一样,Kryo为了提供性能和减小序列化结果体积,提供注册的序列化对象类的方式。
在注册时,会为该序列化类生成int ID,后续在序列化时使用int ID唯一标识该类型。
注册的方式如下:
kryo.register(SomeClass.class);
或者可以明确指定注册类的int ID,但是该ID必须大于等于0。如果不提供,内部将会使用int++的方式维护一个有序的int ID生成。
kryo.register(SomeClass.class, 1);
这是对循环引用的支持,可以有效防止栈内存溢出,kryo默认会打开这个属性。
当你确定不会有循环引用发生的时候,可以通过以下代码关闭循环引用检测,从而提高一些性能,但不是很推荐
Kryo kryo = new Kryo();kryo.setReferences(false);
如果序列化的对象类型未知并且可能为空:
kryo.writeClassAndObject(output, object);// ...Object object = kryo.readClassAndObject(input);if (object instanceof SomeClass) { // ...}
如果对象类型已知并且可能为空:
kryo.writeObjectOrNull(output, someObject);// ...SomeClass someObject = kryo.readObjectOrNull(input, SomeClass.class);
如果对象类型已知并且不可能为空:
kryo.writeObject(output, someObject);// ...SomeClass someObject = kryo.readObject(input, SomeClass.class);
Kryo默认是线程不安全的,有两种解决方式:
一个是通过Threadlocal的方式为某个线程存储一个实例:
private static final ThreadLocal<Kryo> kryoThreadLocal = new ThreadLocal<Kryo>() { protected Kryo initialValue() { Kryo kryo = new Kryo(); // 这里可以增加一系列配置信息 return kryo; }};
另外一种是通过KryoPool的方式,该方式在性能上也好于ThreadLocal:
public KryoPool createPool() { return new KryoPool.Builder(() -> { Kryo kryo = new Kryo(); // 此处也可以进行一系列配置,可通过实现KryoFactory接口来满足动态注册,抽象该类 return kryo; }).softReferences().build();}
boolean | Boolean | byte | Byte | char |
---|---|---|---|---|
Character | short | Short | int | Integer |
long | Long | float | Float | double |
Double | byte[] | String | BigInteger | BigDecimal |
Collection | Date | Collections.emptyList | Collections.singleton | Map |
StringBuilder | TreeMap | Collections.emptyMap | Collections.emptySet | KryoSerializable |
StringBuffer | Class | Collections.singletonList | Collections.singletonMap | Currency |
Calendar | TimeZone | Enum | EnumSet |
跨语言支持较复杂
不支持对象字段的增加/删除/修改
如果更改了对象的字段,然后再从更改前序列化的bytes中反序列化,将会出错。
当然如果想得到Add、Remove等操作的支持,可以使用FieldSerializer的其他扩展,如TaggedFieldSerializer
、VersionFieldSerializer
等等。
import java.io.Serializable;import java.util.Map;public class Simple implements Serializable { private static final long serialVersionUID = -4914434736682797743L; private String name; private int age; private Map<String,Integer> map; public Simple(){ } public Simple(String name,int age,Map<String,Integer> map){ this.name = name; this.age = age; this.map = map; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Map<String, Integer> getMap() { return map; } public void setMap(Map<String, Integer> map) { this.map = map; }}
import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.util.HashMap;import java.util.Map;public class OriginalSerializable { public static void main(String[] args) throws IOException, ClassNotFoundException { long start = System.currentTimeMillis(); setSerializableObject(); System.out.println("java原生序列化时间:" + (System.currentTimeMillis() - start) + " ms" ); start = System.currentTimeMillis(); getSerializableObject(); System.out.println("java原生反序列化时间:" + (System.currentTimeMillis() - start) + " ms"); } public static void setSerializableObject() throws IOException{ FileOutputStream fo = new FileOutputStream("D:/file2.bin"); ObjectOutputStream so = new ObjectOutputStream(fo); for (int i = 0; i < 100000; i++) { Map<String,Integer> map = new HashMap<String, Integer>(2); map.put("zhang0", i); map.put("zhang1", i); so.writeObject(new Simple("zhang"+i,(i+1),map)); } so.flush(); so.close(); } public static void getSerializableObject(){ FileInputStream fi; try { fi = new FileInputStream("D:/file2.bin"); ObjectInputStream si = new ObjectInputStream(fi); Simple simple =null; while((simple=(Simple)si.readObject()) != null){ //System.out.println(simple.getAge() + " " + simple.getName()); } fi.close(); si.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { //e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } }}
import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;import java.util.HashMap;import java.util.Map;import org.objenesis.strategy.StdInstantiatorStrategy;import com.esotericsoftware.kryo.Kryo;import com.esotericsoftware.kryo.KryoException;import com.esotericsoftware.kryo.io.Input;import com.esotericsoftware.kryo.io.Output;public class KyroSerializable { public static void main(String[] args) throws IOException { long start = System.currentTimeMillis(); setSerializableObject(); System.out.println("Kryo 序列化时间:" + (System.currentTimeMillis() - start) + " ms" ); start = System.currentTimeMillis(); getSerializableObject(); System.out.println("Kryo 反序列化时间:" + (System.currentTimeMillis() - start) + " ms"); } public static void setSerializableObject() throws FileNotFoundException{ Kryo kryo = new Kryo(); kryo.setReferences(false); kryo.setRegistrationRequired(false); kryo.setInstantiatorStrategy(new StdInstantiatorStrategy()); kryo.register(Simple.class); Output output = new Output(new FileOutputStream("D:/file1.bin")); for (int i = 0; i < 100000; i++) { Map<String,Integer> map = new HashMap<String, Integer>(2); map.put("zhang0", i); map.put("zhang1", i); kryo.writeObject(output, new Simple("zhang"+i,(i+1),map)); } output.flush(); output.close(); } public static void getSerializableObject(){ Kryo kryo = new Kryo(); kryo.setReferences(false); kryo.setRegistrationRequired(false); kryo.setInstantiatorStrategy(new StdInstantiatorStrategy()); Input input; try { input = new Input(new FileInputStream("D:/file1.bin")); Simple simple =null; while((simple=kryo.readObject(input, Simple.class)) != null){ //System.out.println(simple.getAge() + " " + simple.getName() + " " + simple.getMap().toString()); } input.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch(KryoException e){ } }}
感谢各位的阅读,以上就是“何为序列化”的内容了,经过本文的学习后,相信大家对何为序列化这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是亿速云,小编将为大家推送更多相关知识点的文章,欢迎关注!
亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。
原文链接:https://my.oschina.net/u/4588451/blog/4691343