技术开发 频道

关于 Java 对象序列化您不知道的5件事

  【IT168 文档

  导读:Java 对象序列化(Java Object Serialization)在 Java 编程中是如此基本,以致很容易让人想当然。但是,和 Java 平台的很多方面一样,只要肯深入挖掘,序列化总能给予回报。在这个新系列的第一篇文章中,Ted Neward 给出 5 个需重新审视 Java 对象序列化的理由,并提供重构、加密和验证序列化数据的技巧(和代码)。

  数年前,当和一个软件团队一起用 Java 语言编写一个应用程序时,我体会到比一般程序员多知道一点关于 Java 对象序列化的知识所带来的好处。

  大约一年前,一个负责管理应用程序所有用户设置的开发人员,决定将用户设置存储在一个 Hashtable 中,然后将这个 Hashtable 序列化到磁盘,以便持久化。当用户更改设置时,便重新将 Hashtable 写到磁盘。

  这是一个优雅的、开放式的设置系统,但是,当团队决定从 Hashtable 迁移到 Java Collections 库中的 HashMap 时,这个系统便面临崩溃。

  Hashtable 和 HashMap 在磁盘上的格式是不相同、不兼容的。除非对每个持久化的用户设置运行某种类型的数据转换实用程序(极其庞大的任务),否则以后似乎只能一直用 Hashtable 作为应用程序的存储格式。

  团队感到陷入僵局,但这只是因为他们不知道关于 Java 序列化的一个重要事实:Java 序列化允许随着时间的推移而改变类型。当我向他们展示如何自动进行序列化替换后,他们终于按计划完成了向 HashMap 的转变。

  本文是本系列的第一篇文章,这个系列专门揭示关于 Java 平台的一些有用的小知识 — 这些小知识不易理解,但对于解决 Java 编程挑战迟早有用。

  将 Java 对象序列化 API 作为开端是一个不错的选择,因为它从一开始就存在于 JDK 1.1 中。本文介绍的关于序列化的 5 件事情将说服您重新审视那些标准 Java API。

  Java 序列化简介

  Java 对象序列化是 JDK 1.1 中引入的一组开创性特性之一,用于作为一种将 Java 对象的状态转换为字节数组,以便存储或传输的机制,以后,仍可以将字节数组转换回 Java 对象原有的状态。

  实际上,序列化的思想是 “冻结” 对象状态,传输对象状态(写到磁盘、通过网络传输等等),然后 “解冻” 状态,重新获得可用的 Java 对象。所有这些事情的发生有点像是魔术,这要归功于 ObjectInputStream/ObjectOutputStream 类、完全保真的元数据以及程序员愿意用 Serializable 标识接口标记他们的类,从而 “参与” 这个过程。

  清单 1 显示一个实现 Serializable 的 Person 类。

  清单 1. Serializable Person

package com.tedneward;

public class Person
    
implements java.io.Serializable
{
    
public Person(String fn, String ln, int a)
    {
        
this.firstName = fn; this.lastName = ln; this.age = a;
    }

    
public String getFirstName() { return firstName; }
    
public String getLastName() { return lastName; }
    
public int getAge() { return age; }
    
public Person getSpouse() { return spouse; }

    
public void setFirstName(String value) { firstName = value; }
    
public void setLastName(String value) { lastName = value; }
    
public void setAge(int value) { age = value; }
    
public void setSpouse(Person value) { spouse = value; }

    
public String toString()
    {
        
return "[Person: firstName=" + firstName +
            
" lastName=" + lastName +
            
" age=" + age +
            
" spouse=" + spouse.getFirstName() +
            
"]";
    }    

    
private String firstName;
    
private String lastName;
    
private int age;
    
private Person spouse;

}

  将 Person 序列化后,很容易将对象状态写到磁盘,然后重新读出它,下面的 JUnit 4 单元测试对此做了演示。

  清单 2. 对 Person 进行反序列化

public class SerTest
{
    @Test
public void serializeToDisk()
    {
        
try
        {
            com.tedneward.Person ted
= new com.tedneward.Person("Ted", "Neward", 39);
            com.tedneward.Person charl
= new com.tedneward.Person("Charlotte",
                
"Neward", 38);

            ted.setSpouse(charl); charl.setSpouse(ted);

            FileOutputStream fos
= new FileOutputStream("tempdata.ser");
            ObjectOutputStream oos
= new ObjectOutputStream(fos);
            oos.writeObject(ted);
            oos.close();
        }
        
catch (Exception ex)
        {
            fail(
"Exception thrown during test: " + ex.toString());
        }
        
        
try
        {
            FileInputStream fis
= new FileInputStream("tempdata.ser");
            ObjectInputStream ois
= new ObjectInputStream(fis);
            com.tedneward.Person ted
= (com.tedneward.Person) ois.readObject();
            ois.close();
            
            assertEquals(ted.getFirstName(),
"Ted");
            assertEquals(ted.getSpouse().getFirstName(),
"Charlotte");

            
// Clean up the file
            new File("tempdata.ser").delete();
        }
        
catch (Exception ex)
        {
            fail(
"Exception thrown during test: " + ex.toString());
        }
    }
}
0
相关文章