深耕静禅_Java_序列化_2020年4月5日23:16:48

  于网络一文中获得此序列化相关内容,近日看博客较多,对版权有颇多看法。若不想让他人学会方可私密化,但也莫去看他人整理文章,若是让各位采鉴,莫怪其如何整理。语言不是尔专利,且仅仅学习整理何有如此之多规则,其实若他人采去方可称为学生,争做尔等桃李暗自窃喜乎。——2020年4月5日

概述

  • 序列化即为将对象信息转为字节的有序的内容,可写入至IO流中用于存储,或进行网络传输等。反序列化即为将IO中对象或文件中对象转换为内存中对象,使其可位于程序中运行。

  • 序列可使对象信息持久存储,脱离程序运行寿命。

  • 所有可于网络中传输对象必可序列化,例如远程方法调用(RMI-remote method invoke)必可序列化。所有需保存至磁盘对象都必可序列化。

  • 序列化需事先实现Serializable接口或者Externalizable接口。

    实现Serializable接口 实现Externalizable接口
    系统自动存储必要的信息 程序员决定存储哪些信息
    Java内建支持,易于实现,只需要实现该接口即可,无需任何代码支持 必须实现接口内的两个方法
    性能略差 性能略好

Serializable应用

常见序列化

  • 实现Serializable接口创建一个可序列化对象:

    public class Person implements Serializable {
      private String name;
      private int age;
    }
  • ObjectOutputStream中的writeObject方法可将序列化后对象输出:

    public class WriteObject {
      public static void main(String[] args) {
          try (
               ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.txt"))) {
              Person person = new Person();
              oos.writeObject(person);
          } catch (Exception e) {
              e.printStackTrace();
          }
      }
    }
  • readObject()方法可将文件中对象信息读取并转换为一对象:

    public class ReadObject {
      public static void main(String[] args) {
          try (
               ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.txt"))) {
              Person brady = (Person) ois.readObject();
              System.out.println(brady);
          } catch (Exception e) {
              e.printStackTrace();
          }
      }
    }
  • 反序列化时,并未调用构造方法。

特殊序列化

  • 若要将一类之对象序列化,其中属性有所约束:必为基本类型或String类型。若其属性存在其他类型,那其属性类型必要亦为可序列类型,否则将出现异常。

  • 将一对象多次序列化至文件,其并不会将这对象的这多个瞬间变成多个对象,即还是一个对象。反序列化时,取出的多个对象瞬间为同一对象。进行序列化存储时顺序就是取出的顺序,其最后写入的瞬间在取出时将成为对象最后定义。

    其是由于其算法原因,每个对象皆有自有编号,故虽多次写入文件,依然为一对象,只是时间不同尔。

    可理解为:同一文件中存在多个时间片中的同一对象,在文件中同一对象可看到在不同时间段时的自己。

  • 若不想序列对象中某一属性,可将其设置为transient,在类型后,属性名前添加此关键字,即可忽略序列化。在取出序列对象后,此属性值将为默认值。

    private transient boolean isMan;
  • 自定义序列化可在序列化与读取时自定操作,主要是重写writeObject与readObject方法实现:

    public class Person implements Serializable {
       private String name;
       private int age;
       //省略构造方法,get及set方法
    private void writeObject(ObjectOutputStream out) throws IOException {
           //将名字反转写入二进制流
           out.writeObject(new StringBuffer(this.name).reverse());
           out.writeInt(age);
       }
    ​
       private void readObject(ObjectInputStream ins) throws IOException,ClassNotFoundException{
           //将读出的字符串反转恢复回来
           this.name = ((StringBuffer)ins.readObject()).reverse().toString();
           this.age = ins.readInt();
       }
    }
  • readObjectNoData方法将初始化反序列化流,可在多线程或其他意外情况而造成序列流出现问题时重新初始化对象。

  • writeReplace和readResolve方法可做更彻底的自定义序列化:writeReplace方法将先于writeObject方法调用,其可替换即将序列化后的对象,下文将Person对象使用writeReplace替换为一集合:

    public class Person implements Serializable {
      private String name;
      private int age;
      //省略构造方法,get及set方法
    private Object writeReplace() throws ObjectStreamException {
          ArrayList<Object> list = new ArrayList<>(2);
          list.add(this.name);
          list.add(this.age);
          return list;
      }
    ​
       public static void main(String[] args) throws Exception {
          try (
              ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.txt"));
              ObjectInputStream ios = new ObjectInputStream(new FileInputStream("person.txt"))) {
              Person person = new Person("ABC", 123);
              oos.writeObject(person);
              ArrayList list = (ArrayList)ios.readObject();
              System.out.println(list);
          }
      }
    }
    //输出结果
    //[ABC, 123]

    readResolve则会重新定义序列化后获得的内容,其位于readeObject后调用,下文将一对象篡改为另一对象:

    public class Person implements Serializable {
        private String name;
        private int age;
        //省略构造方法,get及set方法
         private Object readResolve() throws ObjectStreamException{
            return new ("brady", 23);
        }
        public static void main(String[] args) throws Exception {
            try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.txt"));
                 ObjectInputStream ios = new ObjectInputStream(new FileInputStream("person.txt"))) {
                Person person = new Person("9龙", 23);
                oos.writeObject(person);
                HashMap map = (HashMap)ios.readObject();
                System.out.println(map);
            }
        }
    }
    //输出结果
    //{brady=23}
  • readResolve可保证单例类唯一性,故用于单例模式反序列化。

Externalizable应用

  • Externalizable接口强制实现writeExternal、readExternal方法。

  • Externalizable接口继承Serializable接口。

  • 序列化对象之类需提供公开无参构造函数,因其需反射方可创建对象。

    public class ExPerson implements Externalizable {
    ​
        private String name;
        private int age;
        //注意,必须加上pulic 无参构造器
        public ExPerson() {
        }
    ​
        public ExPerson(String name, int age) {
            this.name = name;
            this.age = age;
        }
    ​
        @Override
        public void writeExternal(ObjectOutput out) throws IOException {
            //将name反转后写入二进制流
            StringBuffer reverse = new StringBuffer(name).reverse();
            System.out.println(reverse.toString());
            out.writeObject(reverse);
            out.writeInt(age);
        }
    ​
        @Override
        public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
            //将读取的字符串反转后赋值给name实例变量
            this.name = ((StringBuffer) in.readObject()).reverse().toString();
            System.out.println(name);
            this.age = in.readInt();
        }
    ​
        public static void main(String[] args) throws IOException, ClassNotFoundException {
            try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ExPerson.txt"));
                 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("ExPerson.txt"))) {
                oos.writeObject(new ExPerson("brady", 23));
                ExPerson ep = (ExPerson) ois.readObject();
                System.out.println(ep);
            }
        }
    }
    //输出结果
    //ydarb
    //brady
    //ExPerson{name='brady', age=23}

serialVersionUID

  • 反序列化中,serialVersionUID值可规定class文件版本号,因序列化后原class文件可能修改,再进行提取时可能出现无法反序列情况。

  • 若反序列化所用serialVersionUID版本与序列化时所用不同,将出现异常,无论实际class文件是否曾有修改。

  • 修改方法或静态属性和不序列化变量(transient修饰的变量),无需修改serialVersionUID版本号。

public class Person implements Serializable {
    //序列化版本号
    private static final long serialVersionUID = 1111013L;
    private String name;
    private int age;
    //省略构造方法及get,set
}

 

猜你喜欢

转载自www.cnblogs.com/agoodjavaboy/p/12639928.html