理解Java序列化(二)——再看不懂我找不到女朋友

简介: 理解Java序列化(二)——再看不懂我找不到女朋友

对象序列化

当我们有时需要将Java中的对象进行传输时,需要将它转换成二进制流保存在文件中,然后其他程序通过这个二进制流再将其恢复成原有的Java类型数据

序列化分为两种,第一种就是序列化,即将Java对象转换成字节序列保存起来

第二种就是反序列化,即将处理后的字节序列恢复成Java对象

实现序列化两种方式:

方式一:实现Serializable接口

当我们要将我们的类对象序列化的时候就要使这个类实现Serializable接口,这个接口只是作为一种标记,标记这个类是否可以被序列化,不需要实现任何抽象方法,而另外一种方式就要去实现内部的抽象方法

序列化的流程:

1、创建一个对象输出流

2、调用该对象的writeObject方法,向指定流输出序列化对象

示例:将Teacher对象序列化存入Java.txt文件中

try(
  ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("Java.txt"));
)
{
  oos.writeObject(new Teacher("张三"));
}
class Teacher implements Serializable{
  String name;
  public Teacher(){}
  public Teacher(String name){
  this.name=name;
  }
}

反序列化:

1、创建一个对象输入流

2、调用该对象的readObject方法,向指定流输入序列化对象

示例:将序列化后的Teacher读取出来

try(
  ObjectInputStream ois=new ObjectInputStream(new FileInputStream("Java.txt"));
)
{
    Teacher t=(Teacher)ois.readObject();
}
class Teacher implements Serializable{
  String name;
  public Teacher(){}
  public Teacher(String name){
  this.name=name;
  }
}

注意:反序列化得到的类对象源码中必须有相应的类对象,否则会反序列化失败

将序列化得到的Object对象强转成Teacher对象

反序列化是无需构造器来初始化恢复对象的

读取顺序要按照序列化顺序来读取

对象引用的序列化

由上文可以一个类要序列化必需要实现接口,那么当某个类中存在内部类,且该内部类未实现接口,那么整个大类也是无法序列化的。

例如Teacher类实现了接口,而Teacher类中的Student类没有实现接口,那么Teacher是无法序列化的,但是当我们用关键字transient修饰时,Teacher类序列化时就会忽略Student类,进而可以序列化了。

这里有个特殊情形:当重复序列化同一个对象,他会重复的写入文件吗?

答案是不会的,假设如果会重复写入的话,读取的时候就会读取多个对象,就会导致不一致的现象

Java序列化机制采用一种特殊算法,序列化一个对象时会得到一种编号,那么再次序列化这个对象时Java就会检测这个编号是否存在,如果存在只是输出这个序列化编号,而不会再次序列化

举个例子:

Student s=new Student();
Teacher t1=new Teacher("张三",s);
Teacher t2=new Teacher("李四",s);

由上述代码可以两个Teacher对象都引用了s对象,那么当序列化s后,再序列化两个Teacher对象时,引用的s对象,就不会再次序列化,只是会输出相应的序列化编号。

注意:

当我们将s对象序列化后,再在程序中修改s对象中的值是不会修改序列化中的内容的,反序列化时得到的也是修改之前的

示例:

try(
  ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("Java.txt"));
    ObjectInputStream ois=new ObjectInputStream(new FileInputStream("Java.txt"));
)
{
    Teacher t=new Teacher("张三",18);
    oos.writeObject(t);
    t.name="李四";
    Teacher t2=(Teacher)oos.readObject();
    System.out.println(t2.toString());
}

上述代码将t序列化存入文件后,再去修改t的姓名,再反序列化时,得到的依旧是修改之前的对象

自定义序列化

当我们有些时候,我们类中的数据并不希望全部序列化,有些数据保密,这就需要将这些数据编程不可序列化

这是我们就可以将该数据用transient关键字修饰,这时就不会因为不可序列化而报错,只是序列化时会忽略它

示例:

class Teacher implements Serializable{
    String name;
    transient int id;
}

这是当我们再去序列化Teacher类时只会序列化name,而id就不会写入,当我们再读取这个对象时,得到的id就为0,因为是默认值

有时我们可以自定义方法去满足我们自定义序列化要求

示例:

class Teacher implements Serialiable{
    String name;
    int id;
    private void writeObject(ObjectOutputStream out) throws IOException{
        out.writeObject(new StringBuffer(name));
        out.writeInt(id);
    }
    private void readObject(ObjectInputStream in) throws IOException{
        this.name=(StringBuffer)in.readObject();
        this.id=in.readInt();
    }
}

序列化机制还有一个代替方法,就是序列化对象转换成其他对象

class Teacher implements Serialiable{
    String name;
    int id;
    private Object writeReplace() throws IOException{
        ArrayList<Object> list=new ArrayList<>();
        list.add(name);
        list.add(id);
        return list;
    }
}

这是当我们再去序列化Teacher对象时,我们会将Teacher里面的数据已ArrayList形式进行序列化,再进行反序列化时得到的也是一个ArrayList形式的集合

由上可知,Java在序列化某个对象时,会调用该对象的writeReplace方法和writeObject方法

方式二:实现Externalizable接口

注意该接口与上面的接口不同,实现该接口时要实现两个抽象方法

  • void readExternal(ObjectInput in):
  • void writeExternal(ObjectOutput out):

示例:

class Teacher implements Externalizable{
    String name;
    int id;
    public Teacher(){}
    public Teacher(String name,int id){
        this.name=name;
        this.id=id;
    }
    public void writeExternal(ObjectOutput out) throws IOException{
        out.writeObject(new StringBuffer(name));
        out.writeInt(id);
    }
    public void readExternal(ObjectInput in) throw IOException{
        this.name=((StringBuffer)in.readObject()).toString;
        this.id=in.readInt();
    }
}

注意该方法序列化时必须提供无参构造器,因为反序列化时会调用无参构造器创建一个示例,再去调用readExternal方法进行反序列化。

两种方式对比:

  • Serializable会自动存储所有数据,而Externaliable可以程序员决定序列化哪些数据
  • Serializable易于实现,只需实现接口,而Externaliable需要重写两个方法
  • Serializable相比于Externaliable的性能较差


目录
相关文章
|
2月前
|
存储 Java 数据库
|
4月前
|
存储 算法 Java
从零开始学习 Java:简单易懂的入门指南之IO序列化、打印流、压缩流(三十三)
从零开始学习 Java:简单易懂的入门指南之IO序列化、打印流、压缩流(三十三)
|
4月前
|
分布式计算 Java 大数据
IO流【Java对象的序列化和反序列化、File类在IO中的作用、装饰器模式构建IO流体系、Apache commons-io工具包的使用】(四)-全面详解(学习总结---从入门到深化)
IO流【Java对象的序列化和反序列化、File类在IO中的作用、装饰器模式构建IO流体系、Apache commons-io工具包的使用】(四)-全面详解(学习总结---从入门到深化)
53 0
|
11天前
|
存储 Java 测试技术
滚雪球学Java(22):序列化和反序列化
【4月更文挑战第11天】🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
26 1
滚雪球学Java(22):序列化和反序列化
|
14天前
|
SQL 存储 安全
每日一道面试题:Java中序列化与反序列化
每日一道面试题:Java中序列化与反序列化
10 0
|
24天前
|
存储 Java
Java输入输出:解释一下序列化和反序列化。
Java中的序列化和反序列化是将对象转换为字节流和反之的过程。ObjectOutputStream用于序列化,ObjectInputStream则用于反序列化。示例展示了如何创建一个实现Serializable接口的Person类,并将其序列化到文件,然后从文件反序列化回Person对象。
27 5
|
29天前
|
存储 Java Maven
java序列化
java序列化
|
2月前
|
存储 缓存 JSON
什么是Java序列化,它有哪些重要性
什么是Java序列化,它有哪些重要性
|
3月前
|
存储 自然语言处理 Java
java缓冲流、转换流、序列化流、打印流
java缓冲流、转换流、序列化流、打印流介绍
|
4月前
|
JSON Java fastjson
Java中的JSON序列化和反序列化
Java中的JSON序列化和反序列化