externalizable java_Java—Externalizable

1、什么是-Java序列化

Java序列化是指把Java对象转换为字节序列的过程,Java反序列化是指把字节序列恢复为Java对象的过程。通过序列化和反序列化实现网络传输、本地存储的目的。

1.1 Serializable实现Java序列化

要实现Java对象的序列化,只要将类实现标识接口——Serializable接口即可,不需要我们重写任何方法就可以实现序列化。

案例:Java实现Serializable接口进行序列化案例。

1.1.1 编写实体类

package com.kkb.serialize;

import java.io.Serializable;

import java.util.List;

/*

* 要进行序列化的实体类

*/

public class Student implements Serializable{

/**

* 学号

*/

private String stuNum;

/**

* 学生姓名

*/

private String stuName;

/**

* 教师姓名:一个学生可以多个老师

*/

private List teacherList;

//无参构造方法

public Student(){

}

//全参数构造方法

public Student(String stuNum, String stuName, List teacherList){

this.stuNum = stuNum;

this.stuName = stuName;

this.teacherList = teacherList;

}

@Override

public String toString(){

return "Student{" +

"stuNum='" + stuNum + '\'' +

", stuName='" + stuName + '\'' +

", teacherList=" + teacherList +

'}';

}

public String getStuNum(){

return stuNum;

}

public void setStuNum(String stuNum){

this.stuNum = stuNum;

}

public String getStuName(){

return stuName;

}

public void setStuName(String stuName){

this.stuName = stuName;

}

public List getTeacherList(){

return teacherList;

}

public void setTeacherList(List teacherList){

this.teacherList = teacherList;

}

}

1.1.2 编写Java对象序列化和反序列化工具类

package com.kkb.com.kkb.util;

import java.io.*;

/**

* Java对象序列化和反序列化工具类

*/

public class MySerializeUtil{

/**

* 从指定的文件中反序列化对象

* @param fileName

* @return

* @throws IOException

* @throws ClassNotFoundException

*/

public static Object myDeserialize(String fileName) throws IOException, ClassNotFoundException{

InputStream in=new FileInputStream(fileName);

ObjectInputStream objIn=new ObjectInputStream(in);

Object obj = objIn.readObject();

objIn.close();

return obj;

}

/**

* 将对象序列化到指定文件

* @param obj

* @param fileName

* @throws IOException

*/

public static void mySerialize(Object obj,String fileName) throws IOException{

OutputStream out=new FileOutputStream(fileName);

ObjectOutputStream objOut=new ObjectOutputStream(out);

objOut.writeObject(obj);

objOut.close();

}

}

1.1.3 测试对象的序列化和反序列化

public class TestSerialize{

@Test

public void test1(){

List teacherList=new ArrayList<>();

teacherList.add("空空道人");

teacherList.add("贾代儒");

Student stu1=new Student("1001", "贾宝玉", teacherList);

System.out.println("原始对象:"+stu1);

String fileName="seria01.txt";

try {

//对象序列化

MySerializeUtil.mySerialize(stu1, fileName);

System.out.println("序列化原始对象完成!OK!");

//对象的反序列化

Student student= (Student) MySerializeUtil.myDeserialize(fileName);

System.out.println("反序列化之后的对象:"+student);

} catch (Exception e) {

e.printStackTrace();

System.out.println("error");

}

}

}

运行结果:

408a86f7ec8a963dea0f8f01f6ae8fb1.png

1.2 部分属性的序列化

实现部分字段序列化的方式:

使用transient修饰符

使用static修饰符

默认方法writeObject和readObject。

卖个关子,稍后讲解。

1.2.1  使用transient修饰符

修改实体类,将实体类中不想序列化的属性添加transient修饰词。

public class Student implements Externalizable{

private String stuNum;

private transient String stuName;

private transient List teacherList;

......

重新运行测试类的结果:

我们将实体类中的stuName和teacherList属性添加了transient修饰词,因此对象被序列化的时候忽略这两个属性。通过运行结果可以看出。

3c512b38b20a5e295bb29dfb911e271f.png

1.2.2 使用static修饰符

static修饰符修饰的属性也不会参与序列化和反序列化。

有时候我们反序列化后生成的对象中的静态成员变量的值和序列化之前是一样的,但是这并不是通过反序列化得到的,而是因为静态成员变量时类的属性,反序列化后的对象也是这个目标类,所以这个静态成员变量会和序列化之前的值一样。

1.2.3 默认方法writeObject和readObject

修改实体类,将transient修饰词去掉,添加两个方法。

public class Student implements Serializable{

private String stuNum;

private String stuName;

private List teacherList;

private void writeObject(ObjectOutputStream objOut) throws IOException{

System.out.println("writeObject-----------");

objOut.writeObject(stuNum);

objOut.writeObject(stuName);

}

private void readObject(ObjectInputStream objIn) throws IOException, ClassNotFoundException{

System.out.println("readObject-----------");

stuNum= (String) objIn.readObject();

stuName= (String) objIn.readObject();

}

.....

重新运行测试类的结果:

我们在添加的方法中只对stuNum和stuName属性做了序列化和反序列化的操作,因此只有这个两个属性可以被序列化和反序列化。

380c520689856511e03d918ef2116faa.png

1.2.3.1 源码分析:

注意:添加的两个方法必须是private void,否则不生效。

Java调用ObjectOutputStream类检查其是否有私有的,无返回值的writeObject方法,如果有,其会委托该方法进行对象序列化。

Serializable接口的注释:

cafcbd6f4b8893b2147b32f80f52508d.png

ObjectStreamClass类:在序列化(反序列化)的时候,ObjectOutputStream(ObjectInputStream)会寻找目标类中的私有的writeObject(readObject)方法,赋值给变量writeObjectMethod(readObjectMethod)。

906a7afd87d7d9e229b48e02ad5ad7ae.png

f9183ee408e0952ecf5aa5898acc01de.png

eb1ca33fd718d8e5c259125147773475.png

通过上面这两段代码可以知道,如果writeObjectMethod != null(目标类中定义了私有的writeObject方法),那么将调用目标类中的writeObject方法,如果如果writeObjectMethod == null,那么将调用默认的defaultWriteFields方法来读取目标类中的属性。

readObject的调用逻辑和writeObject一样。

总结一下,如果目标类中没有定义私有的writeObject或readObject方法,那么序列化和反序列化的时候将调用默认的方法来根据目标类中的属性来进行序列化和反序列化,而如果目标类中定义了私有的writeObject或readObject方法,那么序列化和反序列化的时候将调用目标类指定的writeObject或readObject方法来实现。

2、Externalizable实现Java序列化

刚刚我们说实现部分属性序列化的方式有多种,第三种来啦!就是通过实现Eexternalizable接口。

Externalizable继承自Serializable,使用Externalizable接口需要实现writeExternal以及readExternal方法在writeExternal方法中,写入想要外部序列化的元素。

来看看Externalizable接口的说明:

f57ddf873f5ef3acbeb6557530bcfea3.png

代码实现:

package com.kkb.externalizable;

import java.io.*;

import java.util.List;

public class Student implements Externalizable{

private String stuNum;

private String stuName;

private List teacherList;

@Override

public void writeExternal(ObjectOutput out) throws IOException{

out.writeObject(stuNum);

out.writeObject(stuName);

//out.writeObject(teacherList);

}

@Override

public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException{

stuNum= (String) in.readObject();

stuName= (String) in.readObject();

//teacherList= (List) in.readObject();

}

//无参构造方法

public Student(){

}

//全参数构造方法

public Student(String stuNum, String stuName, List teacherList){

this.stuNum = stuNum;

this.stuName = stuName;

this.teacherList = teacherList;

}

@Override

public String toString(){

return "Student{" +

"stuNum='" + stuNum + '\'' +

", stuName='" + stuName + '\'' +

", teacherList=" + teacherList +

'}';

}

public String getStuNum(){

return stuNum;

}

public void setStuNum(String stuNum){

this.stuNum = stuNum;

}

public String getStuName(){

return stuName;

}

public void setStuName(String stuName){

this.stuName = stuName;

}

public List getTeacherList(){

return teacherList;

}

public void setTeacherList(List teacherList){

this.teacherList = teacherList;

}

}

测试结果:

9ced5bff71ed679a35c6f868aca3fdaf.png

Externalizable接口继承了Serializable接口,所以实现Externalizable接口也能实现序列化和反序列化。Externalizable接口中定义了writeExternal和readExternal两个抽象方法,这两个方法其实对应Serializable接口的writeObject和readObject方法。可以这样理解:Externalizable接口被设计出来的目的就是为了抽象出writeObject和readObject这两个方法,但是目前这个接口使用的并不多。

3、Serializable VS Externalizable

区  别

Serializable

Externalizable

实现复杂度

实现简单,Java对其有内建支持

实现复杂,由开发人员自己完成

执行效率

所有对象由Java统一保存,性能较低

开发人员决定哪个对象保存,可能造成速度提升

保存信息

保存时占用空间大

部分存储,可能造成空间减少

使用频率

偏低

- END -

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值