标签:
一个对象产生之后实际上就在内存中开辟了一个存储空间,方便存储信息。
对象的序列化就是将一个对象变成二进制的数据流的一种方法,通过对象的序列化可以方便的实现对象的存储和传输。如果一个类的对象需要被序列化,则该类必须实现Serializable接口,该接口的定义如下:
1 public interface Serializable { 2 3 }
Serializable接口中没有任何方法,只表示该类有这样具备这样一种能力,属于标识接口。下面的一个Student因为实现了Serializable接口,所以它就是可序列化的了:
1 package io.Serializable; 2 3 import java.io.Serializable; 4 5 public class Student implements Serializable { 6 private String name; 7 private int age; 8 9 public Student(String name, int age) { 10 super(); 11 this.name = name; 12 this.age = age; 13 } 14 15 public String getName() { 16 return name; 17 } 18 19 public void setName(String name) { 20 this.name = name; 21 } 22 23 public int getAge() { 24 return age; 25 } 26 27 public void setAge(int age) { 28 this.age = age; 29 } 30 31 @Override 32 public String toString() { 33 return "Student [name=" + name + ", age=" + age + "]"; 34 } 35 36 }
以上的代码仅仅是实现了Serializable接口,其他部分并没有任何改变,以上的类产生的对象就是可序列化(二进制比特流)的了。
ObjectInputStream和ObjectOutputStream
在对对象进行序列化和反序列化的时候要考虑到JDK版本的问题,如果序列化的JDK版本和反序列化的JDK版本不统一就会抛出异常。所以在对象序列化的操作中引入了一个serialVersionUID的常量。可以通过此常量验证版本的一致性。在进行反序列化的操作时,JVM会把传来的字节流中的serialVersionUID与本地响应实体(类)的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化的操作,否则就会抛出序列化版本不一致的异常。
如果使用Eclipse进行编写,如果没有指定serialVersionUID则会出现一些警告信息,如图:
按照Eclipse的提示,我们加上这个常量,这样这个类的设计就完成了。
1 package io.Serializable; 2 3 import java.io.File; 4 import java.io.FileInputStream; 5 import java.io.FileNotFoundException; 6 import java.io.FileOutputStream; 7 import java.io.IOException; 8 import java.io.ObjectInputStream; 9 import java.io.ObjectOutputStream; 10 11 public class SerializableDemo { 12 13 public static void main(String[] args) { 14 15 File file = new File("E:" + File.separator + "tmp" + File.separator 16 + "stu.obj"); // 保存路径 17 try { 18 ObjectOutputStream oos = new ObjectOutputStream( 19 new FileOutputStream(file)); 20 21 Student[] students = { new Student("张三", 12), 22 new Student("李四", 15), new Student("王五", 18) }; 23 oos.writeObject(students); // 直接写入一个对象数组,因为数组也是对象 24 oos.close(); 25 26 ObjectInputStream ois = new ObjectInputStream(new FileInputStream( 27 file)); 28 Student[] stus = (Student[]) ois.readObject(); 29 ois.close(); 30 31 for (Student student : stus) { 32 System.out.println(student); 33 } 34 35 } catch (FileNotFoundException e) { 36 e.printStackTrace(); 37 } catch (IOException e) { 38 e.printStackTrace(); 39 } catch (ClassNotFoundException e) { 40 e.printStackTrace(); 41 } 42 43 } 44 45 }
运行结果:
被Serializable接口声明的类的对象的内容都能够被序列化,如果现在用户希望可以自己制定序列化的内容,则可以让一个类实现Externalizable接口,该接口的定义如下:
1 public interface Externalizable extends Serializable { 2 public void readExternal(ObjectInput in)throws IOException,ClassNotFoundException; 3 public void writeExternal(ObjectOutput out)throws IOException; 4 }
利用此接口修改之前的程序:
1 package io.Serializable; 2 3 import java.io.Externalizable; 4 import java.io.IOException; 5 import java.io.ObjectInput; 6 import java.io.ObjectOutput; 7 8 public class Student implements Externalizable { 9 private String name; 10 private int age; 11 12 public Student(String name, int age) { 13 super(); 14 this.name = name; 15 this.age = age; 16 } 17 18 public String getName() { 19 return name; 20 } 21 22 public void setName(String name) { 23 this.name = name; 24 } 25 26 public int getAge() { 27 return age; 28 } 29 30 public void setAge(int age) { 31 this.age = age; 32 } 33 34 @Override 35 public String toString() { 36 return "Student [name=" + name + ", age=" + age + "]"; 37 } 38 39 @Override 40 public void readExternal(ObjectInput in) throws IOException, 41 ClassNotFoundException { 42 this.name = (String) in.readObject(); //读取属性 43 this.age = in.readInt(); 44 } 45 46 @Override 47 public void writeExternal(ObjectOutput out) throws IOException { 48 out.writeObject(this.name); //保存属性 49 out.writeInt(this.age); 50 } 51 52 }
1 package io.Serializable; 2 3 import java.io.File; 4 import java.io.FileInputStream; 5 import java.io.FileNotFoundException; 6 import java.io.FileOutputStream; 7 import java.io.IOException; 8 import java.io.ObjectInputStream; 9 import java.io.ObjectOutputStream; 10 11 public class SerializableDemo { 12 13 public static void main(String[] args) { 14 15 File file = new File("E:" + File.separator + "tmp" + File.separator 16 + "stu.obj"); // 保存路径 17 try { 18 ObjectOutputStream oos = new ObjectOutputStream( 19 new FileOutputStream(file)); 20 21 Student[] students = { new Student("张三", 12), 22 new Student("李四", 15), new Student("王五", 18) }; 23 oos.writeObject(students); // 直接写入一个对象数组,因为数组也是对象 24 oos.close(); 25 26 27 ObjectInputStream ois = new ObjectInputStream(new FileInputStream( 28 file)); 29 Student[] stus = (Student[]) ois.readObject(); 30 ois.close(); 31 32 for (Student student : stus) { 33 System.out.println(student); 34 } 35 36 } catch (FileNotFoundException e) { 37 e.printStackTrace(); 38 } catch (IOException e) { 39 e.printStackTrace(); 40 } catch (ClassNotFoundException e) { 41 e.printStackTrace(); 42 } 43 44 } 45 46 }
运行结果:
在使用Externalizable接口的时候需要原有的类中有无参构造,在Student类中加入无参构造后一切正常了!此外在Externalizable接口中也可以自定义哪些属性需要序列化,见以下代码:
1 @Override 2 public void readExternal(ObjectInput in) throws IOException, 3 ClassNotFoundException { 4 this.name = (String) in.readObject(); //读取属性 5 // this.age = in.readInt(); 6 } 7 8 @Override 9 public void writeExternal(ObjectOutput out) throws IOException { 10 out.writeObject(this.name); //保存属性 11 // out.writeInt(this.age); 12 }
以上代码在运行的时候age属性将不会序列化!
在序列化对象的时候如果不需要某个属性被序列化可以使用transient关键字进行修饰。如此一来Externalizable接口就变得毫无用途。
1 private transient int age; //age属性不会被序列化
运行结果:
由此可见Externalizable接口的功能完全可以由Serializable接口和transient关键字的组合来取代!
标签:
原文地址:http://www.cnblogs.com/happyfans/p/4230480.html