Java 序列化与反序列化

序列化

问题

  • 什么是序列化
  • 什么是反序列化
  • 序列化的方法
  • 反序列化的方法
  • transient关键字的含义
  • Serializable 接口
  • static变量能否被序列化
  • 序列化ID
  • 单例类的序列化
  • Externalizable
  • 序列化与对象克隆

解答

  • 什么是序列化
    序列化是将内存中的对象字节化到文件或者或者网络流中,用户持久化对象或者网络传输对象。由于是序列化为字节,所以与InputStream和OutputStream有关
  • 什么是反序列化
    反序列化是将文件或者数据库或者网络流中的字节解析成为Java对象。
  • 序列化方法
    使用ObjectOutputStream继承了OutputStream,并实现了DataOutput接口。
  • 反序列化方法
    使用ObjectInputStream,继承了InputStream,并实现了DataInput接口
  • transient关键字
    如果那个字段不需要序列化,那么使用transient后不会将该字段的值序列化,反序列化后是null
  • Serializable接口
    该接口是一个标志接口,任何需要序列化的类必须要实现该接口表示序列化
  • static变量能否被序列化
    不能被序列化。static属于类成员,序列化针对的是对象
  • 序列化ID
    序列化ID代表这次序列化的对象的版本号,反序列化后会检查该id是否一致,如果不一致,那么序列化不一样。如果没有显示定义,那么序列化的时候默认取根据该类的.class文件计算一个值。反序列化的时候,如果对象修改了(任何修改,比如多一个空格),那么根据该类编译的class文件计算的序列化id也会不一样,导致反序列化失败。
    如下:
Exception in thread "main" java.io.InvalidClassException: main.Person; local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 4102796403741744869
	at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:687)
	at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1876)
	at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1745)
	at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2033)
	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1567)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:427)
	at main.Hello.main(Hello.java:58)

最好自己定义序列化的ID

  • 单例的序列化与反序列化
    单例模式下,某个类被反序列化时,通过反射会新建一个对象,导致单例模式被破坏,所以,为了防止这种情况。需要在被序列化中加个readResolve方法。为什么加这个方法?
    因为在ObjectInputStream的readObject方法进行反序列化的时候,会检查该类是否有readResolve方法,如果有,那么会反序列后的对象就是该方法return的Object。
class Apple implements Serializable{
    private static Apple instance = new Apple();
    private Apple(){

    }
    public static  Apple getInstance(){
        return instance;
    }
    public void eat(){
        System.out.println("eat apple");
    }
    private Object readResolve(){
        return instance;
    }
}

ObjectInputStream readObject调用链
readObject--->readObject0--->readOrdinaryObject--->checkResolve

*Externalizable
该接口继承了Serializeble接口

public interface Externalizable extends java.io.Serializable {
   /**
    * The object implements the writeExternal method to save its contents
    * by calling the methods of DataOutput for its primitive values or
    * calling the writeObject method of ObjectOutput for objects, strings,
    * and arrays.
    *
    * @serialData Overriding methods should use this tag to describe
    *             the data layout of this Externalizable object.
    *             List the sequence of element types and, if possible,
    *             relate the element to a public/protected field and/or
    *             method of this Externalizable class.
    *
    * @param out the stream to write the object to
    * @exception IOException Includes any I/O exceptions that may occur
    */
   void writeExternal(ObjectOutput out) throws IOException;

   /**
    * The object implements the readExternal method to restore its
    * contents by calling the methods of DataInput for primitive
    * types and readObject for objects, strings and arrays.  The
    * readExternal method must read the values in the same sequence
    * and with the same types as were written by writeExternal.
    *
    * @param in the stream to read data from in order to restore the object
    * @exception IOException if I/O errors occur
    * @exception ClassNotFoundException If the class for an object being
    *              restored cannot be found.
    */
   void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;
}

如果需要定制序列化和反序列化的逻辑,那么可以实现该接口

package main;


import java.io.*;
import java.nio.charset.Charset;
import java.util.Map;
import java.util.jar.JarInputStream;


class Person implements Externalizable {
   public transient String name;
   public String age;
   public String country = "china";
   public static String sex = "nan";
   public String email;
   public String address;


   public Person() {

   }

   public Person(String name, String age, String country) {
       this.name = name;
       this.age = age;
       this.country = country;
   }

   public String getCountry() {
       return country;
   }

   public void setCountry(String country) {
       this.country = country;
   }

   public Person(String name, String age) {
       this.name = name;
       this.age = age;
   }


   @Override
   public void writeExternal(ObjectOutput out) throws IOException {
       out.writeObject(name);
       out.writeInt(1000);
   }

   @Override
   public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
       this.name = (String) in.readObject();
       this.age = String.valueOf(in.readInt());
   }
}

public class Hello {
   public static void main(String[] args) throws Exception {

       xuliehua();
       fanxuliehua();

   }

   public static void xuliehua() throws IOException, ClassNotFoundException {
       FileOutputStream fos = new FileOutputStream("person.txt");
       ObjectOutputStream out = new ObjectOutputStream(fos);
       Apple apple = Apple.getInstance();
       out.writeObject(new Person("tom", "0", "japan"));
       out.close();

   }

   public static void fanxuliehua() throws IOException, ClassNotFoundException {
       ObjectInputStream in = new ObjectInputStream(new FileInputStream("person.txt"));
       Person str = (Person) in.readObject();
       System.out.println(str.name);
       System.out.println(str.age);
   }
}
  • 序列化与对象克隆
    见另一篇博文对象克隆

你可能感兴趣的:(Java)