Java平台允许我们在内存中创建可复用的Java对象,但一般情况下,只有当JVM处于运行时,这些对象才可能存在,即,这些对象的生命周期不会比JVM的生命周期更长。但在现实应用中,就可能要求在JVM停止运行之后能够保存(持久化)指定的对象,并在将来重新读取被保存的对象。Java对象序列化就能够帮助我们实现该功能。
使用Java对象序列化,在保存对象时,会把其状态保存为一组字节,在未来,再将这些字节组装成对象。必须注意地是,对象序列化保存的是对象的"状态",即它的成员变量。由此可知,对象序列化不会关注类中的静态变量。
下面举一个简单的序列化的例子
import java.io.Serializable; public class Stu implements Serializable,Cloneable{ String name; int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String toString() { return "age="+age+" name="+name; } public Object clone() { Stu stu=null; try{ stu=(Stu)super.clone(); }catch(CloneNotSupportedException ex) { ex.printStackTrace(); } return stu; } }
import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; public class Test { static class League implements Serializable,Cloneable { int level; Stu stu; public int getLevel() { return level; } public void setLevel(int level) { this.level = level; } public Stu getStu() { return stu; } public void setStu(Stu stu) { this.stu = stu; } public String toString() { return "level="+level+" age="+stu.getAge()+" name="+stu.getName(); } public Object clone() { League league=null; try{ league=(League)super.clone(); }catch(CloneNotSupportedException ex) { ex.printStackTrace(); } league.stu=(Stu)stu.clone(); return league; } } public static void main(String[] args) { File file =new File("C:/league.txt"); try{ ObjectOutputStream objectOutputStream=new ObjectOutputStream(new FileOutputStream(file)); Stu stu=new Stu(); stu.setAge(10); stu.setName("ZHANG"); League league=new League(); league.setLevel(2); league.setStu(stu); objectOutputStream.writeObject(league); objectOutputStream.close(); ObjectInputStream oInputStream=new ObjectInputStream(new FileInputStream(file)); League lea=(League)oInputStream.readObject(); oInputStream.close(); System.out.println(lea.toString()); }catch(IOException ex) { ex.printStackTrace(); }catch (ClassNotFoundException ex) { // TODO: handle exception ex.printStackTrace(); } } }
输出:
level=2 age=10 name=ZHANG
我们可以看到,读取到的对象与保存的对象状态一样。这里有几点需要说明一下:
1、基本类型 的数据可以直接序列化
2、对象要被序列化,它的类必须要实现Serializable接口;如果一个类中有引用类型的实例变量,这个引用类型也要实现Serializable接口。比如上面 的例子中,League类中有一个Stu类型 的实例就是,要想让League的对象成功序列化,那么Stu也必须要实现Serializable接口。
3、我们看这个语句:
ObjectOutputStreamout = newObjectOutputStream(new FileOutputStream(file));
我们知道 FileOutputStream类有一个带有两个参数的重载Constructor——FileOutputStream(String,boolean),其第二个参数如果为true且String代表的文件存在,那么将把新的内容写到原来文件的末尾而非重写这个文件,这里我们不能用这个版本的构造函数,也就是说我们必须重写这个文件,否则在读取这个文件反序列化的过程中就会抛出异常,导致只有我们第一次写到这个文件中的对象可以被反序列化,之后程序就会出错。
下面的问题是如果 我们上面 用到的Stu类没有实现Serializable接口,但是我们还想序列化League类的对象 ,怎么办。
Java为我们提供了transient这个关键字。如果一个变量被声明成transient,那么 在序列化的过程 中,这个变量是会被无视的,即这个变量无法被序列化。