java serializable深入了解

一、串行化的概念和目的

1. 什么是Serialization?

串行化(Serialization)是计算机科学中的一个概念,它是指将对象存储到介质(如文件、内在缓冲区等)中或是以二进制方式通过网络传输。之后可以通过反串行化从这些连续的位数据重新构建一个与原始对象状态相同的对象,因此在特定情况下也可以说是得到一个副本,但并不是所有情况都这样。

2. 为什么要Serilzation?

特别地,串行化主要有三种用途:

1)作为一种持久化机制

如果使用的是FileOutputStream流的方式,则数据将被自动地写入文件中,

2)作为一种复制机制

如果使用的是ByteArrayOutputStream流的方式,数据将写入内存中的字节数组中。该字节数组可以用来创建初始对象的副本,

3)作为一种通信机制

如果是使用套接字(Socket)流的方式,则数据自动地通过网络连接传输一另一个端点,并由这个端点上的程序来决定做什么。

二、串行化方法

从JDK1.1开始,Java语言提供了对象串行化机制 ,在java.io包中,接口Serialization用来作为实现对象串行化的工具 ,只有实现了Serialization的类的对象才可以被串行化。

Serializable接口中没有任何的方法。当一个类声明要实现Serializable接口时,只是表明该类参加串行化协议,而不需要实现任何特殊的方法。下面我们通过实例介绍如何对对象进行串行化。

1.定义一个可串行化对象

一个类,如果要使其对象可以被串行化,必须实现Serializable接口。我们定义一个类Student如下:

[java] view plaincopy

  1. import java.io.Serializable;
  2. public class Student implements Serializable {
  3. int id;// 学号
  4. String name;// 姓名
  5. int age;// 年龄
  6. String department; // 系别
  7. public Student(int id, String name, int age, String department) {
  8. this.id = id;
  9. this.name = name;
  10. this.age = age;
  11. this.department = department;
  12. }
  13. }

2.构造对象的输入/输出流

要串行化一个对象,必须与一定的对象输出/输入流联系起来,通过对象输出流将对象状态保存下来,再通过对象输入流将对象状态恢复。

java.io包中,提供了ObjectInputStream和ObjectOutputStream将数据流功能扩展至可读写对象 。在ObjectInputStream 中用readObject()方法可以直接读取一个对象,ObjectOutputStream中用writeObject()方法可以直接将对象保存到输出流中。

[java] view plaincopy

  1. import java.io.FileInputStream;
  2. import java.io.FileOutputStream;
  3. import java.io.IOException;
  4. import java.io.ObjectInputStream;
  5. import java.io.ObjectOutputStream;
  6. public class ObjectSer {
  7. public static void main(String args[]) throws IOException,
  8. ClassNotFoundException {
  9. Student stu = new Student(981036, "LiuMing", 18, "CSD");
  10. FileOutputStream fo = new FileOutputStream("data.ser");
  11. ObjectOutputStream so = new ObjectOutputStream(fo);
  12. try {
  13. so.writeObject(stu);
  14. so.close();
  15. } catch (IOException e) {
  16. System.out.println(e);
  17. }
  18. stu = null;
  19. FileInputStream fi = new FileInputStream("data.ser");
  20. ObjectInputStream si = new ObjectInputStream(fi);
  21. try {
  22. stu = (Student) si.readObject();
  23. si.close();
  24. } catch (IOException e)
  25. {
  26. System.out.println(e);
  27. }
  28. System.out.println("Student Info:");
  29. System.out.println("ID:" + stu.id);
  30. System.out.println("Name:" + stu.name);
  31. System.out.println("Age:" + stu.age);
  32. System.out.println("Dep:" + stu.department);
  33. }
  34. }
  35. 运行结果如下:
  36. Student Info:
  37.   ID:981036
  38.   Name:LiuMing
  39.   Age:18
  40.   Dep:CSD

在这个例子中,我们首先定义了一个类Student,实现了Serializable接口 ,然后通过对象输出流的writeObject()方法将Student对象保存到文件 data.ser中 。之后,通过对家输入流的readObjcet()方法从文件data.ser中读出保存下来的Student对象 。从运行结果可以看到,通过串行化机制,可以正确地保存和恢复对象的状态。

三、串行化的注意事项

1.串行化能保存的元素

串行化只能保存对象的非静态成员交量,不能保存任何的成员方法和静态的成员变量,而且串行化保存的只是变量的值,对于变量的任何修饰符都不能保存。

2.transient关键字

对于某些类型的对象,其状态是瞬时的,这样的对象是无法保存其状态的。例如一个Thread对象或一个FileInputStream对象 ,对于这些字段,我们必须用transient关键字标明,否则编译器将报措。

另外 ,串行化可能涉及将对象存放到 磁盘上或在网络上发达数据,这时候就会产生安全问题。因为数据位于Java运行环境之外,不在Java安全机制的控制之中。对于这些需要保密的字段,不应保存在永久介质中 ,或者不应简单地不加处理地保存下来 ,为了保证安全性。应该在这些字段前加上transient关键字。

下面是java规范中对transient关键字的解释:

The   transient   marker   is   not   fully   specified   by   The   Java   Language     Specification   but   is   used   in   object   serialization   to   mark   member   variables   that   should   not   be   serialized.

以下是transient的一个应用举例:

[java] view plaincopy

  1. //LoggingInfo.java
  2. import java.io.FileInputStream;
  3. import java.io.FileOutputStream;
  4. import java.io.ObjectInputStream;
  5. import java.io.ObjectOutputStream;
  6. import java.util.Date;
  7. public class LoggingInfo implements java.io.Serializable {
  8. private static final long serialVersionUID = 1L;
  9. private Date loggingDate = new Date();
  10. private String uid;
  11. private transient String pwd;
  12. LoggingInfo(String user, String password) {
  13. uid = user;
  14. pwd = password;
  15. }
  16. public String toString() {
  17. String password = null;
  18. if (pwd == null) {
  19. password = "NOT SET";
  20. } else {
  21. password = pwd;
  22. }
  23. return "logon info: \n   " + "user: " + uid + "\n   logging date : "
  24. + loggingDate.toString() + "\n   password: " + password;
  25. }
  26. public static void main(String[] args) {
  27. LoggingInfo logInfo = new LoggingInfo("MIKE", "MECHANICS");
  28. System.out.println(logInfo.toString());
  29. try {
  30. ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream(
  31. "logInfo.out"));
  32. o.writeObject(logInfo);
  33. o.close();
  34. } catch (Exception e) {// deal with exception
  35. }
  36. // To read the object back, we can write
  37. try {
  38. ObjectInputStream in = new ObjectInputStream(new FileInputStream(
  39. "logInfo.out"));
  40. LoggingInfo logInfo1 = (LoggingInfo) in.readObject();
  41. System.out.println(logInfo1.toString());
  42. } catch (Exception e) {// deal with exception
  43. }
  44. }
  45. }

总结:

序列化分为两大部分:序列化和反序列化。序列化是这个过程的第一部分,将数据分解成字节流,以便存储在文件中或在网络上传输。反序列化就是打开字节流并重构对象。对象序列化不仅要将基本数据类型转换成字节表示,有时还要恢复数据。恢复数据要求有恢复数据的对象实例。ObjectOutputStream中的序列化过程与字节流连接,包括对象类型和版本信息。反序列化时,JVM用头信息生成对象实例,然后将对象字节流中的数据复制到对象数据成员中

序列化的过程就是对象写入字节流和从字节流中读取对象。将对象状态转换成字节流之后,可以用java.io包中的各种字节流类将其保存到文件中,管道到另一线程中或通过网络连接将对象数据发送到另一主机。对象序列化功能非常简单、强大,在RMI、Socket、JMS、EJB都有应用。对象序列化问题在网络编程中并不是最激动人心的课题,但却相当重要,具有许多实用意义。

  对象序列化可以实现分布式对象。主要应用例如:RMI要利用对象序列化运行远程主机上的服务,就像在本地机上运行对象时一样。

  java对象序列化不仅保留一个对象的数据,而且递归保存对象引用的每个对象的数据。可以将整个对象层次写入字节流中,可以保存在文件中或在网络连接上传递。利用对象序列化可以进行对象的“深复制”,即复制对象本身及引用的对象本身。序列化一个对象可能得到整个对象序列。

最后列举一些经常遇到的一些真实情境,它们与 Java 序列化相关,通过分析情境出现的原因,使读者轻松牢记 Java 序列化中的一些高级认识。

序列化 ID 问题

情境:两个客户端 A 和 B 试图通过网络传递对象数据,A 端将对象 C 序列化为二进制数据再传给 B,B 反序列化得到 C。

问题:C 对象的全类路径假设为 com.inout.Test,在 A 和 B 端都有这么一个类文件,功能代码完全一致。也都实现了 Serializable 接口,但是反序列化时总是提示不成功。

解决:虚拟机是否允许反序列化,不仅取决于类路径和功能代码是否一致,一个非常重要的一点是两个类的序列化 ID 是否一致(就是 private static final long serialVersionUID = 1L)。两段相同代码中,虽然两个类的功能代码完全一致,但是序列化 ID 不同,他们无法相互序列化和反序列化。

序列化 ID 在 Eclipse 下提供了两种生成策略,一个是固定的 1L,一个是随机生成一个不重复的 long 类型数据(实际上是使用 JDK 工具生成),在这里有一个建议,如果没有特殊需求,就是用默认的 1L 就可以,这样可以确保代码一致时反序列化成功。那么随机生成的序列化 ID 有什么作用呢,有些时候,通过改变序列化 ID 可以用来限制某些用户的使用。

对敏感字段加密

情境:服务器端给客户端发送序列化对象数据,对象中有一些数据是敏感的,比如密码字符串等,希望对该密码字段在序列化时,进行加密,而客户端如果拥有解密的密钥,只有在客户端进行反序列化时,才可以对密码进行读取,这样可以一定程度保证序列化对象的数据安全。

解决:在序列化过程中,虚拟机会试图调用对象类里的 writeObject 和 readObject 方法,进行用户自定义的序列化和反序列化,如果没有这样的方法,则默认调用是 ObjectOutputStream 的 defaultWriteObject 方法以及 ObjectInputStream 的 defaultReadObject 方法。用户自定义的 writeObject 和 readObject 方法可以允许用户控制序列化的过程,比如可以在序列化的过程中动态改变序列化的数值。基于这个原理,可以在实际应用中得到使用,用于敏感字段的加密工作,清单 3 展示了这个过程。

[java] view plaincopy

    1. 清单 3. 静态变量序列化问题代码
    2. private static final long serialVersionUID = 1L;
    3. private String password = "pass";
    4. public String getPassword() {
    5. return password;
    6. }
    7. public void setPassword(String password) {
    8. this.password = password;
    9. }
    10. private void writeObject(ObjectOutputStream out) {
    11. try {
    12. PutField putFields = out.putFields();
    13. System.out.println("原密码:" + password);
    14. password = "encryption";//模拟加密
    15. putFields.put("password", password);
    16. System.out.println("加密后的密码" + password);
    17. out.writeFields();
    18. } catch (IOException e) {
    19. e.printStackTrace();
    20. }
    21. }
    22. private void readObject(ObjectInputStream in) {
    23. try {
    24. GetField readFields = in.readFields();
    25. Object object = readFields.get("password", "");
    26. System.out.println("要解密的字符串:" + object.toString());
    27. password = "pass";//模拟解密,需要获得本地的密钥
    28. } catch (IOException e) {
    29. e.printStackTrace();
    30. } catch (ClassNotFoundException e) {
    31. e.printStackTrace();
    32. }
    33. }
    34. public static void main(String[] args) {
    35. try {
    36. ObjectOutputStream out = new ObjectOutputStream(
    37. new FileOutputStream("result.obj"));
    38. out.writeObject(new Test());
    39. out.close();
    40. ObjectInputStream oin = new ObjectInputStream(new FileInputStream(
    41. "result.obj"));
    42. Test t = (Test) oin.readObject();
    43. System.out.println("解密后的字符串:" + t.getPassword());
    44. oin.close();
    45. } catch (FileNotFoundException e) {
    46. e.printStackTrace();
    47. } catch (IOException e) {
    48. e.printStackTrace();
    49. } catch (ClassNotFoundException e) {
    50. e.printStackTrace();
    51. }
    52. }
    53. 在清单 3 的 writeObject 方法中,对密码进行了加密,在 readObject 中则对 password 进行解密,只有拥有密钥的客户端,才可以正确的解析出密码,确保了数据的安全。
时间: 2024-08-03 20:34:25

java serializable深入了解的相关文章

Java Serializable(序列化)的理解和总结、具体实现过程(转)

原文地址:http://www.apkbus.com/forum.php?mod=viewthread&tid=13576&fromuid=3402 Java Serializable(序列化)的理解和总结.具体实现过程 内存中的对象是怎么存在的?  内存中各种对象的状态是?  实例变量是什么(指的是实例化的对象吗)?    使用序列化的好处是什么? 1.序列化是干什么的?        简单说就是为了保存在内存中的各种对象的状态(也就是实例变量,不是方法),并且可以把保存的对象状态再读出

对Java Serializable(序列化)的理解和总结(二)

遇到这个 Java Serializable 序列化这个接口,我们可能会有如下的问题a,什么叫序列化和反序列化 b,作用.为啥要实现这个 Serializable 接口,也就是为啥要序列化 c,serialVersionUID 这个的值到底是在怎么设置的,有什么用.有的是1L,有的是一长串数字,迷惑ing. 我刚刚见到这个关键字 Serializable 的时候,就有如上的这么些问题. 在处理这个问题之前,你要先知道一个问题,这个比较重要. 这个Serializable接口,以及相关的东西,全部

Java学习之——Java Serializable

1.什么是Serializable接口? http://en.wikipedia.org/wiki/Serialization Java 提供了一种对象序列化的机制,该机制中,一个对象可以被表示为一个字节序列,该字节序列包括该对象的数据.有关对象的类型的信息和存储在对象中数据的类型. 将序列化对象写入文件之后,可以从文件中读取出来,并且对它进行反序列化,也就是说,对象的类型信息.对象的数据,还有对象中的数据类型可以用来在内存中新建对象. 整个过程都是Java虚拟机(JVM)独立的,也就是说,在一

我对Java Serializable(序列化)的理解和总结--转

1.序列化是干什么的?       简单说就是为了保存在内存中的各种对象的状态(也就是实例变量,不是方法),并且可以把保存的对象状态再读出来.虽然你可以用你自己的各种各样的方法来保存object states,但是Java给你提供一种应该比你自己好的保存对象状态的机制,那就是序列化. 2.什么情况下需要序列化       a)当你想把的内存中的对象状态保存到一个文件中或者数据库中时候:    b)当你想用套接字在网络上传送对象的时候:    c)当你想通过RMI传输对象的时候: package

java Serializable和Externalizable序列化反序列化详解--转

一.什么是序列化? “对象序列化”(Object Serialization)是 Java1.1就开始有的特性. 简单地说,就是可以将一个对象(标志对象的类型)及其状态转换为字节码,保存起来(可以保存在数据库,内存,文件等),然后可以在适当的时候再将其状态恢复(也就是反序列化).serialization 不但可以在本机做,而且可以经由网络操作.它自动屏蔽了操作系统的差异,字节顺序等.比如,在 Windows 平台生成一个对象并序列化之,然后通过网络传到一台 Unix 机器上,然后可以在这台Un

java Serializable

package com.zyw.serializablebean; 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 Ser

每天进步一点点-Java Serializable(对象序列化)的理解和总结

往硬盘文件里写数据 序列化:序列化是将对象转换为容易传输的格式的过程.例如,可以序列化一个对象,然后使用 HTTP 通过 Internet 在客户端和服务器之间传输该对象.在另一端,反序列化将从该流重新构造对象. 是对象永久化的一种机制. 确切的说应该是对象的序列化,一般程序在运行时,产生对象,这些对象随着程序的停止运行而消失,但如果我们想把某些对象(因为是对象,所以有各自不同的特性)保存下来,在程序终止运行后,这些对象仍然存在,可以在程序再次运行时读取这些对象的值,或者在其他程序中利用这些保存

Java Serializable接口(序列化)理解及自定义序列化

  1 Serializable接口 (1)简单地说,就是可以将一个对象(标志对象的类型)及其状态转换为字节码,保存起来(可以保存在数据库,内存,文件等),然后可以在适当的时候再将其状态恢复(也就是反序列化).serialization 不但可以在本机做,而且可以经由网络操作.它自动屏蔽了操作系统的差异,字节顺序等.比如,在 Windows 平台生成一个对象并序列化之,然后通过网络传到一台 Unix 机器上,然后可以在这台Unix机器上正确地重构(deserialization)这个对象. 不必

Java Serializable接口实现问题

JavaBeans 要了解Serializable接口之前先来了解最基本的JavaBeans.JavaBeans为我们提供了最简单的Java类实体,这些实体伴随着业务在应用中来回穿梭.JavaBeans是Java中一种特殊的类,可以将多个对象封装到一个对象(bean)中.特点是可序列化,提供无参构造器,提供getter方法和setter方法访问对象的属性.名称中的"Bean"是用于Java的可重用软件组件的惯用叫法. JavaBeans优点: (1) Bean可以控制它的属性.事件和方