java.io 序列化 总结Serializable 和 Externalizable 的区别与联系,以及输出后的乱码问题

[java] view plain copy
  1. "WHITE-SPACE: pre"我们可以通过序列化来保存一个对象的状态(实例变量)到文件中,也可以从这个格式化的文件中很容易地读取对象的状态从而可以恢复我们保存的对象。  

对象序列化就是把对象写入到输出流中,用来存储或者传输。

对象的反序列化就是从输入流中读取对象。

      用来实现序列化的类都在Java.io包中,我们常用的类或接口有:

        ObjectOutputStream:提供序列化对象并把其写入流的方法

ObjectInputStream:读取流并反序列化对象

Serializable:一个对象想要被序列化,那么它的类就要实现 此接口,这个对象的所有属性(包括private属性、包括其引用的对象)都可以被序列化和反序列化来保存、传递。

Externalizable:他是Serializable接口的子类,有时我们不希望序列化那么多,可以使用这个接口,这个接口的writeExternal()和readExternal()方法可以指定序列化哪些属性;

        但是如果你只想隐藏一个属性,比如用户对象user的密码pwd,如果使用Externalizable,并除了pwd之外的每个属性都写在writeExternal()方法里,这样显得麻烦,可以使用Serializable接口,并在要隐藏的属性pwd前面加上transient就可以实现了。

[java] view plain copy
  1. import java.io.*;   
  2.   
  3. /**  
  4. * Java对象的序列化测试  
  5. * File: ObjectStreamTest.java  
  6. * User: leizhimin  
  7. * Date: 2008-3-12 20:41:43  
  8. */   
  9. public class ObjectStreamTest {   
  10.     public static void main(String args[]) {   
  11.         testObjectSeri();   
  12.         testObjectInSeri();   
  13.   
  14.     }   
  15.   
  16.     /**  
  17.      * 对象序列化测试  
  18.      */   
  19.     public static void testObjectSeri() {   
  20.         Person person = new Person("熔岩""341022225562156""lavasoft");   
  21.         FileOutputStream fos = null;   
  22.         ObjectOutputStream oos = null;   
  23.         try {   
  24.             fos = new FileOutputStream("Q:\\study\\java5study\\src\\io\\person.dat");   
  25.             oos = new ObjectOutputStream(fos);   
  26.             oos.writeObject(person);   
  27.         } catch (FileNotFoundException e) {   
  28.             System.out.println("找不到指定的文件!");   
  29.             e.printStackTrace();   
  30.         } catch (IOException e) {   
  31.             e.printStackTrace();   
  32.         } finally {   
  33.             try {   
  34.                 oos.flush();   
  35.                 oos.close();   
  36.             } catch (IOException e) {   
  37.                 e.printStackTrace();   
  38.             }   
  39.         }   
  40.     }   
  41.   
  42.     /**  
  43.      * 对象反序列化测试  
  44.      */   
  45.     public static void testObjectInSeri() {   
  46.         FileInputStream fis = null;   
  47.         ObjectInputStream ois = null;   
  48.         Person person = null;   
  49.         try {   
  50.             fis = new FileInputStream("Q:\\study\\java5study\\src\\io\\person.dat");   
  51.             ois = new ObjectInputStream(fis);   
  52.             person = (Person) ois.readObject();   
  53.         } catch (FileNotFoundException e) {   
  54.             e.printStackTrace();   
  55.         } catch (IOException e) {   
  56.             e.printStackTrace();   
  57.         } catch (ClassNotFoundException e) {   
  58.             e.printStackTrace();   
  59.         } finally {   
  60.             try {   
  61.                 ois.close();   
  62.             } catch (IOException e) {   
  63.                 e.printStackTrace();   
  64.             }   
  65.         }   
  66.   
  67.         System.out.println(person.toString());   
  68.     }   
  69. }   
  70.   
  71. /**  
  72. * 测试序列化所用的类  
  73. */   
  74. class Person implements Serializable {   
  75.     private String username;   
  76.     private String cardNumber;   
  77.     private transient String password;   
  78.   
  79.     public Person(String username, String cardNumber, String password) {   
  80.         this.username = username;   
  81.         this.cardNumber = cardNumber;   
  82.         this.password = password;   
  83.     }   
  84.   
  85.     public String getUsername() {   
  86.         return username;   
  87.     }   
  88.   
  89.     public void setUsername(String username) {   
  90.         this.username = username;   
  91.     }   
  92.   
  93.     public String getCardNumber() {   
  94.         return cardNumber;   
  95.     }   
  96.   
  97.     public void setCardNumber(String cardNumber) {   
  98.         this.cardNumber = cardNumber;   
  99.     }   
  100.   
  101.     public String getPassword() {   
  102.         return password;   
  103.     }   
  104.   
  105.     public void setPassword(String password) {   
  106.         this.password = password;   
  107.     }   
  108.   
  109.     public String toString() {   
  110.         StringBuffer sb = new StringBuffer(this.getClass().getName());   
  111.         sb.append("[");   
  112.         sb.append("\n\t");   
  113.         sb.append("username=" + this.username);   
  114.         sb.append("\n\t");   
  115.         sb.append("cardNumber=" + this.cardNumber);   
  116.         sb.append("\n\t");   
  117.         sb.append("password=" + this.password);   
  118.         sb.append("]");   
  119.         return sb.toString();   
  120.     }   
  121. }  

运行结果如下:
[java] view plain copy
  1. io.Person[   
  2.     username=熔岩   
  3.     cardNumber=341022225562156   
  4.     password=null]   
  5.   
  6. Process finished with exit code 0   

Serializable接口和Externalizable接口的区别不仅限于此:

Serializable序列化时不会调用默认的构造器,而Externalizable序列化时会调用默认构造器的!!!


其他说明:

1、        基本类型 的数据可以直接序列化

2、        对象要被序列化,它的类必须要实现Serializable接口;如果一个类中有引用类型的实例变量,这个引用类型也要实现Serializable接口。比如上面 的例子中,Student类中有一个Book类型 的实例就是,要想让Student的对象成功序列化,那么Book也必须要实现Serializable接口;

如果不想让Book实现Serializable接口,并且让Student类成功序列化也可以,使用transient关键字

 

serialVersionUID作用:序列化时为了保持版本的兼容性,即在版本升级时反序列化仍保持对象的唯一性。 有两种生成方式: 一个是默认的1L,比如:private static final long se...

serialVersionUID作用: 
序列化时为了保持版本的兼容性,即在版本升级时反序列化仍保持对象的唯一性。
 
有两种生成方式:
 
一个是默认的1L,比如:
private static final long serialVersionUID = 1L;
一个是根据类名、接口名、成员方法及属性等来生成一个64位的哈希字段,比如:
 
private static final long serialVersionUID = xxxxL; 
当你一个类实现了Serializable接口,如果没有定义serialVersionUIDEclipse会提供这个
 
提示功能告诉你去定义 。在Eclipse中点击类中warning的图标一下,Eclipse就会
 
自动给定两种生成的方式。如果不想定义它,在Eclipse的设置中也
 
可以把它关掉的,设置如下:
 
Window ==> Preferences ==> Java ==> Compiler ==> Error/Warnings ==> 
Potential programming problems 
Serializable class without serialVersionUIDwarning改成ignore即可。
 

 


最后,还有两个问题:

1、        如果一个类没有实现Serializable接口,但是它的基类实现 了,这个类可不可以序列化?

2、        和上面相反,如果一个类实现了Serializable接口,但是它的父类没有实现 ,这个类可不可以序列化?

 

第1个问题:一个类实现 了某接口,那么它的所有子类都间接实现了此接口,所以它可以被 序列化。

第2个问题:Object是每个类的超类,但是它没有实现 Serializable接口,但是我们照样在序列化对象,所以说明一个类要序列化,它的父类不一定要实现Serializable接口。但是在父类中定义 的状态能被正确 的保存以及读取吗?这个我将在下一篇文章中用一个例子来说明,请见http://blog.csdn.NET/moreevan/article/details/6698529

第3个问题:如果将一个对象写入某文件(比如是a),那么之后对这个对象进行一些修改,然后把修改的对象再写入文件a,那么文件a中会包含该对象的两个 版本吗?

本文参考http://blog.csdn.Net/moreevan/article/details/6697777


关于乱码问题:

现在问题来了,好多人会发现,在反序列化程序中运行后能够正常输出Person的相关信息,但是在目录下的文件“person.obj”用文本编辑器打开之后却是乱码的。这是为什么呢?是不是因为写出去的编码和文本编辑器的默认编码采用了不一样的字符集?然后我们就开始想怎么能够将二者的编码格式设为一致的。

其实,这么想一开始就是错误的。为什么这么说呢,因为序列化和反序列化都是基于二进制流的,也就是说,在第二步中,其实就是将person的相关信息转化为二进制存储在了person.obj这个文件中,那么用文本编辑器打开查看的话当然是会出现乱码的。只有通过反序列化才能将存储的二进制读取出来,然后正常显示在控制台上。


你可能感兴趣的:(java)