对于Java序列化的一次认识 转javaeye(感觉不错)

其实这个问题简单思考一下就可以搞清楚,方法是不带状态的,就是一些指令,指令是不需要序列化的,只要你的JVM classloader可以load到这个类,那么类方法指令自然就可以获得。序列化真正需要保存的只是对象属性的值,和对象的类型。 

这些知识找一本Java基础编程的书,或者Java手册就可以查到,我以为是不应该犯这种基本概念错误的。 

我们可以做一个简单的小试验,来证实一下: 

Java代码  复制代码
  1. package com.javaeye;   
  2.   
  3. import java.io.Serializable;   
  4.   
  5. public class DomainObject  implements Serializable {   
  6.   
  7.     private String name;   
  8.        
  9.     private int age ;   
  10.   
  11.     public int getAge(); {   
  12.         return age;   
  13.      }   
  14.   
  15.     public void setAge(int age); {   
  16.         this.age = age;   
  17.      }   
  18.   
  19.     public String getName(); {   
  20.         return name;   
  21.      }   
  22.   
  23.     public void setName(String name); {   
  24.         this.name = name;   
  25.      }   
  26.        
  27.        
  28. }  
Java代码    收藏代码
  1. package com.javaeye;  
  2.   
  3. import java.io.Serializable;  
  4.   
  5. public class DomainObject  implements Serializable {  
  6.   
  7.  private String name;  
  8.    
  9.  private int age ;  
  10.   
  11.  public int getAge(); {  
  12.   return age;  
  13.  }  
  14.   
  15.  public void setAge(int age); {  
  16.   this.age = age;  
  17.  }  
  18.   
  19.  public String getName(); {  
  20.   return name;  
  21.  }  
  22.   
  23.  public void setName(String name); {  
  24.   this.name = name;  
  25.  }  
  26.    
  27.    
  28. }  



Java代码  复制代码
  1. package com.javaeye;   
  2.   
  3. import java.io.FileOutputStream;   
  4. import java.io.ObjectOutputStream;   
  5.   
  6. public class Main {   
  7.   
  8.     public static void main(String[] args); throws Exception {   
  9.          DomainObject obj = new DomainObject();;   
  10.          obj.setAge(29);;   
  11.          obj.setName("fankai");;   
  12.          FileOutputStream fos = new FileOutputStream("DomainObject");;   
  13.          ObjectOutputStream oos = new ObjectOutputStream(fos);;   
  14.          oos.writeObject(obj);;   
  15.          oos.close();;   
  16.          fos.close();;   
  17.      }   
  18.   
  19. }  
Java代码    收藏代码
  1. package com.javaeye;  
  2.   
  3. import java.io.FileOutputStream;  
  4. import java.io.ObjectOutputStream;  
  5.   
  6. public class Main {  
  7.   
  8.  public static void main(String[] args); throws Exception {  
  9.   DomainObject obj = new DomainObject();;  
  10.   obj.setAge(29);;  
  11.   obj.setName("fankai");;  
  12.   FileOutputStream fos = new FileOutputStream("DomainObject");;  
  13.   ObjectOutputStream oos = new ObjectOutputStream(fos);;  
  14.   oos.writeObject(obj);;  
  15.   oos.close();;  
  16.   fos.close();;  
  17.  }  
  18.   
  19. }  



DomainObject是我们准备序列化的类,在Main里面,我们new一个DomainObject的对象,然后赋值,最后把该对象序列化到一个硬盘文件中。 

然后使用一种支持二进制编辑器,例如UltraEdit打开这个文件,看看Java都对DomainObject序列化了哪些信息,你就什么都明白了。 

为了更方便观察,我使用Linux下面的strings去提取文本信息,输出为: 

robbin@linux:~> strings DomainObject 
com.javaeye.DomainObject 
ageL 
namet 
Ljava/lang/String;xp 
fankai 

这些信息很直观的告诉我们序列化都保存了些什么内容: 
1)对象的类型 
2)对象属性的类型 
3)对象属性的值 

并没有什么方法签名的信息,更不要说什么序列化方法了。 

然后我们再做一个试验,给DomainObject增加两个方法: 
Java代码  复制代码
  1. package com.javaeye;   
  2.   
  3. import java.io.Serializable;   
  4.   
  5. public class DomainObject  implements Serializable {   
  6.   
  7.     private String name;   
  8.        
  9.     private int age ;   
  10.   
  11.     public int getAge(); {   
  12.         return age;   
  13.      }   
  14.   
  15.     public void setAge(int age); {   
  16.         this.age = age;   
  17.      }   
  18.   
  19.     public String getName(); {   
  20.         return name;   
  21.      }   
  22.   
  23.     public void setName(String name); {   
  24.         this.name = name;   
  25.      }   
  26.        
  27.     public String toString(); {   
  28.         return "This is a serializable test!";   
  29.      }   
  30.        
  31.     public void doSomeWork(); {   
  32.          System.out.println("hello");;   
  33.      }   
  34. }  
Java代码    收藏代码
  1. package com.javaeye;  
  2.   
  3. import java.io.Serializable;  
  4.   
  5. public class DomainObject  implements Serializable {  
  6.   
  7.  private String name;  
  8.    
  9.  private int age ;  
  10.   
  11.  public int getAge(); {  
  12.   return age;  
  13.  }  
  14.   
  15.  public void setAge(int age); {  
  16.   this.age = age;  
  17.  }  
  18.   
  19.  public String getName(); {  
  20.   return name;  
  21.  }  
  22.   
  23.  public void setName(String name); {  
  24.   this.name = name;  
  25.  }  
  26.    
  27.  public String toString(); {  
  28.   return "This is a serializable test!";  
  29.  }  
  30.    
  31.  public void doSomeWork(); {  
  32.   System.out.println("hello");;  
  33.  }  
  34. }  


我们增加了toString方法和doSomeWork方法,按照你的理论,如果序列化方法的话,产生的文件体积必然增大。记录一下文件体积,92Byte,好了,删除,运行程序,生成了新的文件,看一下体积,还是92Byte! 

拿到Linux下面再提取一下字符串: 

robbin@linux:~> strings DomainObject 
com.javaeye.DomainObject 
ageL 
namet 
Ljava/lang/String;xp 
fankai 

完全一模一样! 

然后我们再做第三个试验,这次把DomainObject的两个属性以及相关方法删除掉: 
Java代码  复制代码
  1. package com.javaeye;   
  2.   
  3. import java.io.Serializable;   
  4.   
  5. public class DomainObject  implements Serializable {   
  6.   
  7.     public String toString(); {   
  8.         return "This is a serializable test!";   
  9.      }   
  10.        
  11.     public void doSomeWork(); {   
  12.          System.out.println("hello");;   
  13.      }   
  14. }  
Java代码    收藏代码
  1. package com.javaeye;  
  2.   
  3. import java.io.Serializable;  
  4.   
  5. public class DomainObject  implements Serializable {  
  6.   
  7.  public String toString(); {  
  8.   return "This is a serializable test!";  
  9.  }  
  10.    
  11.  public void doSomeWork(); {  
  12.   System.out.println("hello");;  
  13.  }  
  14. }  


修改Main类如下: 

Java代码  复制代码
  1. package com.javaeye;   
  2.   
  3. import java.io.FileOutputStream;   
  4. import java.io.ObjectOutputStream;   
  5.   
  6. public class Main {   
  7.   
  8.     public static void main(String[] args); throws Exception {   
  9.          DomainObject obj = new DomainObject();;   
  10.   
  11.          FileOutputStream fos = new FileOutputStream("DomainObject");;   
  12.          ObjectOutputStream oos = new ObjectOutputStream(fos);;   
  13.          oos.writeObject(obj);;   
  14.          oos.close();;   
  15.          fos.close();;   
  16.      }   
  17.   
  18. }  
Java代码    收藏代码
  1. package com.javaeye;  
  2.   
  3. import java.io.FileOutputStream;  
  4. import java.io.ObjectOutputStream;  
  5.   
  6. public class Main {  
  7.   
  8.  public static void main(String[] args); throws Exception {  
  9.   DomainObject obj = new DomainObject();;  
  10.   
  11.   FileOutputStream fos = new FileOutputStream("DomainObject");;  
  12.   ObjectOutputStream oos = new ObjectOutputStream(fos);;  
  13.   oos.writeObject(obj);;  
  14.   oos.close();;  
  15.   fos.close();;  
  16.  }  
  17.   
  18. }  



按照你的理论,如果序列化方法的话,我们必然应该在文件里面发现方法的签名信息,甚至方法里面包含的字符串,好了,再运行一遍,然后打开看一下吧!文件现在体积变成了45Byte,拿到Linux下面提取一下信息: 

robbin@linux:~> strings DomainObject 
com.javaeye.DomainObject 

只有对象的类型信息,再无其它东西了! 

请记住序列化机制只保存对象的类型信息,属性的类型信息和属性值,和方法没有什么关系,你就是给这个类增加10000个方法,序列化内容也不会增加任何东西,不要想当然的臆测自己不了解的知识,动手去做!

补充:

序列化在 Effective Java 中讲得很清楚啊, 一般认为只声明实现 implements 接口, 不提供自定义的序列化形式是不负责任的做法, 这样可能导致比较多的问题比如类的不同版本之间的兼容性, 看看 Effective Java 中的条目吧     谨慎地实现 Serialiable     
    为了继承而设计的类应该很少实现 Serialiable, 接口也应该很少会扩展它. 如果违反了这条规则, 则扩展这个类或者实现这个接口的程序员会背上沉重的负担. 
    
     若没有认真考虑默认序列化形式是否合适, 则不要接受这种形式 
   
    即使你确定了默认序列化形式是合适的, 通常你仍然要提供一个 readObject方法以保证约束关系和约束性    
    不管你选择了哪种序列化形式, 你都要为自己编写的每个可序列化的类声明一个显式的序列版本 UID (serialVersionUID)
 
从上面的结论看来, 实现了 Serialiable 接口并有比较好的实现的 OpenSource 代码几乎寥寥可数  , JDK 里的情况要好一些

 

你可能感兴趣的:(java序列化)