序列化的概念
序列化:把对象转化为字节序列的过程。
反过来说就是反序列化。
序列化的应用
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 }百万次的序列化只有629ms,其中大部分时间或在构造schema 上,序列化的大小也只有17byte,远远强于java自带的序列化,推荐开发高性能rpc服务上首选采用protustuff