对象序列化的目标是将对象保存到磁盘中,或容许网络中直接传输对象。对象序列化机制容许把内存中的对象转换成平台无关的二进制流,从而容许这种二进制流持久地保存在磁盘上,经过网络将这种二进制流传输到另外一个网络节点,其它程序一旦得到这种二进制流,均可以将这种二进制流恢复为原来的Java对象。java
对象的序列化指将一个Java对象写入IO流中,与此对应,对象的反序列化指从IO流中恢复该Java对象。
若是须要让对象支持序列化机制,则必须让它的类是可序列化的。为了让某个类是可序列化的,该类必须实现以下两个接口之一。算法
一旦某个类实现了Serializable接口,该类的对象就是可序列化的,程序能够经过以下两个步骤来序列化该对象。网络
public class Person implements Serializable { private static final long serialVersionUID = 3232557182439996130L; private String name; private int age; public Person(String name, int age) { System.out.println("有参构造器"); this.name=name; this.age=age; } public static void main(String[] args) { try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("obj.txt"))) { Person per = new Person("sunqiang", 30); oos.writeObject(per); } catch (IOException ex) { ex.printStackTrace(); } } }
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("teacher.txt"))) { Person person = new Person("sun", 500); Teacher t1 = new Teacher("li", person); oos.writeObject(t1); t1.setName("改变姓名"); oos.writeObject(t1); } catch (IOException ex) { ex.printStackTrace(); }
两次将t1对象序列化,但在第二次序列化前改变了t1变量的值,由于t1已经被序列化过一次,再次调用writeObject()不会将该对象写入,下面读取对象,能够看到两次读取的对象name属性相同。ide
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("teacher.txt"))) { Teacher t1 = (Teacher) ois.readObject(); Teacher t2 = (Teacher) ois.readObject(); System.out.println("t1:name:" + t1.getName()); System.out.println("t2:name:" + t2.getName()); } catch (IOException ex) { ex.printStackTrace(); } catch (ClassNotFoundException cnf) { cnf.printStackTrace(); }
经过在实例变量前使用transient关键字,能够指定Java序列化时无须理会该实例变量。this
使用transient修饰的变量被隔离在序列化机制以外,这样致使在反序列化恢复Java对象是没法获取该实例变量的值。Java还提供了一种自定义序列化机制,经过这种机制可让程序控制如何序列化各实例变量。
有一个Person类:code
public class Person implements Serializable { private static final long serialVersionUID = -4271096500079406727L; private String name; private int age; //省略getter,setter方法 }
为序列化类提供以下方法:对象
private void writeObject(java.io.ObjectOutputStream out) throws IOException private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException; private void readObjectNoData() throws ObjectStreamException;
下面是一个例子接口
private void writeObject(ObjectOutputStream out) throws IOException { out.writeObject(new StringBuffer(name).reverse()); out.writeInt(age); } private void readObject(ObjectInputStream in) throws IOException,ClassNotFoundException { this.name=((StringBuffer)in.readObject()).reverse().toString(); this.age=in.readInt(); }
为序列化类提供以下方法:内存
ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException;
writeReplace由序列化机制调用,只要该方法存在,Java在序列化机制运行保证在序列化某个对象以前,先调用该对象的writeReplace()方法。get
private Object writeReplace(){ ArrayList<Object> list=new ArrayList<>(); list.add(name); list.add(age); return list; } public static void main(String[] args) { try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("replace.txt")); ObjectInputStream ois=new ObjectInputStream(new FileInputStream("replace.txt"))){ Person person=new Person("sun",550); oos.writeObject(person); ArrayList list=(ArrayList)ois.readObject(); System.out.println(list); } catch (Exception ex) { ex.printStackTrace(); } }
为序列化类提供以下方法:
ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;
要实现该接口里的两个方法:
@Override public void writeExternal(ObjectOutput out) throws IOException { out.writeObject(new StringBuffer(name).reverse()); out.writeInt(age); } @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { this.name = ((StringBuffer) in.readObject()).reverse().toString(); this.age = in.readInt(); }
须要指出的是当使用Externalizable机制反序列化时,程序会先使用public的无参构造器建立实例,而后才执行readExternal()方法进行反序列化,所以实现Externalizable的序列化类必须提供public的无参构造器。
关于对象序列化,还要注意一下几点: