Java 对象序列化为二进制文件的技术是 Java 系列技术中一个较为重要的技术点,在大多数情况下,只需要了解被序列化的类需要实现 Serializable 接口,使用 ObjectInputStream 和 ObjectOutputStream 进行序列化和反序列化即可。
在工作中,经常会碰到序列化的情况,因为我们调用的东西不可能都在本地,往往需要通过RMI,一些远程接口调用。这样数据序列化的技术更显得重要。而java序列化技术为我们实现这些远程调用提供了技术保障。
一. 序列化的ID,请看如下代码
片段一:
- public class Person implements Serializable{
- private static final long serialVersionUID = 1L;
- private int age;
- private String name;
- private String mobile;
- private transient String address; //不被序列化的属性
- }
片段二:
- public class Person implements Serializable{
- private static final long serialVersionUID = 2L;
- private int age;
- private String name;
- private String mobile;
- private transient String address; //不被序列化的属性
- }
两个类除了serialVersionUID,其余完全一样,但这两个类是不能被序列化与反序列化的,所以在写程序时,实现了序列化的类,serialVersionUID一定要一致,否则客户端与服务端将不匹配,会因serialVersionUID不匹配导致意想不到的问题。当然也可以通过服务端强制更新serialVersionUID值,让客户端主动报错,来强制更新程序。
二.子类实现Serializable接口,而父类并没有实现Serializable 接口
父类:Person.java
- /**
- * Created on 2011-3-11
- * @author shixing_11@sina.com
- * @version 1.0
- */
- public class Person {
- private int age;
- private String name;
- private String mobile;
- private transient String address; //不被序列化的属性
- public Person(){
- System.out.println("父类没有实现序列化接口时,默认构造器会被调用");
- }
- public Person(String name,int age){
- this.name = name;
- this.age = age;
- }
- public int getAge(){
- return age;
- }
- public void setAge(int age){
- this.age = age;
- }
- public String getName(){
- return name;
- }
- public void setName(String name){
- this.name = name;
- }
- public String getMobile(){
- return mobile;
- }
- public void setMobile(String mobile){
- this.mobile = mobile;
- }
- public void setAddress(String address) {
- this.address = address;
- }
- public String getAddress(){
- return address;
- }
- }
子类:Student.java
- /**
- * Created on 2011-3-11
- * @author shixing_11@sina.com
- * @version 1.0
- */
- public class Student extends Person implements Serializable
- {
- public Student(String name, int age){
- super(name, age);
- }
- private static final long serialVersionUID = 1L;
- public static String S_T = "static属性不能被序列化";
- private int no;
- private String schoolName;
- public void setSchoolName(String schoolName){
- this.schoolName = schoolName;
- }
- public String getSchoolName(){
- return schoolName;
- }
- public void setNo(int no){
- this.no = no;
- }
- public int getNo(){
- return no;
- }
- }
做如下测试:
- /**
- * Created on 2011-3-11
- * @author shixing_11@sina.com
- * @version 1.0
- */
- public class SerializeTest
- {
- public static void main(String[] args){
- Student student = new Student("shixing_11",27);
- student.setMobile("1515808XXXX"); //父类的属性
- student.setAddress("浙江省杭州市"); //父类的属性
- student.setNo(1111111); //子类自有属性
- student.setSchoolName("XX理工大学"); //子类自有属性
- serializeObject(student); //序列化方法
- Student.S_T = "我是static,我的值被改";
- deSerializeObject(); //反序列化
- }
- private static void deSerializeObject() {
- try{
- InputStream is = new FileInputStream("C://ss.ser");
- ObjectInputStream ois = new ObjectInputStream(is);
- Student student = (Student)ois.readObject();
- ois.close();
- System.out.println(Student.S_T);
- System.out.println(student.getNo());
- System.out.println(student.getSchoolName());
- }catch (ClassNotFoundException e){
- e.printStackTrace();
- }catch (FileNotFoundException e){
- e.printStackTrace();
- } catch (IOException e){
- e.printStackTrace();
- }
- }
- public static void serializeObject(Person person){
- try{
- OutputStream os = new FileOutputStream("C://ss.ser");
- ObjectOutputStream ois = new ObjectOutputStream(os);
- ois.writeObject(person);
- ois.close();
- }catch (FileNotFoundException e){
- e.printStackTrace();
- }catch (IOException e){
- e.printStackTrace();
- }
- }
- }
运行结果:
结论:
1. 声明为 transient 型的属性是不被序列化的。
2. S_T属性的值被改变了,本来是序列时保存进去的是"static属性不能被序列化“,但读出来发现该值被改变,说明static声明的属性并没有 被写进文件里,所以声明为static型的属性是不被序列化的。
3. 子类实现Serializable接口,父类未实现Serializable接口,反序列化时父类的属性会被恢复成原始值,即int 0 ,String null.
父类必须有无参的构造方法,因为如果父类实现了Serializable接口,则会直接从序列化过的文件里反序列化,读出来原来的对象即可,
但若父类没有实现Serializable接口,则无法直接读取,所以只有调父类的默认构造器创建出该类的对象,再进行属性填充。
三.父类实现了Serializable接口的情况较简单,只需记住规则,如果父类实现了Serializable接口,则子类默认也可被序列化,不需要显式声明Serializable接口。