java中的序列化和反序列化

一 简介

把对象转换为字节序列的过程称为对象的序列化
把字节序列恢复为对象的过程称为对象的反序列化
对象的序列化主要有两种用途:
1) 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中;
2) 在网络上传送对象的字节序列。

在很多应用中,需要对某些对象进行序列化,让它们离开内存空间,入住物理硬盘,以便长期保存。比如最常见的是Web服务器中的Session对象,当有 10万用户并发访问,就有可能出现10万个Session对象,内存可能吃不消,于是Web容器就会把一些seesion先序列化到硬盘中,等要用了,再把保存在硬盘中的对象还原到内存中。

当两个进程在进行远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。发送方需要把这个Java对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为Java对象。


二 Java中的序列化API

java.io.ObjectOutputStream代表对象输出流,它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中;java.io.ObjectInputStream代表对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回


只有实现了Serializable或Externalizable接口的类的对象才能被序列化。有些java教程里面讲过,Externalizable接口继承自 Serializable接口,实现Externalizable接口的类完全由自身来控制序列化的行为,而仅实现Serializable接口的类可以 采用默认的序列化方式 。


对象序列化包括如下步骤:
1) 创建一个对象输出流,它可以包装一个其他类型的目标输出流,如文件输出流;
2) 通过对象输出流的writeObject()方法写对象。

对象反序列化的步骤如下:
1) 创建一个对象输入流,它可以包装一个其他类型的源输入流,如文件输入流;
2) 通过对象输入流的readObject()方法读取对象。

三 使用Serializable接口实现的实例

首先定义了一个实现了Serializable接口WebSite的实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package  cn.zifangsky.serializable;
 
import  java.io.Serializable;
 
public  class  WebSite  implements  Serializable {
     private  static  final  long  serialVersionUID = 1835573222135484360L;
 
     private  String siteName;
     private  String siteUrl;
     private  String webMaster;
 
     public  WebSite() {
 
     }
 
     public  WebSite(String siteName, String siteUrl, String webMaster) {
         this .siteName = siteName;
         this .siteUrl = siteUrl;
         this .webMaster = webMaster;
     }
 
     public  String getSiteName() {
         return  siteName;
     }
 
     public  void  setSiteName(String siteName) {
         this .siteName = siteName;
     }
 
     public  String getSiteUrl() {
         return  siteUrl;
     }
 
     public  void  setSiteUrl(String siteUrl) {
         this .siteUrl = siteUrl;
     }
 
     public  String getWebMaster() {
         return  webMaster;
     }
 
     public  void  setWebMaster(String webMaster) {
         this .webMaster = webMaster;
     }
 
     public  String toString() {
         return  "WebSite [siteName="  + siteName +  ", siteUrl="  + siteUrl +  ", webMaster="  + webMaster +  "]" ;
     }
 
}

然后进行序列化和反序列化测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package  cn.zifangsky.serializable;
 
import  java.io.File;
import  java.io.FileInputStream;
import  java.io.FileNotFoundException;
import  java.io.FileOutputStream;
import  java.io.IOException;
import  java.io.ObjectInputStream;
import  java.io.ObjectOutputStream;
 
public  class  TestSerialize {
 
     public  static  void  main(String[] args)  throws  FileNotFoundException, IOException, ClassNotFoundException {
         TestSerialize.serializeWebSite();
         TestSerialize.deserializeWebSite();
     }
     
     /**
      * 使用ObjectOutputStream 序列化WebSite
      * @throws IOException 
      * @throws FileNotFoundException 
     
      * */
     public  static  void  serializeWebSite()  throws  FileNotFoundException, IOException{
         WebSite webSite =  new  WebSite();
         webSite.setSiteName( "zifangsky的个人博客" );
         webSite.setSiteUrl( "http://www.zifangsky.cn" );
         webSite.setWebMaster( "zifangsky" );
         
         //序列化
         ObjectOutputStream objectOutputStream =  new  ObjectOutputStream( new  FileOutputStream( new  File( "C:/Users/Administrator/Desktop/test.txt" )));
         objectOutputStream.writeObject(webSite);
         objectOutputStream.flush();
         objectOutputStream.close();
     }
     
     /**
      * 使用ObjectInputStream 反序列化WebSite
      * @throws IOException 
      * @throws FileNotFoundException 
      * @throws ClassNotFoundException 
     
      * */
     public  static  void  deserializeWebSite()  throws  FileNotFoundException, IOException, ClassNotFoundException{
         ObjectInputStream objectInputStream =  new  ObjectInputStream( new  FileInputStream( new  File( "C:/Users/Administrator/Desktop/test.txt" )));
         //反序列化
         WebSite webSite = (WebSite) objectInputStream.readObject();
         objectInputStream.close();
 
         System.out.println(webSite);
     }
     
}

输出:

1
WebSite [siteName=zifangsky的个人博客, siteUrl=http://www.zifangsky.cn, webMaster=zifangsky]

四 使用Externalizable接口实现的实例

实现了Externalizable接口的实体类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package  cn.zifangsky.serializable;
 
import  java.io.Externalizable;
import  java.io.IOException;
import  java.io.ObjectInput;
import  java.io.ObjectOutput;
 
public  class  ExternalizableDemo  implements  Externalizable{
     private  String name;
     static  {
         System.out.println( "调用静态代码块" );
     }
 
     public  ExternalizableDemo() {
         System.out.println( "调用无参构造方法" );
     }
 
     public  ExternalizableDemo(String name) {
         this .name = name;
         System.out.println( "调用有参构造方法" );
     }
 
     public  String getName() {
         return  name;
     }
 
     public  void  setName(String name) {
         this .name = name;
     }
 
     public  String toString() {
         return  "ExternalizableDemo [name="  + name +  "]" ;
     }
     
     /**
      * ObjectOutputStream会调用writeExternal(ObjectOutput out))这个方法进行序列化
      * */
     public  void  writeExternal(ObjectOutput out)  throws  IOException {
         out.writeObject(name);
     }
 
     /**
      * ObjectInputStream会调用readExternal(ObjectInput in)这个方法进行反序列化
      * */
     public  void  readExternal(ObjectInput in)  throws  IOException, ClassNotFoundException {
         name = (String) in.readObject();
     }
 
}

然后进行序列化和反序列化测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package  cn.zifangsky.serializable;
 
import  java.io.File;
import  java.io.FileInputStream;
import  java.io.FileNotFoundException;
import  java.io.FileOutputStream;
import  java.io.IOException;
import  java.io.ObjectInputStream;
import  java.io.ObjectOutputStream;
 
public  class  TestExternalizable {
 
     public  static  void  main(String[] args)  throws  FileNotFoundException, IOException, ClassNotFoundException {
         ExternalizableDemo demo =  new  ExternalizableDemo( "hello" );
         // 序列化
         ObjectOutputStream objectOutputStream =  new  ObjectOutputStream(
                 new  FileOutputStream( new  File( "C:/Users/Administrator/Desktop/test2.txt" )));
         objectOutputStream.writeObject(demo);
         objectOutputStream.flush();
         objectOutputStream.close();
         
         //反序列化
         ObjectInputStream objectInputStream =  new  ObjectInputStream( new  FileInputStream( new  File( "C:/Users/Administrator/Desktop/test2.txt" )));
         ExternalizableDemo demo2 = (ExternalizableDemo) objectInputStream.readObject();
         objectInputStream.close();
         System.out.println(demo2);     
     }
 
}

输出:

1
2
3
4
调用静态代码块
调用有参构造方法
调用无参构造方法
ExternalizableDemo [name=hello]

注:

(1)使用Externalizable进行序列化时,当读取对象时,会调用被序列化类的无参构造器去创建一个新的对象,然后再将被保存对象的字段的值分别填充到新对象中

(2)Externalizable接口继承自Serializable接口。两者的区别如下:

假设有一个Customer类的对象需要序列化,如果这个类仅仅实现了这个接口,那么序列化和反序列化的方式如下:ObjectOutputStream采用默认的序列化方式,对于这个类的非static,非transient的实例变量进行序列化,ObjectInputStream采用默认的反序列化方式,对于这个类的非static,非transient的实例变量进行反序列化。

如果这个类不仅实现了Serializable接口,而且定义了readObject(ObjectInputStream in)和 writeObject(ObjectOutputStream out)方法,那么将按照如下的方式进行序列化和反序列化:

ObjectOutputStream会调用这个类的writeObject方法进行序列化,

ObjectInputStream会调用相应的readObject方法进行反序列化。

实现Externalizable接口的类完全由自身来控制序列化的行为。而且必须实现writeExternal(ObjectOutput out)和readExternal(ObjectInput in)。那么将按照如下的方式进行序列化和反序列化:

ObjectOutputStream会调用这个类的writeExternal方法进行序列化,

ObjectInputStream会调用相应的readExternal方法进行反序列化

### 序列化反序列化的基本定义 序列化是指将 Java 对象转换为可存储或传输的格式(如字节流)的过程。这一过程使得对象的状态可以被保存到文件、数据库或通过网络传输到其他系统。反序列化则是与之相对的过程,即将序列化的字节流重新转换为 Java 对象,从而恢复其原始状态结构[^1]。 ### 序列化反序列化的实现方式 在 Java 中,序列化通常通过实现 `Serializable` 接口来完成,该接口是一个标记接口,没有定义任何方法,仅用于标识类的对象可以被序列化使用 `ObjectOutputStream` 可以将对象写入输出流,从而完成序列化操作。例如: ```java try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.ser"))) { oos.writeObject(new Person("Alice", 30)); } catch (IOException e) { e.printStackTrace(); } ``` 反序列化则通过 `ObjectInputStream` 实现,它从输入流中读取字节流并将其转换回 Java 对象。例如: ```java try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.ser"))) { Person person = (Person) ois.readObject(); System.out.println(person); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } ``` 这两个过程都依赖于 Java 的对象流协议,能够自动处理对象的序列化反序列化[^3]。 ### 序列化反序列化的应用场景 序列化反序列化在分布式系统中尤为重要。例如,在远程方法调用(RMI)中,对象需要在客户端服务器之间传输,这时序列化机制确保了对象能够被正确地转换为字节流并通过网络传输,接收方则通过反序列化将字节流恢复为对象。此外,序列化还常用于对象的持久化存储,如将对象保存到文件或数据库中,以便后续恢复使用。 ### 序列化反序列化的注意事项 在使用序列化反序列化时,需要注意版本控制问题。Java 提供了 `serialVersionUID` 字段用于标识类的版本,确保在反序列化时类的结构没有发生变化。如果类的结构发生了变化(如添加或删除字段),而 `serialVersionUID` 没有相应更新,反序列化可能会失败。此外,安全性也是一个重要考虑因素,因为反序列化过程中可能会执行恶意代码,因此应确保反序列化的数据来源可信[^2]。 ### 序列化反序列化的高级应用 除了 Java 原生的序列化机制外,还可以使用第三方库如 Jackson、Gson 等进行更高效的序列化反序列化操作。这些库通常支持多种数据格式(如 JSON、XML),并且提供了更高的性能灵活性。例如,使用 Jackson 进行 JSON 到 Java 对象的反序列化: ```java ObjectMapper mapper = new ObjectMapper(); Person person = mapper.readValue(new File("person.json"), Person.class); System.out.println(person); ``` Jackson 提供了高度自动化的反序列化机制,能够根据目标类结构自动匹配 JSON 数据字段[^2]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值