您的位置:首页 > 编程语言 > Java开发

Java 对象序列化与反序列化知识点汇总

2013-08-20 19:28 429 查看
转自: http://lapulande.iteye.com/blog/721633

 

一、两个概念,何为序列化?何为反序列化?

 

序列化:将对象转化成流的过程称为序列化。

反序列化:将流转化成对象的过程称之为反序列化。

 

二、序列化机制一般用途?

  1.需要将对象的状态保存到文件中,而后能够通过读入对象状态来重新构造对象,恢复程序状态

  2.使用套接字在网络上传送对象的程序来说,是很有用的。

  3.通过序列化在进程间传递对象。

 

三、Java对象序列化究竟保存的是什么?

Java的序列化机制只序列化对象的属性值,而不会去序列化什么所谓的方法。其实这个问题简单思考一下就可以搞清楚,方法是不带状态的,就是一些指令,指令是不需要序列化的,只要你的JVM classloader可以load到这个类,那么类方法指令自然就可以获得。序列化真正需要保存的只是:对象的类型,对象属性的类型,对象属性的值。

 

四、序列化和反序列化的操作

 

a) Java对象和java.io.Serializable接口

 

在java中要想使一个java对象可以实现序列化与反序列化,必须让该类实现java.io.Serializable接口,它并未指定要实现的方法,所以Serializable被称为"tagging interface" ,但是它仅仅"tags"它自己的对象是一个特殊的类型。任一个您希望serialize的对象都应该实现这个接口。这是必须的。否则,用到流技术时将根本不工作。例如,如果您试着去serialize 一个没有实现这个接口的对象,一个 NotSerializableException将产生。 类通过实现
java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。可序列化类的所有子类型本身都是可序列化的。序列化接口没有方法或字段,仅用于标识可序列化的语义。

java.io.Serializable接口定义如下:

public interface Serializable {

}

 

b) 序列化和反序列化

 

序列化主要依赖java.io.ObjectOutputStream类,该类对java.io.FileOutputStream进一步做了封装,这里主要使用ObjectOutputStream类的writeObject()方法实现序列化功能。

反序列化主要依赖java.io.ObjectInputStream类,这里主要使用ObjectInputStream类的readObject()方法实现序列化功能。

在序列化的时候,writeObject与 readObject之间的先后顺序。readObject将最先write的object read出来。用数据结构的术语来讲就姑且称之为先进先出吧!

在序列化时,有几点要注意的:

  1:当一个对象被序列化时,只保存对象的非静态成员变量,不能保存任何的成员方法和静态的成员变量。

  2:如果一个对象的成员变量是一个对象,那么这个对象的数据成员也会被保存。

  3:如果一个可序列化的对象包含对某个不可序列化的对象的引用,那么整个序列化操作将会失败,并且会抛出一个NotSerializableException。我们可以将这个引用标记为transient,那么对象仍然可以序列化。

 

还有我们对某个对象进行序列化时候,往往对整个对象全部序列化了,比如说对象里有些数据不希望序列化,一个方法可以用transient来标识,另一个方法我们可以在这个类里重写二个方法

  private    void  readObject(java.io.ObjectInputStream stream)

      throws  IOException, ClassNotFoundException;

  private   void  writeObject(java.io.ObjectOutputStream stream)

      throws  IOException

 

Java代码


 





class  ObjectSerialTest   
{   
     public   static   void  main(String[] args)  throws  Exception   
     {   
        Employee e1 = new  Employee( " zhangsan " , 25 , 3000.50 );   
        Employee e2 = new  Employee( " lisi " , 24 , 3200.40 );   
        Employee e3 = new  Employee( " wangwu " , 27 , 3800.55 );   
           
        FileOutputStream fos = new  FileOutputStream( " employee.txt " );   
        ObjectOutputStream oos = new  ObjectOutputStream(fos);   
        oos.writeObject(e1);   
        oos.writeObject(e2);   
        oos.writeObject(e3);   
        oos.close();   
           
        FileInputStream fis = new  FileInputStream( " employee.txt " );   
        ObjectInputStream ois = new  ObjectInputStream(fis);   
        Employee e;   
         for ( int  i = 0 ;i < 3 ;i ++ )   
         {   
            e = (Employee)ois.readObject();   
            System.out.println(e.name + " : " + e.age + " : " + e.salary);   
        }   
        ois.close();   
    }   
}   
  
class  Employee  implements  Serializable   
{   
    String name;   
     int  age;   
     double  salary;   
     transient  Thread t = new  Thread();   
     public  Employee(String name, int  age, double  salary)   
     {   
         this .name = name;   
         this .age = age;   
         this .salary = salary;   
    }   
     private   void  writeObject(java.io.ObjectOutputStream oos)  throws  IOException   
     {   
        oos.writeInt(age);   
        oos.writeUTF(name);   
        System.out.println( " Write Object " );   
    }   
     private   void  readObject(java.io.ObjectInputStream ois)  throws  IOException   
     {   
        age = ois.readInt();   
        name = ois.readUTF();   
        System.out.println( " Read Object " );   
    }   
  
}   

class  ObjectSerialTest
{
public   static   void  main(String[] args)  throws  Exception
{
Employee e1 = new  Employee( " zhangsan " , 25 , 3000.50 );
Employee e2 = new  Employee( " lisi " , 24 , 3200.40 );
Employee e3 = new  Employee( " wangwu " , 27 , 3800.55 );

FileOutputStream fos = new  FileOutputStream( " employee.txt " );
ObjectOutputStream oos = new  ObjectOutputStream(fos);
oos.writeObject(e1);
oos.writeObject(e2);
oos.writeObject(e3);
oos.close();

FileInputStream fis = new  FileInputStream( " employee.txt " );
ObjectInputStream ois = new  ObjectInputStream(fis);
Employee e;
for ( int  i = 0 ;i < 3 ;i ++ )
{
e = (Employee)ois.readObject();
System.out.println(e.name + " : " + e.age + " : " + e.salary);
}
ois.close();
}
}

class  Employee  implements  Serializable
{
String name;
int  age;
double  salary;
transient  Thread t = new  Thread();
public  Employee(String name, int  age, double  salary)
{
this .name = name;
this .age = age;
this .salary = salary;
}
private   void  writeObject(java.io.ObjectOutputStream oos)  throws  IOException
{
oos.writeInt(age);
oos.writeUTF(name);
System.out.println( " Write Object " );
}
private   void  readObject(java.io.ObjectInputStream ois)  throws  IOException
{
age = ois.readInt();
name = ois.readUTF();
System.out.println( " Read Object " );
}

}
 
其实还有一个接口java.io.Externalizable,它继承了Serializable接口,所以也可以通过实现它,定义readExternal和writeExternal方法来实现序列化。

ObjectOutputStream调用类的writeExternal方法对Customer对象的非transient实例变量进行序列化;

ObjectInputStream首先通过类的无参数构造函数实例化一个对象,再用readExternal方法对对象的非transient实例变量进行反序列化。

 

五、总结

如果采用默认的序列化方式,只要让一个类实现Serializable接口,其实例就可以被序列化。通常,专门为继承而设计的类应该尽量不要实现 Serializable接口,因为一旦父类实现了Serializable接口,其所有子类也都是可序列化的了。

默认的序列化方式的不足之处:

1.直接对对象的不宜对外公开的敏感数据进行序列化,这是不安全的;

2.不会检查对象的成员变量是否符合正确的约束条件,有可能被传改数据而导致运行异常;

3.需要对对象图做递归遍历,如果对象图很复杂,会消耗很多资源,设置引起Java虚拟机的堆栈溢出;

4.使类的接口被类的内部实现约束,制约类的升级与维护。

通过实现Serializable接口的private类型的writeObject()和readObject(),或是实现 Externalizable接口,并实现writeExternal()与readExternal()方法,并提供public类型的无参数构造函数两种方式来控制序列化过程可以有效规避默认序列化方式的不足之处。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: