Java基础知识系列—序列化

发表于:2018-5-07 10:01

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:codersm    来源:51testing软件测试网采编

  Java基础知识系列—序列化
  序列化是将对象的状态转换为字节流;反序列化恰恰相反。换言之,序列化是将Java对象转换为字节的静态流(序列,然后可以将其保存到数据库或通过网络传输。
  序列化和反序列化
  序列化过程是独立于实例的,即对象可以在一个平台上序列化并在另一个平台上反序列化。有资格序列化的类需要实现一个特殊的标记接口Serializable。
  ObjectInputStream和ObjectOutputStream都是分别扩展java.io.InputStream和java.io.OutputStream的高级类。 ObjectOutputStream可以将对象的基本类型和对象作为字节流写入OutputStream。随后可以使用ObjectInputStream读取这些流。
  ObjectOutputStream提供了writeObject方法可以将可序列化的对象转换为字节的序列(流),同样地,ObjectInputStream提供了readObject方法可以将字节流转换为Java对象。
      请注意,静态字段属于类(与对象相对)并不会被序列化;另外,也可以使用关键字transient忽略字段序列化。
  用一个Person类来说明序列化,其源码如下:
  public class Person implements Serializable {
      private static final long serialVersionUID = 1L;
      static String country = "ITALY";
      private int age;
      private String name;
      transient int height;
   
      // getters and setters
  }
  @Test
  public void whenSerializingAndDeserializing_ThenObjectIsTheSame() () 
    throws IOException, ClassNotFoundException { 
      Person person = new Person();
      person.setAge(20);
      person.setName("Joe");
       
      FileOutputStream fileOutputStream
        = new FileOutputStream("yourfile.txt");
      ObjectOutputStream objectOutputStream 
        = new ObjectOutputStream(fileOutputStream);
      objectOutputStream.writeObject(person);
      objectOutputStream.flush();
      objectOutputStream.close();
       
      FileInputStream fileInputStream
        = new FileInputStream("yourfile.txt");
      ObjectInputStream objectInputStream
        = new ObjectInputStream(fileInputStream);
      Person p2 = (Person) objectInputStream.readObject();
      objectInputStream.close(); 
    
      assertTrue(p2.getAge() == p.getAge());
      assertTrue(p2.getName().equals(p.getName()));
  }
  序列化注意事项
  继承与构成
  当一个类实现了java.io.Serializable接口时,它的所有子类也是可序列化的。相反,当一个对象具有对另一个对象的引用时,这些对象必须单独实现Serializable接口,否则会引发NotSerializableException异常。
  public class Person implements Serializable {
      private int age;
      private String name;
      private Address country; // must be serializable too
  }
  Serial Version UID
  JVM将版本号与每个可序列化的类相关联。它用于验证保存和加载的对象具有相同的属性,因此在序列化时兼容。
  如果可序列化的类没有声明serialVersionUID,则JVM将在运行时自动生成一个。但是,强烈建议每个类声明其serialVersionUID,因为生成的是依赖于编译器的,因此可能会导致意外的InvalidClassExceptions。
  Java中的自定义序列化
  Java指定了可以序列化对象的默认方式。Java类可以覆盖此默认行为。在尝试序列化具有一些不可序列化属性的对象时,自定义序列化特别有用。
  1、在类中提供writeObject和readObject方法
  可以通过在类中提供两个我们想要序列化的方法来完成:
  private void writeObject(ObjectOutputStream out) throws IOException;
  private void readObject(ObjectInputStream in) throws 
                                  IOException, ClassNotFoundException;
  通过这些方法,我们可以将那些不可序列化的属性序列化为可以序列化的其他形式:
  public class Employee implements Serializable {
      private static final long serialVersionUID = 1L;
      private transient Address address;
      private Person person;
   
      // setters and getters
   
      private void writeObject(ObjectOutputStream oos) 
        throws IOException {
          oos.defaultWriteObject();
          oos.writeObject(address.getHouseNumber());
      }
   
      private void readObject(ObjectInputStream ois) 
        throws ClassNotFoundException, IOException {
          ois.defaultReadObject();
          Integer houseNumber = (Integer) ois.readObject();
          Address a = new Address();
          a.setHouseNumber(houseNumber);
          this.setAddress(a);
      }
  }
  public class Address {
      private int houseNumber;
   
      // setters and getters
  }
  测试自定义序列化:
  @Test
  public void whenCustomSerializingAndDeserializing_ThenObjectIsTheSame() 
    throws IOException, ClassNotFoundException {
      Person p = new Person();
      p.setAge(20);
      p.setName("Joe");
   
      Address a = new Address();
      a.setHouseNumber(1);
   
      Employee e = new Employee();
      e.setPerson(p);
      e.setAddress(a);
   
      FileOutputStream fileOutputStream
        = new FileOutputStream("yourfile2.txt");
      ObjectOutputStream objectOutputStream 
        = new ObjectOutputStream(fileOutputStream);
      objectOutputStream.writeObject(e);
      objectOutputStream.flush();
      objectOutputStream.close();
   
      FileInputStream fileInputStream 
        = new FileInputStream("yourfile2.txt");
      ObjectInputStream objectInputStream 
        = new ObjectInputStream(fileInputStream);
      Employee e2 = (Employee) objectInputStream.readObject();
      objectInputStream.close();
   
      assertTrue(
        e2.getPerson().getAge() == e.getPerson().getAge());
      assertTrue(
        e2.getAddress().getHouseNumber() == e.getAddress().getHouseNumber());
  }
  2、实现Externalizable接口
  除了Serializable 之外,java中还提供了另一个序列化接口Externalizable。
  Externalizable继承了Serializable,该接口中定义了两个抽象方法:writeExternal()与readExternal()。当使用Externalizable接口来进行序列化与反序列化的时候需要开发人员重写writeExternal()与readExternal()方法。
  public class Person implements Externalizable {
      private int age;
      private String name;
      public int getAge() {
          return age;
      }
      public void setAge(int age) {
          this.age = age;
      }
      public String getName() {
          return name;
      }
      public void setName(String name) {
          this.name = name;
      }
      public Address getCountry() {
          return country;
      }
      public void setCountry(Address country) {
          this.country = country;
      }
      @Override
      public void writeExternal(ObjectOutput out) throws IOException {
          out.writeInt(age);
          out.writeObject(name);
      }
      @Override
      public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
         age =  in.readInt();
         name = (String) in.readObject();
      }
  }
  测试代码如下:
   ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("yourfile2.txt"));
          Person person = new Person();
          person.setAge(10);
          person.setName("zhang san");
          oos.writeObject(person);
          //Read Obj from file
          File file = new File("yourfile2.txt");
          ObjectInputStream ois =  new ObjectInputStream(new FileInputStream(file));
          Person newInstance = (Person) ois.readObject();
      值得注意:在使用Externalizable进行序列化的时候,在读取对象时,会调用被序列化类的无参构造器去创建一个新的对象,然后再将被保存对象的字段的值分别填充到新对象中。所以,实现Externalizable接口的类必须要提供一个public的无参的构造器。




上文内容不用于商业目的,如涉及知识产权问题,请权利人联系博为峰小编(021-64471599-8017),我们将立即处理。
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号