zoukankan      html  css  js  c++  java
  • java中的序列化和反序列化

    介绍

    序列化就是将对象转换成字节序列,反序列化就是将字节序列转换成对象。

    使用

    默认序列化和反序列化

    public class Client {
      public static void main(String[] args) {
        User user = new User();
        user.setUserName("lisi");
        System.out.println(user);
        byte[] data = serialize(user);
        User newUser = deserialize(data, User.class);
        System.out.println(newUser);
        System.out.println(user == newUser);
      }
    
    // 将一个对象序列化成字节序列
      private static byte[] serialize(Object obj) {
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
             ObjectOutputStream oos = new ObjectOutputStream(baos);) {
          oos.writeObject(obj);
          return baos.toByteArray();
        } catch (IOException e) {
          e.printStackTrace();
        }
        return null;
      }
    // 将字节序列反序列化成指定类型的对象
      private static <T> T deserialize(byte[] data, Class<T> clazz) {
        try (ByteArrayInputStream bais = new ByteArrayInputStream(data);
             ObjectInputStream bis = new ObjectInputStream(bais)) {
          Object obj = bis.readObject();
          return clazz.cast(obj);
        } catch (IOException | ClassNotFoundException e) {
          e.printStackTrace();
        }
        return null;
      }
    
      @Setter
      @Getter
      @ToString
      public static class User implements Serializable {
        private String userName;
      }
    }
    

    一个类要序列化必须实现Serializable接口,序列化字段为非static非transient修饰,

    从结果可以看到反序列化出的对象是一个新的对象。

    自定义序列化和反序列化

    public class Client {
      public static void main(String[] args) {
        User user = new User();
        user.setUserName("lisi");
        System.out.println(user);
        byte[] data = serialize(user);
        User newUser = deserialize(data, User.class);
        System.out.println(newUser);
        System.out.println(user == newUser);
      }
    
      private static byte[] serialize(Object obj) {
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
             ObjectOutputStream oos = new ObjectOutputStream(baos);) {
          oos.writeObject(obj);
          return baos.toByteArray();
        } catch (IOException e) {
          e.printStackTrace();
        }
        return null;
      }
    
      private static <T> T deserialize(byte[] data, Class<T> clazz) {
        try (ByteArrayInputStream bais = new ByteArrayInputStream(data);
             ObjectInputStream bis = new ObjectInputStream(bais)) {
          Object obj = bis.readObject();
          return clazz.cast(obj);
        } catch (IOException | ClassNotFoundException e) {
          e.printStackTrace();
        }
        return null;
      }
    
      @Setter
      @Getter
      @ToString
      public static class User implements Serializable {
        private String userName;
    // 自定义序列化过程
        private void writeObject(ObjectOutputStream oos) throws IOException {
    // 默认序列化逻辑
          oos.defaultWriteObject();
    // 将用户名长度序列化
          oos.writeInt(userName.length());
        }
    // 自定义反序列化过程
        private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
    // 默认反序列化逻辑
          ois.defaultReadObject();
    // 读取用户名长度
          System.out.println(ois.readInt());
        }
      }
    }
    

    自定义序列化必须添加writeObject方法,必须为private非static,参数类型为ObjectOutputStream,返回类型为void。
    自定义反序列化必须添加readObject方法,必须为private非static,参数类型为ObjectInputStream,返回类型为void。


    HashMap就是通过自定义来实现序列化和反序列化的。

    /**
         * Save the state of the <tt>HashMap</tt> instance to a stream (i.e.,
         * serialize it).
         *
         * @serialData The <i>capacity</i> of the HashMap (the length of the
         *             bucket array) is emitted (int), followed by the
         *             <i>size</i> (an int, the number of key-value
         *             mappings), followed by the key (Object) and value (Object)
         *             for each key-value mapping.  The key-value mappings are
         *             emitted in no particular order.
         */
        private void writeObject(java.io.ObjectOutputStream s)
            throws IOException {
            int buckets = capacity();
            // Write out the threshold, loadfactor, and any hidden stuff
            s.defaultWriteObject();
            s.writeInt(buckets);
            s.writeInt(size);
            internalWriteEntries(s);
        }
    
        /**
         * Reconstitute the {@code HashMap} instance from a stream (i.e.,
         * deserialize it).
         */
        private void readObject(java.io.ObjectInputStream s)
            throws IOException, ClassNotFoundException {
            // Read in the threshold (ignored), loadfactor, and any hidden stuff
            s.defaultReadObject();
            reinitialize();
            if (loadFactor <= 0 || Float.isNaN(loadFactor))
                throw new InvalidObjectException("Illegal load factor: " +
                                                 loadFactor);
            s.readInt();                // Read and ignore number of buckets
            int mappings = s.readInt(); // Read number of mappings (size)
            if (mappings < 0)
                throw new InvalidObjectException("Illegal mappings count: " +
                                                 mappings);
            else if (mappings > 0) { // (if zero, use defaults)
                // Size the table using given load factor only if within
                // range of 0.25...4.0
                float lf = Math.min(Math.max(0.25f, loadFactor), 4.0f);
                float fc = (float)mappings / lf + 1.0f;
                int cap = ((fc < DEFAULT_INITIAL_CAPACITY) ?
                           DEFAULT_INITIAL_CAPACITY :
                           (fc >= MAXIMUM_CAPACITY) ?
                           MAXIMUM_CAPACITY :
                           tableSizeFor((int)fc));
                float ft = (float)cap * lf;
                threshold = ((cap < MAXIMUM_CAPACITY && ft < MAXIMUM_CAPACITY) ?
                             (int)ft : Integer.MAX_VALUE);
    
                // Check Map.Entry[].class since it's the nearest public type to
                // what we're actually creating.
                SharedSecrets.getJavaOISAccess().checkArray(s, Map.Entry[].class, cap);
                @SuppressWarnings({"rawtypes","unchecked"})
                Node<K,V>[] tab = (Node<K,V>[])new Node[cap];
                table = tab;
    
                // Read the keys and values, and put the mappings in the HashMap
                for (int i = 0; i < mappings; i++) {
                    @SuppressWarnings("unchecked")
                        K key = (K) s.readObject();
                    @SuppressWarnings("unchecked")
                        V value = (V) s.readObject();
                    putVal(hash(key), key, value, false, false);
                }
            }
        }
    

    更彻底的自定义

    public class Client {
      public static void main(String[] args) {
        User user = new User();
        user.setUserName("lisi");
        System.out.println(user);
        byte[] data = serialize(user);
        User newUser = deserialize(data, User.class);
        System.out.println(newUser);
        System.out.println(user == newUser);
      }
    
      private static byte[] serialize(Object obj) {
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
             ObjectOutputStream oos = new ObjectOutputStream(baos);) {
          oos.writeObject(obj);
          return baos.toByteArray();
        } catch (IOException e) {
          e.printStackTrace();
        }
        return null;
      }
    
      private static <T> T deserialize(byte[] data, Class<T> clazz) {
        try (ByteArrayInputStream bais = new ByteArrayInputStream(data);
             ObjectInputStream bis = new ObjectInputStream(bais)) {
          Object obj = bis.readObject();
          return clazz.cast(obj);
        } catch (IOException | ClassNotFoundException e) {
          e.printStackTrace();
        }
        return null;
      }
    
      @Setter
      @Getter
      @ToString
      public static class User implements Serializable {
        private String userName;
    // 替换序列化对象
        private Object writeReplace() {
          User user = new User();
          user.setUserName(userName);
          return user;
        }
    
    // 处理反序列出的对象
        private Object readResolve() {
          userName = "lisi1";
          return this;
        }
      }
    }
    

    writeReplace方法会在真正的序列化执行之前执行,会替换我们的待序列化对象,readResolve会在反序列化执行之后执行,就可以对结果做一些后置处理。通过readResolve我们可以进行单例的保护性恢复,直接返回单例实例。

    java11中的不可变集合序列化和反序列化就是通过这两个方法实现的。

    序列化Id

    /** use serialVersionUID from JDK 1.0.2 for interoperability */
        private static final long serialVersionUID = -6849794470754667710L;
    

    必须为static且final的字段。

    如果我们没有声明这个字段,序列化时会根据class,method信息综合计算出一个。

  • 相关阅读:
    MySQL日志设置及查看方法
    delphi 程序强制结束自身(两种方法都暴力)
    BAT-把当前用户以管理员权限运行(用户帐户控制:用于内置管理员帐户的管理员批准模式)
    SetWindowLong
    修复VirtualBox "This kernel requires the following features not present on the CPU: pae Unable to boot – please use a kernel appropriate for your CPU"(安装深度Linux的时候就需要)
    国家网络与信息安全中心紧急通报:请升级安装补丁!
    Web Api 2(Cors)Ajax跨域访问
    理解依赖注入
    www.centos.org
    VMware Player安装centos
  • 原文地址:https://www.cnblogs.com/strongmore/p/13376299.html
Copyright © 2011-2022 走看看