Java序列化
Java提供了一种对象序列化的机制,在该机制中,一个对象可以被表示为一个字节序列,该字节序列包含对象的数据、有关对象的类的信息和存储在对象中数据的类型。
将序列化对象写入文件后,可以从文件中读取出来,对且对其进行反序列化,就是说对象的类型信息、对象的数据和对象中的数据类型都可以用来在内存中新建对象。
整个过程都是在JAVA虚拟机(JVM)独立的,也就是说,在一个平台上序列化的对象可以在另一个完全不同的平台上反序列化该对象,因为JAVA虚拟机是跨平台的。
类ObjectInputStream和ObjectOutputStream是最高层的数据流,他们包含序列化和反序列化的方法。
ObjectOutputStream类包含很多写方法来写各种数据类型,但是一个特别的方法例外:
writeObject(Object x)方法:
public final void writeObject(Object x) throws IOException
上面的方法序列化一个对象,并将它发送到输出流。相似的ObjectInputStream类包含如下反序列化一个对象的方法:
readObject()方法:
public final Object readObject() throws IOException,CalssNotFoundException
该方法从流中取出下一个对象,并将对象反序列化。它的返回值为Object,因此,需要将其转换成要用的相应的数据类型。
如何判断一个对象是否可被序列化?
若某对象是可序列化的,则肯定满足以下两个条件:
1.实例化该对象的类必须实现java.io.Serializable接口
2.实例化该对象的类的所有属性都是可序列化的,如果有一个属性不可序列化,则必须注明该属性是短暂的,例如:
public transient int SSN = 112;
则说明该属性SSN是不可序列化的,在序列化的时候该属性的值并没有传到输出流中,反序列化时,取不到值112。
为什么一个类实现了Serializable接口,就可以被序列化?当使用ObjectOutputStream来持久化对象,该类中有如下代码:
private void writeObject0(Object obj, boolean unshared) throws IOException {
...
if (obj instanceof String) {
writeString((String) obj, unshared);
} else if (cl.isArray()) {
writeArray(obj, desc, unshared);
} else if (obj instanceof Enum) {
writeEnum((Enum) obj, desc, unshared);
} else if (obj instanceof Serializable) {
writeOrdinaryObject(obj, desc, unshared);
} else {
if (extendedDebugInfo) {
throw new NotSerializableException(cl.getName() + "\n"
+ debugInfoStack.toString());
} else {
throw new NotSerializableException(cl.getName());
}
}
...}
从上述代码中可以看出,如果被写对象的类型是String、数组、Enum、或者Serializable,那么就可以对该对象进行序列化操作,否则将抛出NotSerializableException异常。
java中序列化的意义
关于序列化,常有称为持久化,将对象写入磁盘中。
进而对于编码规则来说:
任意一个实体类必须要实现Serializable接口,方便以后将该类持久化,或者将其用于转为字节数组,用于网络传输。
对于一个实体类,如果不想将所有的属性都进行序列化,可以通过关键字transient修饰该属性。
private transient String name
当对该类进行序列化时,会自动忽略被transient关键字修饰的属性。
实例:序列化一个实现Serializable接口的Employee类
Employee.java文件代码:
public class Employee implements java.io.Serializable
{
public String name;
public String address;
public transient int SSN;
public int number;
public void mailCheck()
{
System.out.println("Mailing a check to " + name + " " + address);
}
}
序列化类Employee对象
新建SerializeDemo.java文件,该程序执行后,就会创建一个employee.ser文件。
注意:当序列化一个对象到文件时,按照Java的标准约定时给文件一个.ser扩展名。
import java.io.*;
public class SerializeDemo
{
public static void main(String [] args)
{
Employee e = new Employee();
e.name = "Reyan Ali";
e.address = "Phokka Kuan, Ambehta Peer";
e.SSN = 11122333;
e.number = 101;
try
{
FileOutputStream fileOut = new FileOutputStream("/tmp/employee.ser");
ObjectOutputStream out = ObjectOutputStream(fileOut);
out.writeObject(e);
out.close();
fileOut.close();
System.out.printf("Serialized data is saved in /tmp/employee.ser");
}catch(IOException i)
{
i.printStackTrace();
}
}
}
反序列化对象
下面的DeserializeDemo程序实现了反序列化,/tmp/employee.ser存储了Employee对象。
import java.io.*;
public class DeserializeDemo
{
public static void main(String [] args)
{
Employee e = null;
try
{
FileInputStream fileIn = new FileInputStream("/tmp/employee.ser");
ObjectInputStream in = new ObjectInputStream(fileIn);
e = (Employee) in.readObject();
in.close();
fileIn.close();
}catch(IOException i)
{
i.printStackTrace();
return;
}catch(ClassNotFoundException c)
{
System.out.println("Employee class not found");
c.printStackTrace();
return;
}
System.out.println("Deserialized Employee...");
System.out.println("Name: " + e.name);
System.out.println("Address: " + e.address);
System.out.println("SSN: " + e.SSN);
System.out.println("Number: " + e.number);
}
}
以上程序编译运行结果如下:
Deserialized Employee...
Name: Reyan Ali
Address:Phokka Kuan, Ambehta Peer
SSN: 0
Number:101
注意:1,对于JVM反序列化对象,必须要能够找到字节码的类。如果JVM在反序列化对象的过程中找不到该类,会抛出ClassNotFoundException异常。
2.readObject()方法的返回值为Object类型的对象,需要被转换成要用的类型。
3.由于SSN不可被序列化,所以反序列化时找不到该值。