Java中的「序列化 | 反序列化 | transient关键字 | Serializable接口 | Exteranlizable接口」

1. 详细介绍

transient是短暂的意思。对于transient 修饰的成员变量,在类的实例对象的序列化处理过程中会被忽略。
因此,transient变量不会贯穿对象的序列化和反序列化,生命周期仅存在于内存中而不会写到磁盘里进行持久化。

1.1 序列化

  Java中对象的序列化:指将对象转换成以字节序列的形式来表示,这些字节序列包含了对象的数据和信息,一个序列化后的对象可以被写到数据库或文件中,也可用于网络传输。

  当然,序列化后的最终目的是为了反序列化,恢复成原先的Java对象实例。所以序列化后的字节序列都是可以恢复成Java对象的,这个过程就是反序列化。

  另外,实现序列化有两种方式:

  1. 直接或间接实现Serializable接口:JVM完全负责序列化(非transient修饰的就序列化,transient修饰的就不序列化)。
    【这是一种常用的序列化方式,搭配transient使用】
  2. 直接或间接实现Exteranlizable接口:程序员负责序列化与反序列化(通过重写writeExternal和readExternal方法决定哪些需要被序列化,即使是即使是transient修饰没用)。
    【实现Exteranlizable接口需要重写writeExternal和readExternal方法。】

注意:因为Serializable接口使用反射和元数据,这会导致相对较慢的性能。所以,Exteranlizable比Serializable的效率高

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.*;

public class Student implements Externalizable {
    private transient String name;
    private int age;

    // setter 和 getter方法
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }

    // 实现方法
    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeUTF(name); // 设置name可序列化
        out.writeInt(age);	// 设置age可序列化
    }
    @Override
    public void readExternal(ObjectInput in)
            throws IOException, ClassNotFoundException {
        this.name = in.readUTF();
        this.age = in.readInt();
    }

}

class ExternalizableTest {
    public static void main(String[] args) throws IOException, ClassNotFoundException {

        Student student = new Student();
        student.setAge(27);
        student.setName("Tom");

        // 序列化
        System.out.println("序列化前:name = " + student.getName() + ",age = " + student.getAge());
        try (ObjectOutputStream outputFile = new ObjectOutputStream(new FileOutputStream(new File("./Student.txt")))) {
            outputFile.writeObject(student); // write person to file
        }

        // 反序列化
        Student studentDeserializable;
        try (ObjectInputStream inputFile = new ObjectInputStream(new FileInputStream(new File("./Student.txt")))) {
            studentDeserializable = (Student) inputFile.readObject();
        }
        System.out.println("序列化后:name = " + studentDeserializable.getName() + ",age = " + studentDeserializable.getAge());
    }
}

输出:
在这里插入图片描述
可以看见即使是transient修饰的name,还是被序列化了。

1.2 为什么要用transient关键字?

  一个类直接或间接实现了Serializable接口,那么整个类都可被序列化。但是有时,我们需要将某部分不可序列化。因此,在不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会被序列化。

  比如:在持久化对象时,对于用户的密码,银行卡号等,我们不想用序列化机制来保存它,可以在这些成员变量前加上关键字transient。

注意:static修饰的变量,即类字段,天然就是不可序列化的。

在这里插入图片描述
在这里插入图片描述

2. transient使用总结

  1. transient关键字只能修饰变量,而不能修饰方法和类。
  2. 虽然类没有直接或间接继承Serializable接口,也可以使用transient关键字修饰字段。但是只有类直接或间接继承Serializable接口,可序列化,使用transient才有意义。
  3. 一旦变量被transient修饰,变量将不再是对象持久化的一部分,该变量内容在序列化后无法被访问。
  4. 一个静态变量不管是否被transient修饰,均不能被序列化(如果反序列化后类中static变量还有值,则值为当前JVM中对应static变量的值)。序列化保存的是对象状态,静态变量保存的是类状态,因此序列化并不保存静态变量。
    在这里插入图片描述
    在这里插入图片描述

3. 使用场景

  1. 一些安全性的信息,比如密码等
  2. 某个字段值能够由其他字段计算出来。比如:圆类,有字段半径和面积,那么知道圆半径,就可以计算出圆面积,故圆面积可用stransient修饰。这样可以节省持久化所占资源空间。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ElegantCodingWH

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值