序列化的概念
序列化:把对象转化为字节序列的过程。
反过来说就是反序列化。
序列化的应用
1、储存对象,可以是永久的储存在硬盘的一个文件上,也可以是储存在redis支持序列化存储的容器中。
2、网络上远程传输对象。
Rpc之间调用以流字节的方式传输,java序列化是java平台上的序列化工具,一个对象需要在网络上传输,需实现 Serializable接口
@Data
@AllArgsConstructor
public class Pojo implements Serializable {
public String name;
public Integer age;
public Address address; // transient 不被序列化
public static void main(String[] args) throws IOException, ClassNotFoundException {
Address address = new Address("zj", "hz");
Pojo pojo = new Pojo("van", 22, address);
// 序列化到磁盘Io
try (FileOutputStream fos = new FileOutputStream("pojo");
ObjectOutputStream oos = new ObjectOutputStream(fos)) {
oos.writeObject(pojo);
oos.flush();
}
try (FileInputStream fis = new FileInputStream("pojo");
ObjectInputStream ois = new ObjectInputStream(fis);
) {
Pojo p = (Pojo) ois.readObject();
System.out.println(p);
}
}
}
@Data
@AllArgsConstructor
public class Address implements Serializable {
public String province;
public String city;
}
以上代码会把对象序列化,存在磁盘上
内容为�� sr !org.spafka.serializable.java.Pojo����FH�� L addresst &Lorg/spafka/serializable/java/Address;L aget Ljava/lang/Integer;L namet Ljava/lang/String;xpsr $org.spafka.serializable.java.AddressۦX%o�� L cityq ~ L provinceq ~ xpt hzt zjsr java.lang.Integer⠤���8 I valuexr java.lang.Number��� ��� xp t van
大致看得懂
对序列化过程做基准测试
Address address = new Address("zj", "hz");
Pojo pojo = new Pojo("van", 22, address);
long start = System.currentTimeMillis();
IntStream.rangeClosed(0, 1000000).forEach((i) -> {
byte[] bytes = null;
try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);) {
oos.writeObject(pojo); //335个字节长度
bytes = baos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
// System.out.println(bytes.length);
try (ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bais);) {
Pojo p = (Pojo) ois.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
});
System.out.println(System.currentTimeMillis() - start); //15630
一百万的对象的序列化反序列化花了15s,每个序列化后的大小为335个字节 对比其他序列化方式,不得不说有点弱。
protobuf是谷歌开发的一套序列化工具
谷歌出品,必属精品。
在java上开发protostuff 很容易
添加依赖包
<dependency>
<groupId>com.dyuproject.protostuff</groupId>
<artifactId>protostuff-core</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>com.dyuproject.protostuff</groupId>
<artifactId>protostuff-runtime</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>org.objenesis</groupId>
<artifactId>objenesis</artifactId>
<version>2.6</version>
</dependency>
调用起来也十分方便
public class ProtuStuffUtil {
private static volatile Map <Class <?>, Schema <?>> cachedSchema = new ConcurrentHashMap <>();
//schema 对象的描述信息, 构建schema的过程可能会比较耗时,因此希望使用过的类对应的schema能被缓存起来。代码如下,不再赘述:
private static <T> Schema <T> getSchema(Class <T> cls) {
Schema <T> schema = (Schema <T>) cachedSchema.get(cls);
if (schema == null) {
schema = RuntimeSchema.createFrom(cls);
synchronized (ProtuStuffUtil.class) {
if (schema != null) {
cachedSchema.put(cls, schema);
}
}
}
return schema;
}
private static Objenesis objenesis = new ObjenesisStd(true);
// 第3行:获得对象的类;
// 第4行:使用LinkedBuffer分配一块默认大小的buffer空间;
// 第6行:通过对象的类构建对应的schema;
// 第7行:使用给定的schema将对象序列化为一个byte数组,并返回。
@SuppressWarnings("unchecked")
public static <T> byte[] serialize(T obj) {
Class <T> cls = (Class <T>) obj.getClass();
LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
try {
Schema <T> schema = getSchema(cls);
return ProtostuffIOUtil.toByteArray(obj, schema, buffer);
} catch (Exception e) {
throw new IllegalStateException(e.getMessage(), e);
} finally {
buffer.clear();
}
}
//
// 第3行:使用objenesis实例化一个类的对象;
// 第4行:通过对象的类构建对应的schema;
// 第5,6行:使用给定的schema将byte数组和对象合并,并返回。
public static <T> T deserialize(byte[] data, Class <T> cls) {
try {
T message = objenesis.newInstance(cls);
Schema <T> schema = getSchema(cls);
ProtostuffIOUtil.mergeFrom(data, message, schema);
return message;
} catch (Exception e) {
throw new IllegalStateException(e.getMessage(), e);
}
}
}
对protostuff做基准测试
@Test
public void benchMarkProtusuff() {
long start = System.currentTimeMillis();
Address address = new Address("zj", "hz");
Pojo pojo = new Pojo("van", 22, address);
IntStream.rangeClosed(0, 1000000).forEach((i) -> {
byte[] serialize = ProtuStuffUtil.serialize(pojo);
// System.out.println(serialize.length); //17个字节长度
Pojo deserialize = ProtuStuffUtil.deserialize(serialize, Pojo.class);
});
System.out.println(System.currentTimeMillis() - start); //629
}