本章目标
掌握对象序列化的作用
掌握Serializable接口的作用
可以使用ObjectOutputStream进行对象序列化操作
可以使用ObjectInputStream进行对象的反序列化操作
掌握Externalizable接口的作用及与Serializable接口的实现区别
掌握transient关键字的作用
可以序列化一组对象
对象序列化
对象序列化,就是把一个对象变为二进制的数据流的一种方法,通过对象序列化可以方便的实现对象的传输或存储。
如果一个类的对象想被序列化,则对象所在的类必须实现java.io.Serializable接口。此接口定义如下:
——public interface Serializable{}
定义可序列化的类
import java.io.Serializable; public class Person implements Serializable {//此类的对象可以被序列化 private String name;//声明 name 属性 private int age;//声明 age 属性 public Person(String name, int age) {//通过构造方法设置属性内容 this.name = name; this.age = age; } public String toString(){//覆写 toString() 方法 return "姓名:"+this.name+"; 年龄:"+this.age; } }
对象的序列化和反序列化
要想完成对象的输入或输出,还必须依靠对象输出流(ObjectOutputStream)和对象输入流(ObjectInputStream)
使用对象输出流输出序列化对象的步骤,有时也称为序列化,而使用对象输入流读入对象的过程,有时也称为反序列化
serialVersionUID
在对象进行序列化或反序列化操作的时候,要考虑JDK版本的问题,如果序列化的JDK版本和反序列化的JDK版本不统一则就有可能造成异常。所以在序列化操作中引入了一个serialVersionUID的常量,可以通过此常量来验证版本的一致性,在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体(类)的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。
——private static final long serialVersionUID = 1L;
对象输出流:ObjectOutputStream
一个对象如果要想进行输出,则必须使用ObjectOutputStream类,此类定义如下:
——public class ObjectOutputStreamextends OutputStream implements ObjectOutput, ObjectStreamConstants
此类的常用方法如下
此类的使用形式与PrintStream非常的相似,在实例化时也需要传入一个OutputStream的子类对象,之后根据传入的OutputStream子类的对象不同,输出的位置也不同。
将Person类的对象保存在文件之中
public class SerDemo01 { public static void main(String[] args) throws Exception{ File f = new File("D:"+File.separator+"test.txt"); ObjectOutputStream oos = null; OutputStream out = new FileOutputStream(f);//文件输出流 oos = new ObjectOutputStream(out);//为对象输出流实例化 oos.writeObject(new Person("张三", 30));//保存对象到文件 oos.close();//关闭输出 } }
对象输入流:ObjectInputStream
使用ObjectInputStream可以直接把被序列化好的对象反序列化回来。ObjectInputStream的定义如下:
——public class ObjectInputStream extends InputStream implements ObjectInput, ObjectStreamConstants
此类的常用方法如下:
此类也是InputStream的子类,与PrintStream类的使用类似,此类同样需要接收InputStream类的实例才可以实例化。
从文件中将Person对象反序列化
import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.io.ObjectInputStream; public class SerDemo02 { public static void main(String[] args) throws Exception{ File f = new File("D:"+File.separator+"test.txt"); ObjectInputStream ois = null; InputStream input = new FileInputStream(f);//文件输入流 ois = new ObjectInputStream(input);//为对象输入流实例化 Object obj = ois.readObject();//读取对象 ois.close();//关闭输出 System.out.println(obj); } /* 结果: * 姓名:张三; 年龄:30 * */ }
Externalizable接口
被Serializable接口声明的类其对象的内容都将被序列化,如果现在用户希望可以自己指定序列化的内容,则可以让一个类实现Externalizable接口,此接口定义如下:
——public interface Externalizable extends Serializable {
public void writeExternal(ObjectOutput out) throws IOException ;
public void readExternal(ObjectInput in) throws IOException,ClassNotFoundException ;}
修改Person类并实现Externalizable接口
import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; public class Person implements Externalizable {//此类的对象可以被序列化 private String name;//声明 name 属性 private int age;//声明 age 属性 public Person(){}//必须定义无参构造 public Person(String name, int age) {//通过构造方法设置属性内容 this.name = name; this.age = age; } public String toString(){//覆写 toString() 方法 return "姓名:"+this.name+"; 年龄:"+this.age; } //覆写此方法,根据需要读取内容,反序列化时使用 public void readExternal(ObjectInput in) throws IOException,ClassNotFoundException{ this.name = (String)in.readObject();//读取姓名属性 this.age = in.readInt();//读取年龄 } //覆写此方法,根据需要可以保存属性或具体内容,序列化时使用 public void writeExternal(ObjectOutput out) throws IOException{ out.writeObject(this.name);//保存姓名属性 out.writeInt(this.age);//保存年龄属性 } }
序列化和反序列化Person对象
import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.OutputStream; public class SerDemo03 { public static void main(String[] args) throws Exception { ser();//序列化 dser();//反序列化 } public static void ser() throws Exception{//序列化操作 File f = new File("D:"+File.separator+"test.txt"); ObjectOutputStream oos = null; OutputStream out = new FileOutputStream(f);//文件输出流 oos = new ObjectOutputStream(out);//为对象输出流实例化 oos.writeObject(new Person("李四", 31));//保存对象到文件 oos.close();//关闭输出 } public static void dser() throws Exception{//反序列化操作 File f = new File("D:"+File.separator+"test.txt"); ObjectInputStream ois = null; InputStream input = new FileInputStream(f);//文件输入流 ois = new ObjectInputStream(input);//为对象输入流实例化 Object obj = ois.readObject();//读取对象 ois.close();//关闭输出 System.out.println(obj); } /* 结果: * 姓名:李四; 年龄:31 * */ }
Externalizable接口与Serializable接口实现序列化的区别
transient关键字
当使用Serializable接口实现序列化操作时,如果一个对象中的某个属性不希望被序列化的话,则可以使用transient关键字进行声明
Person中的name属性不希望被序列化
import java.io.Serializable; public class Person implements Serializable {//此类的对象可以被序列化 private transient String name;//此属性将不被序列化 private int age;//此属性将被序列化 public Person(String name, int age) { this.name = name; this.age = age; } public String toString(){//覆写 toString() 方法 return "姓名:"+this.name+"; 年龄:"+this.age; } }
public class SerDemo04 { public static void main(String[] args) throws Exception { ser();//序列化 dser();//反序列化 } public static void ser() throws Exception{//序列化操作 File f = new File("D:"+File.separator+"test.txt"); ObjectOutputStream oos = null; OutputStream out = new FileOutputStream(f);//文件输出流 oos = new ObjectOutputStream(out);//为对象输出流实例化 oos.writeObject(new Person("李四", 31));//保存对象到文件 oos.close();//关闭输出 } public static void dser() throws Exception{//反序列化操作 File f = new File("D:"+File.separator+"test.txt"); ObjectInputStream ois = null; InputStream input = new FileInputStream(f);//文件输入流 ois = new ObjectInputStream(input);//为对象输入流实例化 Object obj = ois.readObject();//读取对象 ois.close();//关闭输出 System.out.println(obj); } /* 结果: * 姓名:李四; 年龄:31 * */ }
序列化一组对象
对象输出时只提供了一个对象的输出操作(writeObject(Object obj)),并没有提供多个对象的输出,所以如果现在要同时序列化多个对象的时候就可以使用对象数组进行操作,因为数组属于引用数据类型,所以可以直接使用Object类型进行接收
序列化多个Person对象
import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.OutputStream; public class SerDemo05 { public static void main(String[] args) throws Exception { //定义对象数组 Person per[] = {new Person("张三", 30),new Person("李四", 31),new Person("王五", 32)}; ser(per);//序列化对象数组 Object o[] = dser();//读取被序列化的对象数组 for(int i=0; i<o.length; i++){ Person p = (Person)o[i]; System.out.println(p); } } public static void ser(Object obj[]) throws Exception{ File f = new File("D:"+File.separator+"test.txt"); ObjectOutputStream oos = null; OutputStream out = new FileOutputStream(f);//文件输出流 oos = new ObjectOutputStream(out);//为对象输出流实例化 oos.writeObject(obj);//保存对象到文件 oos.close();//关闭输出 } public static Object[] dser() throws Exception{//反序列化操作 File f = new File("D:"+File.separator+"test.txt"); ObjectInputStream ois = null; InputStream input = new FileInputStream(f);//文件输入流 ois = new ObjectInputStream(input);//为对象输入流实例化 Object obj[] = (Object[])ois.readObject();//读取对象 ois.close();//关闭输出 return obj; } /* 结果: * 姓名:张三; 年龄:30 * 姓名:李四; 年龄:31 * 姓名:王五; 年龄:32 * */ }