Java 的序列化 (Serialization) 教程
来源:互联网 发布:sqlserver日常维护 编辑:程序博客网 时间:2024/05/01 19:02
Java提供一种机制叫做序列化,通过有序的格式或者字节序列持久化java对象,其中包含对象的数据,还有对象的类型,和保存在对象中的数据类型。
所以,如果我们已经序列化了一个对象,那么它可以被读取并通过对象的类型和其他信息进行反序列化,并最终获取对象的原型。
ObjectInputStream 和 ObjectOutputStream对象是高级别的流对象,包含序列化和反序列化的方法。
ObjectOutputStream 拥有很多序列化对象的方法,最常用的是:
private
void
writeObject(ObjectOutputStream os)
throws
IOException
{
}
private
void
readObject(ObjectInputStream is)
throws
IOException, ClassNotFoundException
{
}
!
那么哪里会需要序列化呢?序列化通常在需要通过网络传输数据,或者保存对象到文件的场合使用。这里说的数据是对象而不是文本。
现在的问题是,我们的网络架构和硬盘都只能识别二进制和字节,而不能识别Java对象。
序列化就是把Java对象中的value/states翻译为字节,以便通过网络传输或者保存。另外,反序列化就是通过读取字节码,并把它翻译回java对象。
serialVersionUID概念
serialVersionUID 是用于保证同一个对象(在序列化中会被用到)可以在Deserialization过程中被载入。serialVersionUID 是用于对象的版本控制。你可以参考serialVersionUID in java serialization获取更多信息。
对于序列化:
步骤如下:
让我们看一个列子:
在 src->org.arpit.javapostsforlearning 创建Employee.java
1.Employee.java
package
org.arpit.javapostsforlearning;
import
java.io.Serializable;
public
class
Employee
implements
Serializable{
int
employeeId;
String employeeName;
String department;
public
int
getEmployeeId() {
return
employeeId;
}
public
void
setEmployeeId(
int
employeeId) {
this
.employeeId = employeeId;
}
public
String getEmployeeName() {
return
employeeName;
}
public
void
setEmployeeName(String employeeName) {
this
.employeeName = employeeName;
}
public
String getDepartment() {
return
department;
}
public
void
setDepartment(String department) {
this
.department = department;
}
}
Java中的标记接口(marker interface)就是一个没有任何字段或者方法的接口,简单的来说,java中把空接口叫做标记接口(marker interface)
package
org.arpit.javapostsforlearning;
import
java.io.FileOutputStream;
import
java.io.IOException;
import
java.io.ObjectOutputStream;
public
class
SerializeMain {
/**
* @author Arpit Mandliya
*/
public
static
void
main(String[] args) {
Employee emp =
new
Employee();
emp.setEmployeeId(
101
);
emp.setEmployeeName(
"Arpit"
);
emp.setDepartment(
"CS"
);
try
{
FileOutputStream fileOut =
new
FileOutputStream(
"employee.ser"
);
ObjectOutputStream outStream =
new
ObjectOutputStream(fileOut);
outStream.writeObject(emp);
outStream.close();
fileOut.close();
}
catch
(IOException i)
{
i.printStackTrace();
}
}
}
对于反序列化:
在包src->org.arpit.javapostsforlearning中,创建 DeserializeMain.java
package
org.arpit.javapostsforlearning;
import
java.io.IOException;
import
java.io.ObjectInputStream;
public
class
DeserializeMain {
/**
* @author Arpit Mandliya
*/
public
static
void
main(String[] args) {
Employee emp =
null
;
try
{
FileInputStream fileIn =
new
FileInputStream(
"employee.ser"
);
ObjectInputStream in =
new
ObjectInputStream(fileIn);
emp = (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(
"Emp id: "
+ emp.getEmployeeId());
System.out.println(
"Name: "
+ emp.getEmployeeName());
System.out.println(
"Department: "
+ emp.getDepartment());
}
}
4.运行:
Deserialized Employee...
Emp id:
101
Name: Arpit
Department: CS
案例1 - 如果对象引用了其他对象,那该如何
我们已经看过最简单的序列化例子,现在看看,如何处理对象中引用了其他对象的场合。我们该如何序列化?引用对象也会被序列化吗?对的,你不需要显式的序列化引用对象。当你序列化任何对象,如果它包含引用对象,那么Java序列化会自动序列化该对象的整个对象图。例如,Employee现在引用了一个address对象,并且Address也引用了其他对象(例如,Home),那么当你序列化Employee对象的时候,所有其他引用对象,例如address和home将会被自动地被序列化。让我们来创建Address类,并它Address的对象作为引用,添加到employee类中。
package
org.arpit.javapostsforlearning;
import
java.io.Serializable;
public
class
Employee
implements
Serializable{
int
employeeId;
String employeeName;
String department;
Address address;
public
int
getEmployeeId() {
return
employeeId;
}
public
void
setEmployeeId(
int
employeeId) {
this
.employeeId = employeeId;
}
public
String getEmployeeName() {
return
employeeName;
}
public
void
setEmployeeName(String employeeName) {
this
.employeeName = employeeName;
}
public
String getDepartment() {
return
department;
}
public
void
setDepartment(String department) {
this
.department = department;
}
public
Address getAddress() {
return
address;
}
public
void
setAddress(Address address) {
this
.address = address;
}
}
在 org.arpit.javapostsforlearning 包中,创建Address.java
Address.java:
package
org.arpit.javapostsforlearning;
public
class
Address {
int
homeNo;
String street;
String city;
public
Address(
int
homeNo, String street, String city) {
super
();
this
.homeNo = homeNo;
this
.street = street;
this
.city = city;
}
public
int
getHomeNo() {
return
homeNo;
}
public
void
setHomeNo(
int
homeNo) {
this
.homeNo = homeNo;
}
public
String getStreet() {
return
street;
}
public
void
setStreet(String street) {
this
.street = street;
}
public
String getCity() {
return
city;
}
public
void
setCity(String city) {
this
.city = city;
}
}
package
org.arpit.javapostsforlearning;
import
java.io.FileInputStream;
import
java.io.FileOutputStream;
import
java.io.IOException;
import
java.io.ObjectInputStream;
import
java.io.ObjectOutputStream;
public
class
SerializeDeserializeMain {
/**
* @author Arpit Mandliya
*/
public
static
void
main(String[] args) {
Employee emp =
new
Employee();
emp.setEmployeeId(
101
);
emp.setEmployeeName(
"Arpit"
);
emp.setDepartment(
"CS"
);
Address address=
new
Address(
88
,
"MG road"
,
"Pune"
);
emp.setAddress(address);
//Serialize
try
{
FileOutputStream fileOut =
new
FileOutputStream(
"employee.ser"
);
ObjectOutputStream outStream =
new
ObjectOutputStream(fileOut);
outStream.writeObject(emp);
outStream.close();
fileOut.close();
}
catch
(IOException i)
{
i.printStackTrace();
}
//Deserialize
emp =
null
;
try
{
FileInputStream fileIn =
new
FileInputStream(
"employee.ser"
);
ObjectInputStream in =
new
ObjectInputStream(fileIn);
emp = (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(
"Emp id: "
+ emp.getEmployeeId());
System.out.println(
"Name: "
+ emp.getEmployeeName());
System.out.println(
"Department: "
+ emp.getDepartment());
address=emp.getAddress();
System.out.println(
"City :"
+address.getCity());
}
}
java.io.NotSerializableException: org.arpit.javapostsforlearning.Address
at java.io.ObjectOutputStream.writeObject0(Unknown Source)
at java.io.ObjectOutputStream.defaultWriteFields(Unknown Source)
at java.io.ObjectOutputStream.writeSerialData(Unknown Source)
at java.io.ObjectOutputStream.writeOrdinaryObject(Unknown Source)
at java.io.ObjectOutputStream.writeObject0(Unknown Source)
at java.io.ObjectOutputStream.writeObject(Unknown Source)
import
java.io.Serializable;
public
class
Address
implements
Serializable{
int
homeNo;
String street;
String city;
public
Address(
int
homeNo, String street, String city) {
super
();
this
.homeNo = homeNo;
this
.street = street;
this
.city = city;
}
public
int
getHomeNo() {
return
homeNo;
}
public
void
setHomeNo(
int
homeNo) {
this
.homeNo = homeNo;
}
public
String getStreet() {
return
street;
}
public
void
setStreet(String street) {
this
.street = street;
}
public
String getCity() {
return
city;
}
public
void
setCity(String city) {
this
.city = city;
}
}
Deserialized Employee...
Emp id:
101
Name: Arpit
Department: CS
City :Pune
案例2:如果我们不能访问引用对象的源代码(例如,你不能访问上面的Address类的源码)
如果我们不能访问到address类,那么我们该如何在Address类中实现serializable 接口?是否有另外的途径来实现呢?对的,你可以创建另外一个类,并继承Address,然后让它继承serializable 接口,但是对于下面的情况,这个方案会失败:
- 如果引用类被定义为final
- 如果引用类引用了另外一个非可序列化的对象
那么,我们该如何序列化Employee对象?解决的办法是,标记transient。如果你不需要序列化任何字段,只需把它标记为transient。
transient
Address address
案例3 - 如果我仍然需要保存引用对象的状态呢?(例如address对象)
如果你在反序列化过程中,标记了address为transient,它将会返回null结果。但是如果你仍然需要保存它的状态,你就需要序列化address对象。 Java序列化提供一个机制,如果你有特定签名的private方法,那么它们就会在序列化和反序列化过程中被调用,所以我们将重写Employee类的writeObject和readObject方法,然后它们就会在Employee对象序列化/反序列化过程中被调用。
Employee.java:
package
org.arpit.javapostsforlearning;
import
java.io.IOException;
import
java.io.ObjectInputStream;
import
java.io.ObjectOutputStream;
import
java.io.Serializable;
public
class
Employee
implements
Serializable{
int
employeeId;
String employeeName;
String department;
transient
Address address;
public
int
getEmployeeId() {
return
employeeId;
}
public
void
setEmployeeId(
int
employeeId) {
this
.employeeId = employeeId;
}
public
String getEmployeeName() {
return
employeeName;
}
public
void
setEmployeeName(String employeeName) {
this
.employeeName = employeeName;
}
public
String getDepartment() {
return
department;
}
public
void
setDepartment(String department) {
this
.department = department;
}
public
Address getAddress() {
return
address;
}
public
void
setAddress(Address address) {
this
.address = address;
}
private
void
writeObject(ObjectOutputStream os)
throws
IOException, ClassNotFoundException
{
try
{
os.defaultWriteObject();
os.writeInt(address.getHomeNo());
os.writeObject(address.getStreet());
os.writeObject(address.getCity());
}
catch
(Exception e)
{ e.printStackTrace(); }
}
private
void
readObject(ObjectInputStream is)
throws
IOException, ClassNotFoundException
{
try
{
is.defaultReadObject();
int
homeNo=is.readInt();
String street=(String) is.readObject();
String city=(String) is.readObject();
address=
new
Address(homeNo,street,city);
}
catch
(Exception e) { e.printStackTrace(); }
}
}
Address.java:
package
org.arpit.javapostsforlearning;
import
java.io.Serializable;
public
class
Address {
int
homeNo;
String street;
String city;
public
Address(
int
homeNo, String street, String city) {
super
();
this
.homeNo = homeNo;
this
.street = street;
this
.city = city;
}
public
int
getHomeNo() {
return
homeNo;
}
public
void
setHomeNo(
int
homeNo) {
this
.homeNo = homeNo;
}
public
String getStreet() {
return
street;
}
public
void
setStreet(String street) {
this
.street = street;
}
public
String getCity() {
return
city;
}
public
void
setCity(String city) {
this
.city = city;
}
}
在包org.arpit.javapostsforlearning中创建SerializeDeserializeMain.java
package
org.arpit.javapostsforlearning;
import
java.io.FileInputStream;
import
java.io.FileOutputStream;
import
java.io.IOException;
import
java.io.ObjectInputStream;
import
java.io.ObjectOutputStream;
public
class
SerializeDeserializeMain {
/**
* @author Arpit Mandliya
*/
public
static
void
main(String[] args) {
Employee emp =
new
Employee();
emp.setEmployeeId(
101
);
emp.setEmployeeName(
"Arpit"
);
emp.setDepartment(
"CS"
);
Address address=
new
Address(
88
,
"MG road"
,
"Pune"
);
emp.setAddress(address);
//Serialize
try
{
FileOutputStream fileOut =
new
FileOutputStream(
"employee.ser"
);
ObjectOutputStream outStream =
new
ObjectOutputStream(fileOut);
outStream.writeObject(emp);
outStream.close();
fileOut.close();
}
catch
(IOException i)
{
i.printStackTrace();
}
//Deserialize
emp =
null
;
try
{
FileInputStream fileIn =
new
FileInputStream(
"employee.ser"
);
ObjectInputStream in =
new
ObjectInputStream(fileIn);
emp = (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(
"Emp id: "
+ emp.getEmployeeId());
System.out.println(
"Name: "
+ emp.getEmployeeName());
System.out.println(
"Department: "
+ emp.getDepartment());
address=emp.getAddress();
System.out.println(
"City :"
+address.getCity());
}
}
Deserialized Employee...
Emp id:
101
Name: Arpit
Department: CS
City :Pune
序列化中的继承:
现在我们看看继承是如何影响序列化的。不管父类是不是可序列化,这将引出很多个例子。如果父类是非可序列化的,我们将如何处理,并且它是如何工作的。让我们看看例子。
我们将创建一个Person.java,作为 Employee的父类。
案例4: 如果父类是可序列化的
如果父类可序列化,那么所有的继承类将是可序列化的。
案例5: 如果父类为非可序列化呢?
如果父类为非可序列化的 ,那么我们的处理办法会很不一样。
- 如果父类为非可序列化的,那么它必然不会有参数构造函数。
Person.java
package
org.arpit.javapostsforlearning;
public
class
Person {
String name=
"default"
;
String nationality;
public
Person()
{
System.out.println(
"Person:Constructor"
);
}
public
Person(String name, String nationality) {
super
();
this
.name = name;
this
.nationality = nationality;
}
public
String getName() {
return
name;
}
public
void
setName(String name) {
this
.name = name;
}
public
String getNationality() {
return
nationality;
}
public
void
setNationality(String nationality) {
this
.nationality = nationality;
}
}
在包org.arpit.javapostsforlearning 中创建Employee.java
Employee.java:
package
org.arpit.javapostsforlearning;
import
java.io.Serializable;
public
class
Employee
extends
Person
implements
Serializable{
int
employeeId;
String department;
public
Employee(
int
employeeId,String name,String department,String nationality)
{
super
(name,nationality);
this
.employeeId=employeeId;
this
.department=department;
System.out.println(
"Employee:Constructor"
);
}
public
int
getEmployeeId() {
return
employeeId;
}
public
void
setEmployeeId(
int
employeeId) {
this
.employeeId = employeeId;
}
public
String getDepartment() {
return
department;
}
public
void
setDepartment(String department) {
this
.department = department;
}
}
package
org.arpit.javapostsforlearning;
import
java.io.FileInputStream;
import
java.io.FileOutputStream;
import
java.io.IOException;
import
java.io.ObjectInputStream;
import
java.io.ObjectOutputStream;
public
class
SerializeDeserializeMain {
/**
* @author Arpit Mandliya
*/
public
static
void
main(String[] args) {
//Serialize
Employee emp =
new
Employee(
101
,
"Arpit"
,
"CS"
,
"Indian"
);
System.out.println(
"Before serializing"
);
System.out.println(
"Emp id: "
+ emp.getEmployeeId());
System.out.println(
"Name: "
+ emp.getName());
System.out.println(
"Department: "
+ emp.getDepartment());
System.out.println(
"Nationality: "
+ emp.getNationality());
System.out.println(
"************"
);
System.out.println(
"Serializing"
);
try
{
FileOutputStream fileOut =
new
FileOutputStream(
"employee.ser"
);
ObjectOutputStream outStream =
new
ObjectOutputStream(fileOut);
outStream.writeObject(emp);
outStream.close();
fileOut.close();
}
catch
(IOException i)
{
i.printStackTrace();
}
//Deserialize
System.out.println(
"************"
);
System.out.println(
"Deserializing"
);
emp =
null
;
try
{
FileInputStream fileIn =
new
FileInputStream(
"employee.ser"
);
ObjectInputStream in =
new
ObjectInputStream(fileIn);
emp = (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(
"After serializing"
);
System.out.println(
"Emp id: "
+ emp.getEmployeeId());
System.out.println(
"Name: "
+ emp.getName());
System.out.println(
"Department: "
+ emp.getDepartment());
System.out.println(
"Nationality: "
+ emp.getNationality());
}
}
当你运行SerializeDeserializeMain.java后,你会得到如下的输出,如果父类是非可序列化的,那么在反序列化过程中,所有继承于父类的实例变量值,将会通过调用非序列化构造函数来初始化。 这里 name继承于person,所以在反序列化过程中,name将会被初始化为默认值。
案例6 - 如果父类是可序列化,但你不需要继承类为可序列化
如果你不希望继承类为可序列化,那么你需要实现 writeObject() 和readObject() 方法,并且需要抛出NotSerializableException 异常。
案例7 - 可否序列化静态变量?
不能。因为静态变量是类级别的,不是对象级别的,当你序列化一个对象的时候,是不能序列化静态变量。
总结:
- 序列化是java对象的values/states转化为字节并在网络中传输或者保存它的过程。另外反序列化是把字节码翻译为对应的对象的过程。
- 序列化的好处是,JVM的独立性,也就是说,一个对象可以在一个平台中被序列化,然后在另外一个不同的平台反序列化。
- 如果你需要序列化任何对象,你必须实现标记接口Serializable。
- Java中的标记接口(Marker interface)就是没有字段或者方法的接口,或者更简单的说,空接口
- serialVersionUID 是用于保证同一个对象(在序列化中会被用到)可以在Deserialization过程中被载入。serialVersionUID 是用于对象的版本控制。
- 当你需要序列化任何包含引用对象的对象,那么Java会自动序列化该对象的整个对象图。
- 如果你不希望序列化某个字段,你可以标记它为trasient
- 如果父类为可序列化,那么它的继承类也将是可序列化的。
- 如果父类为非可序列化,那么在反序列化过程中,所有继承于父类的实例变量值将会通过调用非可序列化的构造器来初始化。
- 如果你需希望子类为可序列化的,那么你需要实现writeObject() 和 readObject() 方法,并在这两个方法中抛出NotSerializableException异常
- 你不能序列化静态变量。
- Java 的序列化 (Serialization) 教程
- Java 的序列化 (Serialization) 教程
- Java的序列化(Serialization)
- Java 的序列化 (Serialization)
- Java之序列化 (Serialization) 详细教程
- Java Serialization 序列化
- JAVA之序列化(Serialization)的理解
- 深入分析Java的序列化(Serialization)
- 深入分析Java的序列化(Serialization)
- java serialization--java序列化
- java中的序列化 serialization
- 初探Java序列化(Serialization)
- 序列化(Serialization)库教程
- java中对象序列化(Serialization)的注意事项
- Java Serialization/序列化/反序列化
- Java Serialization/序列化/反序列化
- Java之一:Serialization(序列化…
- Java对象序列化(Object Serialization)
- 常用MySQL操作
- forward 和redirect的区别
- Cursor工具类,用于支持根据列名取得对应的值
- Android Binder机制(超级详尽)
- zoj 3911 Prime Query (ZOJ Monthly, October 2015 - I)
- Java 的序列化 (Serialization) 教程
- 得到当前日期是星期几
- ASP.NET 5简介
- ser2net安装使用说明
- Mongod 芒果数据库初学.
- Ldom can't boot from vdisk ( Can't open disk label package )
- int 类型的自增长ID生成类,支持多线程。
- MySQL- SHOW TABLE STATUS命令
- Eclipse上配置MonkeyRunner(Windows)