同步操作将从 小牛肉/cs-wiki 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
Kryo 是一个快速高效的 Java 对象图形序列化框架,主要特点是性能、高效和易用。该项目用来序列化对象到文件、数据库或者网络。
To use the latest Kryo release in your application, use this dependency entry in your pom.xml
:
<dependency>
<groupId>com.esotericsoftware</groupId>
<artifactId>kryo</artifactId>
<version>5.0.3</version>
</dependency>
示例代码:
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import java.io.*;
public class HelloKryo {
static public void main (String[] args) throws Exception {
Kryo kryo = new Kryo();
kryo.register(SomeClass.class);
SomeClass object = new SomeClass();
object.value = "Hello Kryo!";
// 序列化
Output output = new Output(new FileOutputStream("file.bin"));
kryo.writeObject(output, object);
output.close();
// 反序列化
Input input = new Input(new FileInputStream("file.bin"));
SomeClass object2 = kryo.readObject(input, SomeClass.class);
input.close();
}
static public class SomeClass {
String value;
}
}
Kryo 共支持三种读写方式
1. 如果知道 class 字节码,并且对象不为空
kryo.writeObject(output, someObject);
// ...
SomeClass someObject = kryo.readObject(input, SomeClass.class);
快速入门中的序列化/反序列化的方式便是这一种。而 Kryo 考虑到 someObject 可能为null,也会导致返回的结果为null,所以提供了第二套读写方式。
2. 如果知道 class 字节码,并且对象可能为空
kryo.writeObjectOrNull(output, someObject);
// ...
SomeClass someObject = kryo.readObjectOrNull(input, SomeClass.class);
但这两种方法似乎都不能满足我们的需求,在 RPC 调用中,序列化和反序列化分布在不同的端点,对象的类型确定,我们不想依赖于手动指定参数,最好是将字节码的信息直接存放到序列化结果中,在反序列化时自行读取字节码信息。Kryo 考虑到了这一点,于是提供了第三种方式。
3. 如果实现类的字节码未知,并且对象可能为 null
kryo.writeClassAndObject(output, object);
// ...
Object object = kryo.readClassAndObject(input);
if (object instanceof SomeClass) {
// ...
}
我们牺牲了一些空间一些性能去存放字节码信息,但这种方式是我们在 RPC 中应当使用的方式。
这都是其默认支持的。
Kryo kryo = new Kryo();
kryo.addDefaultSerializer(SomeClass.class, SomeSerializer.class);
这样的方式,也可以为一个 Kryo 实例扩展序列化器。
总体而言,Kryo支持以下的类型:
但需要注意的是,Kryo不支持Bean中增删字段。如果使用Kryo序列化了一个类,存入了Redis,对类进行了修改,会导致反序列化的异常。
另外需要注意的一点是使用反射创建的一些类序列化的支持。如使用Arrays.asList();
创建的 List
对象,会引起序列化异常。
Exception in thread "main" com.esotericsoftware.kryo.KryoException: Class cannot be created (missing no-arg constructor): java.util.Arrays$ArrayList
但new ArrayList()
创建的List
对象则不会,使用时需要注意,可以使用第三方库对 Kryo 进行序列化类型的扩展。如https://github.com/magro/kryo-serializers所提供的。
不支持包含无参构造器类的反序列化,尝试反序列化一个不包含无参构造器的类将会得到以下的异常:
Exception in thread "main" com.esotericsoftware.kryo.KryoException: Class cannot be created (missing no-arg constructor): moe.cnkirito.Xxx
保证每个类具有无参构造器是应当遵守的编程规范,但实际开发中一些第三库的相关类不包含无参构造,的确是有点麻烦。
Kryo是线程不安全的,意味着每当需要序列化和反序列化时都需要实例化一次,或者借助ThreadLocal
来维护以保证其线程安全。
private static final ThreadLocal<Kryo> KryoThreadLocal = new ThreadLocal<Kryo>() {
protected Kryo initialValue() {
Kryo kryo = new Kryo();
// configure kryo instance, customize settings
return kryo;
};
};
// Somewhere else, use Kryo
Kryo k = KryoThreadLocal.get();
...
每个 Kryo 实例都可以拥有两个配置参数
kryo.setRegistrationRequired(false); // 关闭注册行为(默认)
kryo.setReferences(true); // 支持循环引用
当 kryo 写一个对象的实例的时候,默认需要将类的完全限定名称写入。将类名一同写入序列化数据中是比较低效的,所以 kryo 支持通过类注册进行优化。如
Kryo kryo = new Kryo();
kryo.register(SomeClass.class);
// ...
Output output = ...
SomeClass someObject = ...
kryo.writeObject(output, someObject);
这会赋予该 Class 一个从 0 开始的编号,但 Kryo 使用注册行为最大的问题在于,其不保证同一个 Class 每一次注册的号码相同,这与注册的顺序有关,也就意味着在不同的机器、同一个机器重启前后都有可能拥有不同的编号,这会导致序列化产生问题,所以在分布式项目中,一般关闭注册行为。
第二个注意点在于循环引用,Kryo 为了追求高性能,可以关闭循环引用的支持。不过我并不认为关闭它是一件好的选择,大多数情况下,请保持 kryo.setReferences(true)
。
public class KryoSerializer {
public byte[] serialize(Object obj) {
Kryo kryo = KryoThreadLocal.get();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
Output output = new Output(byteArrayOutputStream);//<1>
kryo.writeClassAndObject(output, obj);//<2>
output.close();
return byteArrayOutputStream.toByteArray();
}
public <T> T deserialize(byte[] bytes) {
Kryo kryo = KryoThreadLocal.get();
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
Input input = new Input(byteArrayInputStream);// <1>
input.close();
return (T) kryo.readClassAndObject(input);//<2>
}
private static final ThreadLocal<Kryo> KryoThreadLocal = new ThreadLocal<Kryo>() {//<3>
@Override
protected Kryo initialValue() {
Kryo kryo = new Kryo();
kryo.setReferences(true); // 默认值为true
kryo.setRegistrationRequired(false); //默认值为 false
return kryo;
}
};
}
<1> Kryo的Input
和Output
接收一个InputStream
和OutputStream
,Kryo通常完成字节数组和对象的转换,所以常用的输入输出流实现为ByteArrayInputStream
/ByteArrayOutputStream
。
<2> 记录类型信息的 writeClassAndObject
和readClassAndObject
配对使用在分布式场景下是最常见的,这算是 kryo 的一个特点,可以把对象信息直接写到序列化数据里,反序列化的时候可以精确地找到原始类信息,不会出错,这意味着在写 readxxx
方法时,无需传入 Class 或 Type 类信息。
<3> 使用ThreadLocal
维护Kryo实例,这样减少了每次使用都实例化一次 Kryo 的开销又可以保证其线程安全。
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。