Java平台允许我们在内存中创建可复用的Java对象,但一般情况下,只有当JVM处于运行时,这些对象才可能存在,即,这些
对象的生命周期不会比JVM的生命周期更长。但在现实应用中,就可能要求在JVM停止运行之后能够保存(持久化)指定的对象,并在
将来重新读取被保存的对象。Java对象序列化就能够帮助我们实现该功能。
如果某个类能够被序列化,其子类也可以被序列化。声明为static和transient类型的成员数据不能被序列化。因为static代表类的状态, transient代表对象的临时数据。
二、---Serializable---
下面是一个默认序列化的例子,是通过文件流的方式来将对象进行序列化的。
默认的序列化机制,是对对象的所有成员变量(静态变量不会被序列化,因为它不属于某个对象,是所有对象共享的)进行序列化,而
且,假如,成员变量中有对象引用,也会对这个对象引用指向的对象实例进行序列化,如果这个对象实例也包含有其它对象引用,
也会把那些对象进行序列化,以此类推。因此,如果是一个比较复杂的对象实例,比如集合类对象,集合元素也是集合类对象,那
么其序列化过程会比较复杂,开销较大。
import java.io.Serializable; public class Student implements Serializable { int id;// 学号 String name;// 姓名 int age;// 年龄 String password; // 密码 public Student(int id, String name, int age, String password) { this.id = id; this.name = name; this.age = age; this.password = password; } }如下是对Student对象的序列化以及输出结果:
import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; public class ObjectSer { public static void main(String args[]) throws IOException, ClassNotFoundException { Student stu = new Student(981036, "LiuMing", 18, "123456"); FileOutputStream fo = new FileOutputStream("data.ser"); ObjectOutputStream so = new ObjectOutputStream(fo); try { so.writeObject(stu); so.close(); } catch (IOException e) { System.out.println(e); } stu = null; FileInputStream fi = new FileInputStream("data.ser"); ObjectInputStream si = new ObjectInputStream(fi); try { stu = (Student) si.readObject(); si.close(); } catch (IOException e) { System.out.println(e); } System.out.println("Student Info:"); System.out.println("ID:" + stu.id); System.out.println("Name:" + stu.name); System.out.println("Age:" + stu.age); System.out.println("Passw:" + stu.password); } } 运行结果如下: Student Info: ID:981036 Name:LiuMing Age:18 Passw:123456
从上面的代码,我们可以看到对Student对象实例序列化后保存到data.ser中,然后读取该文件获取到原对象的信息。
串行化可能涉及将对象存放到 磁盘上或在网络上发达数据,这时候就会产生安全问题。因为数据位于Java运行环境之外,不在Java安全机制的控制之中。对于这些需要保密的字段,不应保存在永久介质中 ,或者不应简单地不加处理地保存下来 ,为了保证安全性。应该在这些字段前加上transient关键字。
在有些情况下,是不希望对象的所有成员变量都进行序列化,比如Student中的passwd
字段,这个是敏感数据,不希望它被序列
化,那么就可以使用transient
关键字。transient
,顾名思义,非持久化的。使用transient
关键字修饰成员变量,能够使它在序列化的
过程中被忽略。
可以使用writeObject
与readObject
方法来实现自定义序列化。在通过ObjectOutputStream
的writeObject
方法写入对象的时候,如果这个对象
的类中定义了writeObject
方法,就会调用该方法,并把当前 ObjectOutputStream
对象作为参数传递进去。writeObject
方法中一般会包含自
定义的序列化逻辑,比如在写入之前修改域的值,或是写入额外的数据等。
如下是自定义序列化的例子,注意是private类型,如下:
transient private String passwd; private void writeObject(ObjectOutputStream out) throws IOException { out.defaultWriteObject(); out.writeObject("new_passwd"); }通过上面的方式就可以实现密码的改变密码为new_passwd,其中defaultWriteObject()是默认的序列化操作,所以我们在自定序列
之前首先要先默认序列化的操作。
Serializable
接口来实现序列化的,Externalizable
接口继承于Serializable
,通过实现Externalizable
接口也能
实现序列化,不同的是,序列化操作的细节需要自己实现,而且,必须提供public
的无参构造函数
package ThreeWeek; import java.io.Externalizable; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectInputStream; import java.io.ObjectOutput; import java.io.ObjectOutputStream; public class Test { public static void main(String[] args) { File file = new File("user.out"); User user = new User("Jack", "123456", 20); try { ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(file)); out.writeObject(user); out.close(); ObjectInputStream in = new ObjectInputStream(new FileInputStream(file)); Object object = in.readObject(); System.out.println(object); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } } class User implements Externalizable { private String name; private String passwd; private int age; public User(String name, String passwd, int age) { this.name = name; this.passwd = passwd; this.age = age; } public User() { System.out.println("no arguments constructor"); } public String toString() { return "[" + name + ", " + passwd + ", " + age + "]"; } @Override public void writeExternal(ObjectOutput out) throws IOException { out.writeObject(name); out.writeObject(passwd); out.writeInt(age); } @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { name = (String) in.readObject(); passwd = (String) in.readObject(); age = in.readInt(); } }通过上面的代码我们可以看出,在Externalizable中具体的写入和读出的方法需要自己来实现。
采用这种方法实现序列化,transient
是不起作用的,如果你不想序列化某个成员变量,只要在readExternal
和writeExternal
中不对该变量进行相应操作就可以了。