`
konoha
  • 浏览: 4459 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

【转载】 Java 7之基础 - 序列化与反序列化

 
阅读更多

转载出处:http://blog.csdn.net/mazhimazh/article/details/20292331

 

使用Java的序列化和反序列化可以实现信息的持久存储、或者也可以实现Java对象的深克隆。在前面文章讲解过使用序列化和反序列化来实现对象克隆,如下:

Java之基础 - 深克隆与浅克隆(参见文章:http://blog.csdn.net/mazhimazh/article/details/16828505

下面来具体讲解一下序列化。能够进行序列化的类必须要实现Serializable接口,这个接口的定义如下:

 

[java] view plain copy
 
 print?
  1. public interface Serializable {   }  

这个接口仅用作一个标识,表示这个类可以进行序列化和反序列化。典型代码如下:

 

 

[java] view plain copy
 
 print?
  1. class Shitt implements Serializable{  // 必须实现Serializable接口   }  

 

将如下的类进行序列化和反序列化时继承序列化接口:

 

[java] view plain copy
 
 print?
  1. class Shitt  implements Serializable{  
  2.     private static final long serialVersionUID = 1L;  
  3.     private String name;  
  4.     public String getName() {  
  5.         return name;  
  6.     }  
  7.     public void setName(String name) {  
  8.         this.name = name;  
  9.     }  
  10. }  

 

进行类的序列化和反序列化操作:

 

[java] view plain copy
 
 print?
  1. public static void save(Shitt t) {  // 序列化给定的类  
  2.         try {  
  3.             FileOutputStream fs = new FileOutputStream("test.txt");  
  4.             ObjectOutputStream os = new ObjectOutputStream(fs);  
  5.             os.writeObject(t);  
  6.             os.flush();  
  7.             os.close();  
  8.         } catch (Exception ex) {  
  9.             ex.printStackTrace();  
  10.         }  
  11.     }  
  12.   
  13.     public static void read() { //反序列化给定的类  
  14.         try {  
  15.             FileInputStream fs = new FileInputStream("test.txt");  
  16.             ObjectInputStream ois = new ObjectInputStream(fs);  
  17.             Shitt login = (Shitt) ois.readObject();  
  18.             System.out.println(login.getName());  
  19.             ois.close();  
  20.         } catch (Exception ex) {  
  21.             ex.printStackTrace();  
  22.         }  
  23.     }  

 

需要注意的几个几点:

(1)在每个要序列化的类中加入private static final 的serialVersionUID,这样,即使在某个对象被序列化之后,它所对应的类被修改了,该对象也依然可以被正确地反序列化。举个例子:

 

[java] view plain copy
 
 print?
  1. public class MeTest{  
  2.     public static void main(String[] args) {  
  3.         Shitt t = new Shitt();  
  4.         t.setName("mazhi");  
  5.         MeTest.save(t);  
  6.         MeTest.read();  
  7.   
  8.     }  
  9.         // 省略save()和read()方法      
  10. }  

最终运行的结果如下:

 

mazhi

现在修改序列化类为:

 

[java] view plain copy
 
 print?
  1. class Shitt  implements Serializable{  
  2.     private static final long serialVersionUID = 1L;  
  3.     private String name;  
  4.     private int p;  
  5.     public String getName() {  
  6.         return name;  
  7.     }  
  8.     public void setName(String name) {  
  9.         this.name = name;  
  10.     }  
  11.     public int getP() {  
  12.         return p;  
  13.     }  
  14.     public void setP(int p) {  
  15.         this.p = p;  
  16.     }  
  17. }  

现在只调用read()方法进行读取操作,可以看到能够正常读取。去掉serialVersionUID属性或修改后报错:

 

local class incompatible:
实现序列化接口的实体能够兼容先前版本,不修改这个变量值的序列化实体都可以相互进行串行化和反串行化。

(2)如果一个类中有引用类型的实例变量,这个引用类型也要实现Serializable接口。否则就会抛出异常。或者在不实例Serializable接口时,为这个引用变量加上transient关键字即可。

首先说明一下引用类型的实例变量怎么理解,如下:

 

[java] view plain copy
 
 print?
  1. class Father{  }  // 没有实现Serializable接口  

 

 

[java] view plain copy
 
 print?
  1. class Shitt implements Serializable{  
  2.     private String name;  
  3.     public Father t;  
  4.     public String getName() {  
  5.         return name;  
  6.     }  
  7.     public void setName(String name) {  
  8.         this.name = name;  
  9.     }  
  10. }  

但是在进行序列化的时候不使用这个t,也就只是声明而不进行实例同样不会出现错误。如果要进行实例化,就需要实现接口,或者加上transient关键字也行,然后再序列化后就不会出错,如下:

 

 

[java] view plain copy
 
 print?
  1. Shitt t = new Shitt();  
  2.         t.setName("mazhi");  
  3.         t.t=new Father();  
  4.         MeTest.save(t);  
  5.         MeTest.read();  

一旦变量被transient修饰,变量将不再是对象持久化的一部分,该变量内容在序列化后无法获得访问。

(3)被transient关键字修饰的变量不再能被序列化,一个静态变量不管是否被transient修饰,均不能被序列化。

[java] view plain copy
 
 print?
  1. class Father{   
  2.     public int a;  
  3.       
  4.     public int getA() {  
  5.         return a;  
  6.     }  
  7.     public void setA(int a) {  
  8.         this.a = a;  
  9.     }  
  10. }  
  11. class Shitt extends Father implements Serializable{  
  12.     private String name;  
  13.     public static String str="my name is mazhi";  
  14.     public String getName() {  
  15.         return name;  
  16.     }  
  17.     public void setName(String name) {  
  18.         this.name = name;  
  19.     }  
  20. }  

在这个类中,到底哪些变量的值能够进行序列化,哪些不能呢?测试如下:

 

[java] view plain copy
 
 print?
  1. Shitt t = new Shitt();  
  2.         t.setName("mazhi");  
  3.         t.a=3;  
  4.         MeTest.save(t);  
  5.         MeTest.read();  

在read()方法中打印str和a的值:

 

 

[java] view plain copy
 
 print?
  1. System.out.println(login.getName()+" "+login.a);  
  2.             System.out.println(login.str);  

最终运行的结果如下:

 

mazhi 0
my name is mazhi


可以看出Father类没有实现序列化接口,其定义的属性不能被序列化。最需要注意的是静态变量,这个变量不被序列化,那为什么还可以读出值呢?

因为在类加载时这个变量已经存在于内存中了,而且保持着唯一,不会被垃圾回收。当再运行时,由于静态变量属于类,当然可以取出这个值了。我们可以在另外一个虚拟机上进行测试就不会出现这个问题了。

同时从这个例子还能得出一些结论:

如果一个类实现了Serializable接口,但是它的父类没有实现,那么这个类也可以序列化。需要提醒的是Object超类没有实现这个序列化的接口。为什么呢??因为如果一个类没有实现Serializable接口,但是它的父类实现了,那么这个类也可以序列化。Object实现序列化接口意味着所有的类都可以被序列化,这一概念变得无意义,现时还会造成各种问题。


(4)序列化和反序列化过程中构造函数的调用

 

 

[java] view plain copy
 
 print?
  1. class Father{   // 没有实现Serializable接口  
  2.     public Father(){  
  3.         System.out.println("父类构造方法");  
  4.     }  
  5. }  
  6. class Shitt extends Father implements Serializable{  
  7.     public Shitt(){  
  8.         System.out.println("子类构造方法");  
  9.     }  
  10. }  

对Shitt类进行序列化和反序列化后的运行结果如下:

 

父类构造方法
子类构造方法
父类构造方法

父类加上序列化接口后再次进行序列化和反序列化的操作,结果如下:

父类构造方法
子类构造方法

由上可以得出结论:在进行反序列化过程中还是遵守类的初始化规则,也就是先父类后子类,但是子类的初始化并不是通过调用构造函数等手段,而是从持久化文件中存储的信息进行初始化的。

扩展:

(1)其实可以在类的序列化过程中,指定属性存储的格式,例如XML,反序列化也是一样的,有兴趣的话可以参见其他资料。

(2)既然序列化和反序列化可以实现文件的持久存取,那么与其他一些持久化,例如数据库比起来又怎么样呢?

分享到:
评论

相关推荐

    浅谈java中为什么实体类需要实现序列化

    在Java中,实体类需要实现序列化是因为序列化是将对象转换为字节流的过程,而反序列化是将字节流转换回对象的过程。在Java中,实现序列化的类需要实现java.io.Serializable接口,否则将会抛出异常。 首先,为什么...

    [转载] JAXB中Java对象和XML之间的转换

    JAXB允许我们将Java类与XML Schema定义的元素和类型绑定,从而实现XML的序列化和反序列化。 在Java对象和XML之间的转换过程中,JAXB主要涉及到以下几个关键概念: 1. **Java类与XML Schema绑定**: JAXB使用`@...

    java 时间转换(转载)

    9. **日期时间的序列化和反序列化**: 在JSON序列化时,可能需要将日期时间转换为字符串,可以使用`Jackson`库的`@JsonFormat`注解来指定序列化格式。 在提供的`Test.java`文件中,可能包含了对上述知识点的实际...

    深入分析 Java I/O 的工作机制(转载)

    6. **对象序列化与反序列化** Java提供了Serializable接口,使得对象可以被序列化成字节流,便于存储或通过网络传输。ObjectInputStream和ObjectOutputStream负责对象的序列化和反序列化操作。 7. **NIO(非阻塞I/...

    Java利用Preferences设置个人偏好,转载自:燕窝

    Preferences API默认只支持字符串,但可以通过序列化和反序列化将其他类型的对象转换为字符串进行存储。例如,可以使用`ObjectOutputStream`和`ObjectInputStream`来处理非字符串数据。 3. **安全性和权限**: ...

    Castor学习笔记 (转载)

    它通过XML Schema或XML Mapping文件定义Java类与XML元素之间的映射关系,实现了XML数据的自动序列化和反序列化。 2. XML绑定与映射文件 在Castor中,映射文件通常以`.xml`扩展名,如`mapping.xml`,定义了Java类与...

    java编程事项(转载收集整理版)

    6. **输入/输出(I/O)**:Java的I/O流系统包括字符流和字节流,以及用于文件操作、网络通信和对象序列化的API。学会使用BufferedReader、FileWriter等类进行文件操作,是开发中常见的需求。 7. **网络编程**:Java...

    com.fasterxml.jackson

    使用databind模块,直接对Java对象进行序列化和反序列化 通常来说,我们在日常开发中使用的是第3种方式,有时为了简便也会使用第2种方式,比如你要从一个很大的Json对象中只读取那么一两个字段的时候,采用databind...

    java编写建议(转载)

    - 实现`Serializable`接口以支持序列化。 #### 二、主方法与程序入口 **2.1 主方法** - 每个可执行的Java程序都应包含一个`main`方法作为入口点。 - 避免删除源代码中的`main`方法,即使在调试过程中也不应该移除...

    dwr中文教程~转载的,共享一下

    1. **AjaxEngine**:这是DWR的核心,它负责处理JavaScript到Java的通信,包括序列化和反序列化、错误处理等。 2. **Caucho Hessian/Burlap**:DWR可以使用Hessian或Burlap作为传输协议,它们是轻量级的二进制/文本...

    转载 xStream完美转换XML、JSON

    xStream是一个强大的库,它提供了一种简单的方式来序列化和反序列化Java对象到XML,反之亦然。在这个场景中,它同样能够处理JSON转换,虽然JSON不是其原生支持的格式,但通过一些额外的配置和库,如json-simple,...

    tomcat使用memcached集群session共享(转载并修改)

    该工具有多种序列化策略,例如 msm-javolution-serializer,用于将 Java 对象转换为可在网络上传输的字节序列。 下面是实现 Tomcat 使用 Memcached 集群进行 Session 共享的详细步骤: 1. **安装和配置 Memcached*...

    word源码java-csdn-blogs:这是一个存储我的csdn博客的存储库

    4. 文件和I/O流操作:文件读写、缓冲流、对象序列化等。 5. 多线程编程:线程同步、线程池、并发工具类等。 6. 网络编程:Socket通信、HTTP请求等。 7. Java异常处理:自定义异常、异常链、try-catch-finally语句块...

    Android面试专题(七):Serializable 和 Parcelable 的区别

    不诗意的女程序媛不是好厨师~ 转载请注明出处,From李诗雨—...Serializable是序列化的意思,表示将一个对象转换成可存储或可传输的状态。序列化后的对象可以在网络上进行传输,也可以存储到本地。 Parcelable(androi

    javac的词法分析,转载自:百度文库

    **Java编译器 javac 的词法分析** Java编译器`javac`是Java开发不可或缺的一部分,负责将源代码转换成可执行的字节码。在这个过程中,词法分析(也称为扫描或词法分解)是第一步,它是将源代码文本转化为编程语言的...

    WCF 分布式开发转载

    类中的属性通常带有`[DataMember]`特性,表示需要序列化。 2. **服务接口**: 服务接口定义了可供调用的操作,使用`[ServiceContract]`和`[OperationContract]`标记。操作方法通常是服务的实际业务逻辑。 3. **服务...

    deeplearning4j.zip (自己封装,deeplearning4j和apache的资源)欢迎转载

    Deeplearning4j是中国开发者社区基于Java开发的一款深度学习库,它旨在使Java和Scala的开发者能够方便地进行机器学习,特别是在深度学习领域的应用。这个压缩包“deeplearning4j.zip”包含了开发者封装的资源,结合...

    jfreechat 中文API

    **JFreeChart中文API**是Java开发者在创建数据可视化应用时的一个强大工具。JFreeChart是一个开源库,它提供了一套丰富的图表类型,包括饼图、柱状图、线图、散点图、甘特图等,使得开发人员能够轻松地在Java应用...

Global site tag (gtag.js) - Google Analytics