Java序列化(含transient)
什么是序列化?
我们创建的对象只有在Java虚拟机保持运行时,才会存在于内存中。如果想要超出Java虚拟机的生命周期,就可以将对象序列化,将对象状态转换为字节序列,写入文件(或socket传输),后面使用时再读入文件,读入原始字节并创建一个完全相同的对象。
PS:只有对象的状态会被序列化,类本身或方法都不会被序列化。
三种序列化方式
1、默认机制
需要序列化的对象,实现java.io.Serializable接口即可。
例子:
import java.io.Serializable; import java.util.Date; import java.util.Calendar; public class PersistentTime implements Serializable { private Date time; public PersistentTime() { time = Calendar.getInstance().getTime(); } public Date getTime() { return time; } } import java.io.ObjectOutputStream; import java.io.FileOutputStream; import java.io.IOException; public class FlattenTime { public static void main(String[] args) { String filename = "time.ser"; if (args.length > 0) { filename = args[0]; } PersistentTime time = new PersistentTime(); FileOutputStream fos = null; ObjectOutputStream out = null; try { fos = new FileOutputStream(filename); out = new ObjectOutputStream(fos); out.writeObject(time); out.close(); } catch (IOException ex) { ex.printStackTrace(); } } } import java.io.ObjectInputStream; import java.io.FileInputStream; import java.io.IOException; import java.util.Calendar; public class InflateTime { public static void main(String[] args) { String filename = "time.ser"; if (args.length > 0) { filename = args[0]; } PersistentTime time = null; FileInputStream fis = null; ObjectInputStream in = null; try { fis = new FileInputStream(filename); in = new ObjectInputStream(fis); time = (PersistentTime) in.readObject(); in.close(); } catch (IOException ex) { ex.printStackTrace(); } catch (ClassNotFoundException ex) { ex.printStackTrace(); } // print out restored time System.out.println("Flattened time: " + time.getTime()); System.out.println(); // print out the current time System.out.println("Current time: " + Calendar.getInstance().getTime()); } }
注意:
1、持久化的对象必现实现Serializable接口或继承的父类实现了。
2、不能(例如系统级别的类Thread、OutPutStream)或不想被序列化的字段必需加transient声明。
transient是字段的修饰符,当一个字段声明为transient时,它就不能被序列化。
transient不能声明方法、类、接口,它们不需要被序列化。
2、自定义默认协议
由于类的构造函数只要在对象创建时候才调用,而反序列化的对象是用readObject创建的,不会再去调构造函数。如果构造函数里有执行某些行为,那么反序列化回来的对象就会丢失这些行为。
解决方法:增加两个方法,在保证序列化的默认行为后,增加自己要的行为。
private void writeObject(ObjectOutputStream out) throws IOException; private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException;
例子:
import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; public class PersistentAnimation implements Serializable, Runnable { private static final long serialVersionUID = 2850527457134768151L; transient private Thread animator; private int animationSpeed; public PersistentAnimation(int animationSpeed) { this.animationSpeed = animationSpeed; startAnimation(); } public void run() { while (true) { // do animation here } } private void writeObject(ObjectOutputStream out) throws IOException { out.defaultWriteObject(); } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { // our "pseudo-constructor" in.defaultReadObject(); // now we are a "live" object again, so let's run rebuild and start startAnimation(); } private void startAnimation() { animator = new Thread(this); animator.start(); } }
同理,如果父类已经实现Serializable接口,子类又不想被序列化,就可以添加这两个内容为空的方法。
3、自定义协议
实现Externalizable接口,复写writeExternal、readExternal两个方法,自己定义序列化协议(例如pdf的存取)。
public void writeExternal(ObjectOutput out) throws IOException; public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;
序列化的陷阱
1、缓存流中的对象,再修改对象状态再写入是无效的。
ObjectOutputStream out = new ObjectOutputStream(...); MyObject obj = new MyObject(); // must be Serializable obj.setState(100); out.writeObject(obj); // saves object with state = 100 obj.setState(200); out.writeObject(obj); // does not save new object state
2、版本控制
当你序列化一个对象存储到文件后,修改类的定义(例如增加个字段),当你反序列化时,会报一个java.io.InvalidClassException的异常。这是因为所有可以持续化的类都会有一个唯一的标识符,如果类的标识符跟反序列化对象的标识符不一样时,就会报这个异常。
解决方法:增加一个serialVersionUID字段作为类的标识符,只要标识符不变,就可反序列化。
添加方法:在eclipse中,鼠标悬浮到类名上,就可以看到“添加已生成的串行版本标识”,点击即可添加serialVersionUID。
3、性能问题
序列化对象写入文件的性能会比自己手动写入文件低。
参考文献
http://www.oracle.com/technetwork/articles/java/javaserial-1536170.html