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

java序列化与反序列化(2)------jdk原生序列化机制Serializable

2017-10-03 21:13 316 查看
使用jdk原生的序列化机制,我们要把需要序列化的类实现Serializable接口或者Externalizable(关于Externalizable这个接口稍后我会讲到),我们这里先说Serializable,Serializable这是一个标记接口没有声明任何的方法。

public abstract interface Serializable {
}


(1)序列化的基本操作

下面我们构造一个Book类并实现Serializable接口,代码如下:

/**
* 序列化类的模型
* @author yujie.wang
* @since 10/03/2017
*/
public class Book implements Serializable{
//序列化的版本号
private static final long serialVersionUID = 6165984730624879548L;

private String name;

private double price;

private boolean status;

private int rent_days;

private List<String> list;

// transient关键字修饰的域不被序列化
private transient int rentTimes;

// 静态域属于类,不是对象的状态
private static String note = "this is a nice book";

public Book(String name, double price, boolean status, int rent_days, List<String> list,
int rentTimes){
this.name = name;
this.price = price;
this.status = status;
this.rent_days = rent_days;
this.list = list;
this.rentTimes = rentTimes;

}

public int getRentTimes() {
return rentTimes;
}

public void setRentTimes(int rentTimes) {
this.rentTimes = rentTimes;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public double getPrice() {
return price;
}

public void setPrice(double price) {
this.price = price;
}

public boolean isStatus() {
return status;
}

public void setStatus(boolean status) {
this.status = status;
}

public int getRent_days() {
return rent_days;
}

public void setRent_days(int rent_days) {
this.rent_days = rent_days;
}

public List<String> getList() {
return list;
}

public void setList(List<String> list) {
this.list = list;
}

@Override
public String toString() {
return "Book [name=" + name + ", price=" + price + ", status=" + status
+ ", rent_days=" + rent_days + ", list=" + list
+ ", rentTimes=" + rentTimes + "]";
}

}

我们之前说过序列化是保存对象的状态,所以类中的静态变量属于类而不属于对象的状态,所以静态变量是不被序列化的。如果我们有一些不想被序列化的属性,比如程序打开的文件句柄之类的,那么我们可以在这样的域上用transient关键字进行声明。

序列化和反序列化的测试代码如下所示:

/**
* java jdk自带序列化工具测试
* @author yujie.wang
* @since 10/03/2017
*/
public class Serialize_Main {

private static FileInputStream fin;

private static FileOutputStream fout;

private static String prefix_Path = "D:\\yujie_serialize\\yujie_serialize_book";

private static String suffix_file_name = ".txt";

public static void main(String[] args) throws IOException, ClassNotFoundException {
// TODO Auto-generated method stub
System.out.println("begin to execute");
Book book = new Book("thinking in java", 99.99, true, 10, new ArrayList<String>(),15);
String filePathName = getFileFullPathName("1");
//执行序列化
serializeObject(book,filePathName );
//执行反序列化过程
deSerializeObject(filePathName);
System.out.println("end");
}

/**
* 反序列化
* @param fileName
* @throws IOException
* @throws ClassNotFoundException
*/
public static void deSerializeObject(String fileName) throws IOException, ClassNotFoundException{
File file = createFile(fileName);
if(file == null)
return;
fin = new FileInputStream(file);
ObjectInputStream ois = new ObjectInputStream(fin);
Book book = (Book)ois.readObject();
if(book == null) {
System.out.println("boo == null");
} else {
System.out.println(book.toString());
}
}

/**
* 序列化一个对象
* @param book
* @param fileName
* @throws IOException
*/
public static void serializeObject(Book book, String fileName) throws IOException{
File file = createFile(fileName);
if(file == null)
return;
fout = new FileOutputStream(file);
ObjectOutputStream oos = new ObjectOutputStream(fout);
oos.writeObject(book);
oos.flush();
oos.close();
fout.close();
}

/**
* 创建一个文件
* @param fileName
* @return
* @throws IOException
*/
public static File createFile(String fileName) throws IOException{
if(fileName == null || fileName.isEmpty())
return null;
File file = new File(fileName);
if(!file.exists()){
file.createNewFile();
System.out.println("create a file: "+ fileName);
} else {
System.out.println("file "+ fileName + " already exists");
}
return file;
}

/**
* 获得一个全路径的文件名
* @param fileName
* @return
*/
public static String getFileFullPathName(String fileName){
return prefix_Path + fileName +suffix_file_name;
}
}

测试代码输出结果如下:

begin to execute
create a file: D:\yujie_serialize\yujie_serialize_book1.txt
file D:\yujie_serialize\yujie_serialize_book1.txt already exists
Book [name=thinking in java, price=99.99, status=true, rent_days=10, list=[], rentTimes=0]
end
(2)自定义序列化方法
在上面我们说过使用transient关键字修饰的域不会被序列化,反序列化时这个域值为类型的默认值,但是如果我们就是希望将transient关键字修饰的域进行序列化,其实我们可以在需要序列化的类中自定义writeObject(ObjectOutputStream out)和readObject(ObjectInputStream in)函数,之后java序列化机制会调用这里定义的这两个函数进行序列化。

我们在Book类中增加如下代码:

private void writeObject(ObjectOutputStream out) throws IOException {
//调用默认的序列化机制 这里会正常的序列化对象的非transient修饰的域值
out.defaultWriteObject();
//调用out写入transient修饰的rentTimes域
out.writeInt(rentTimes);
System.out.println("invoke writeObject");
}

private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
//调用默认的反序列化机制 这里会正常的反序列化对象的非transient修饰的域值
in.defaultReadObject();
//调用ObjectInputStream读入我们序列化时写入的rentTimes字段
rentTimes = in.readInt();
System.out.println("invoke readObject");
}测试输出结果:
begin to execute
create a file: D:\yujie_serialize\yujie_serialize_book1.txt
invoke writeObject
file D:\yujie_serialize\yujie_serialize_book1.txt already exists
invoke readObject
Book [name=thinking in java, price=99.99, status=true, rent_days=10, list=[], rentTimes=15]
end

我们看到通过自定义函数写入的rentTimes域值,反序列化之后成功读取结果15。

(3)父子继承关系的序列化

如果父类实现了Serializable接口,子类没有实现Serializable接口,那么子类可以正常序列化。

如果子类实现了Serializable接口,而父类没有实现Serializable接口,那么必须满足如下条件子类才能正常序列化,

第一:父类必须提供一个公共的无参默认构造函数

第二:子类负责序列化父类的对象状态

我们继续改造上面的代码,接下类我们定义一个父类Paper并且实现Serializable接口代码如下:

/**
* 父类实现Serializable接口,子类直接继承父类
* 子类可直接序列化
* @author yujie.wang
*
*/
public class Paper implements Serializable{

private int length;

private int width;

public Paper(int length, int width) {
this.length = length;
this.width = width;
}

public int getLength() {
return length;
}

public void setLength(int length) {
this.length = length;
}

public int getWidth() {
return width;
}

public void setWidth(int width) {
this.width = width;
}
}

子类Book取消实现Serializable接口,直接继承Paper父类,然后运行序列化测试代码,程序正常输出如下:
begin to execute
create a file: D:\yujie_serialize\yujie_serialize_book1.txt
invoke writeObject
file D:\yujie_serialize\yujie_serialize_book1.txt already exists
invoke readObject
Book [name=thinking in java, price=99.99, status=true, rent_days=10, list=[], rentTimes=15]
end

第二种情况
父类代码改造如下:

/**
* 父类没有实现接口Seria
cf0d
lizable,父类和子类必须满足一定的条件才能正常序列化
* 子类可直接序列
* @author yujie.wang
*
*/
public class Paper{

private int length;

private int width;

//父类没有实现Serializable接口的情况下
//如果子类希望能正常序列化,则父类必须提供一个默认的无参构造函数
public Paper(){

}

public Paper(int length, int width) {
this.length = length;
this.width = width;
}

public int getLength() {
return length;
}

public void setLength(int length) {
this.length = length;
}

public int getWidth() {
return width;
}

public void setWidth(int width) {
this.width = width;
}
}

子类Book改造如下:
/**
* 序列化类的模型继承未实现Serializable接口的父类
* @author yujie.wang
* @since 10/03/2017
*/
public class Book extends Paper implements Serializable {
//序列化的版本号
private static final long serialVersionUID = 6165984730624879548L;

private String name;

private double price;

private boolean status;

private int rent_days;

private List<String> list;

// transient关键字修饰的域不被序列化
private transient int rentTimes;

// 静态域属于类,不是对象的状态
private static String note = "this is a nice book";

public Book(String name, double price, boolean status, int rent_days, List<String> list,
int rentTimes,int length,int width){
//调用父类的构造函数
super(length, width);
this.name = name;
this.price = price;
this.status = status;
this.rent_days = rent_days;
this.list = list;
this.rentTimes = rentTimes;

}

private void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
out.writeInt(rentTimes);
//序列化父类的域
out.writeInt(getLength());
out.writeInt(getWidth());
System.out.println("invoke writeObject");
}

private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
rentTimes = in.readInt();
//反序列化父类的域
setLength( in.readInt());
setWidth(in.readInt());
System.out.println("invoke readObject");
}

public static String getNote() {
return note;
}

public static void setNote(String note) {
Book.note = note;
}

public int getRentTimes() {
return rentTimes;
}

public void setRentTimes(int rentTimes) {
this.rentTimes = rentTimes;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public double getPrice() {
return price;
}

public void setPrice(double price) {
this.price = price;
}

public boolean isStatus() {
return status;
}

public void setStatus(boolean status) {
this.status = status;
}

public int getRent_days() {
return rent_days;
}

public void setRent_days(int rent_days) {
this.rent_days = rent_days;
}

public List<String> getList() {
return list;
}

public void setList(List<String> list) {
this.list = list;
}

// 重写toString方法 输出父类的域
@Override
public String toString() {
return "Book [name=" + name + ", price=" + price + ", status=" + status
+ ", rent_days=" + rent_days + ", list=" + list
+ ", rentTimes=" + rentTimes + "length"+ getLength() + "width: "+ getWidth() + "]";
}
}

测试代码中Book的构造函数传入父类的域,运行测试代码输出:
begin to execute
create a file: D:\yujie_serialize\yujie_serialize_book1.txt
invoke writeObject
file D:\yujie_serialize\yujie_serialize_book1.txt already exists
invoke readObject
Book [name=thinking in java, price=99.99, status=true, rent_days=10, list=[], rentTimes=15length20width: 29]
end

当然如果父类没有提供无参的公共构造函数,运行序列化测试代码将报如下异常:
Exception in thread "main" java.io.InvalidClassException: com.yujie.seria.Book; com.yujie.seria.Book; no valid constructor
at java.io.ObjectStreamClass.checkDeserialize(Unknown Source)
at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
at java.io.ObjectInputStream.readObject0(Unknown Source)
at java.io.ObjectInputStream.readObject(Unknown Source)
at com.yujie.seria.Serialize_Main.deSerializeObject(Serialize_Main.java:50)
at com.yujie.seria.Serialize_Main.main(Serialize_Main.java:34)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息