Java基础知识系列—序列化-博为峰51Code
  • Java基础知识系列—序列化

    发布:51Code 时间: 2018-05-15 14:01

  • Java基础知识系列序列化 序列化是将对象的状态转换为字节流;反序列化恰恰相反。换言之,序列化是将Java对象转换为字节的静态流(序列,然后可以将其保存到数据库或通过网络传输...

  • 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的无参的构造器。

    来自:codersm
    文章来源:51testing软件测试网采编
    博为峰对此不表示赞同或者反对,也不为其提供证明,仅供阅读者交流参考。
    上文内容不用于商业目的,如涉及知识产权问题,请权利人联系博为峰小编caoxiaoyan@51testing.com,我们将立即处理

  • 上一篇:HTTP Header 详解

    下一篇:10大最高效的Java库盘点

网站导航
Copyright(C)51Code软件开发网 2003-2018 , 沪ICP备16012939号-1