一、对象序列化的作用
Object serialization 允许你将实现了Serializable接口的对象转换为字节序列,这些字节序列可以被完全存储以备以后重新生成原来的对象。
在Java语言中允许使用java.io.Serializable接口将类标志为可序列化,从而可以将该类的对象通过I/O传递(如:通过网络传递)或者将对象写入到文件系统中。然而在java.io.Serializable中并没有定义任何的操作。那么Serializable接口究竟起到什么样的作用呢?
Java语言为程序开发者提供了许多的安全检查,从而保证java程序在语言级别以及运行环境上的稳定、可靠,从而使开发人员能把主要经历集中在软件设计上。然而,一个功能相对复杂的软件大都会应用到网络传输或者本地数据的持久化(将对象写到文件中),而这些操作都在一定程度上破坏了JAVA程序的安全性,因为网络传输与文件系统都是不受控的环境,在这样的环境中,用户的数据很容易就遭到攻击或者破坏。比如:改写或者破坏本地文件系统简直是轻而易举的事情。
在保障JAVA程序的安全又要保证程序功能与用户需求的情况下,需要介入一部分人为因素,来保证程序的安全。Serializable接口的使用其实是在程序开发人员或者设计者在做开发和设计的时候对JAVA运行环境的一种声明,用来说明程序中需要传递或者保存到文件系统中的对象数据程序开发者认为是在不受控的环境中安全的。通过这样的标识,来告诉JVM可以对对象进行网络传输或者持久化,从而来穿越JAVA的安全管理层。
总结:对于将JAVA程序中的对象数据进行网络传输或者本地持久化,给程序开发者带来了很大的方便,但是在某种意义上,对象序列化确实又引发了一定的安全问题。那么这就要求我们在考虑在准备将一些对象数据进行序列化的时候,进行安全的处理,从而保护敏感数据。
二、对象序列化的概念
所谓的对象序列化就是指将一个在内存中保存的对象变成一个二进制的数据流进行传输。
Object serialization 允许你将实现了Serializable接口的对象转换为字节序列,这些字节序列可以被完全存储以备以后重新生成原来的对象。反序列化就是打开字节流并重构对象。但并不是所有类的对象都可以进行序列化的操作,如果一个对象需要被序列化,则对象所在的类必须实现Serializable接口。但是此接口中没有任何的方法定义,所以此接口和Cloneable接口是完全一样的,都是作为标识接口出现。
import java.io.Serializable; @SuppressWarnings("serial") public class Person implements Serializable { private String name; private int age; public Person(String name,int age) { this.name = name; this.age = age; } @Override public String toString() { return"姓名:" +this.name +",年龄:" +this.age; } }
此时,Person类的对象已经允许被序列化操作了,即变为二进制的byte流。
如果要想序列化,需要使用ObjectOutputStream类完成。如果需要反序列化,则需要ObjectInputStream类完成。
三、序列化对象
ObjectOutputStream主要是序列化对象的使用,也是一个OutputStream的子类,方法:
·构造:public ObjectOutputStream(OutputStream out) throwsIOException
·输出对象:public final void writeObject(Object obj) throwsIOException
import java.io.File; import java.io.FileOutputStream; import java.io.ObjectOutputStream; public class ObjectOutputStreamDemo { public static void main(String[] args)throws Exception { Person per = new Person("张三", 30); ObjectOutputStream oos =new ObjectOutputStream(new FileOutputStream(new File("D:" + File.separator + "person.ini"))); oos.writeObject(per); // 序列化对象 oos.close(); } }
在没有序列化前,每个保存在堆(Heap)中的对象都有相应的状态(state),即实例变量(instance ariable)比如:Person per = new Person("张三", 30);当序列化之后,per对象中的Name和age实例变量的值("张三",30)都被保存到文件中,这样以后又可以把它从文件中读出来,重新在堆中创建原来的对象。当然保存时候不仅仅是保存对象的实例变量的值,JVM还要保存一些小量信息,比如类的类型等以便恢复原来的对象。
四、反序列化对象
ObjectInputStream就可以完成对象的反序列化的功能,方法如下:
·构造:public ObjectInputStream(InputStream in) throwsIOException
·读取对象:public final Object readObject() throwsIOException,ClassNotFoundException
import java.io.File; import java.io.FileInputStream; import java.io.ObjectInputStream; public class ObjectInputStreamDemo { public static void main(String[] args)throws Exception { ObjectInputStream ois = new ObjectInputStream(new FileInputStream( new File("D:" + File.separator + "person.ini"))); Object obj = ois.readObject(); ois.close(); if (obj instanceof Person) { Person per = (Person) obj; System.out.println(per);//姓名:张三,年龄:30 } } }
五、transient关键字
实际上在进行对象序列化的时候,序列化的是类中的属性,因为每个类的对象只有属性是不同的。
但是如果现在有某个属性不希望被序列化下来的话,则可以使用transient关键字。
private transient String name; private int age;
如下只将age属性序列化,这上面两个例子的结果就变为:
而反序列化后的结果为:姓名:null,年龄:30
六、序列化一组对象
在正常情况下ObjectOutputStream只能输出一个对象,如果现在有多个对象要同时进行序列化的话,则可以使用对象数组的形式完成,对象数组可以直接使用Object接收。
import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; public class ObjectArrayDemo { public static void main(String[] args) throws Exception { ser(new Person[] { new Person("张三", 20), new Person("李四", 21), new Person("王五", 22) }); Person per[] = (Person[]) dser(); for (int x = 0; x < per.length; x++) { System.out.println(per[x]); } } public static void ser(Object obj) throws Exception { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream( new File("D:" + File.separator + "person.ini"))); oos.writeObject(obj); oos.close(); } public static Object dser() throws Exception { ObjectInputStream ois = new ObjectInputStream(new FileInputStream( new File("D:" + File.separator + "person.ini"))); Object obj = ois.readObject(); ois.close(); return obj; } }
七、什么情况下需要序列化
a)当你想把的内存中的对象保存到一个文件中或者数据库中时候;
b)当你想用套接字在网络上传送对象的时候;
c)当你想通过RMI(Remote Method Invocation,远程方法调用)传输对象的时候;
八、序列化的特点
1、如果某个类能够被序列化,其子类也可以被序列化。
2、声明为static和transient类型的成员数据不能被序列化。因为static代表类的状态
参考文章http://blog.csdn.net/sole_ghost/article/details/1564981
来源:https://www.cnblogs.com/xqzt/archive/2012/10/29/5637151.html