资讯专栏INFORMATION COLUMN

小伙子,你真的搞懂 transient 关键字了吗?

curlyCheng / 3012人阅读

摘要:由以上结果分析可知,静态变量不能被序列化,示例读取出来的是在内存中存储的值。关键字总结修饰的变量不能被序列化只作用于实现接口只能用来修饰普通成员变量字段不管有没有修饰,静态变量都不能被序列化好了,栈长花了半天时间,终于整理完了。

先解释下什么是序列化

我们的对象并不只是存在内存中,还需要传输网络,或者保存起来下次再加载出来用,所以需要Java序列化技术。

Java序列化技术正是将对象转变成一串由二进制字节组成的数组,可以通过将二进制数据保存到磁盘或者传输网络,磁盘或者网络接收者可以在对象的属类的模板上来反序列化类的对象,达到对象持久化的目的。

更多序列化请参考:《关于Java序列化你应该知道的一切》这篇文章。

什么是 transient?

简单来说就是,被 transient 修饰的变量不能被序列化。

具体来看下面的示例1

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

/**
 * @author 微信公众号:Java技术栈
 */
public class TransientTest {

    public static void main(String[] args) throws Exception {

        User user = new User();
        user.setUsername("Java技术栈");
        user.setId("javastack");

        System.out.println("
序列化之前");
        System.out.println("username: " + user.getUsername());
        System.out.println("id: " + user.getId());

        ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("d:/user.txt"));
        os.writeObject(user);
        os.flush();
        os.close();

        ObjectInputStream is = new ObjectInputStream(new FileInputStream("d:/user.txt"));
        user = (User) is.readObject();
        is.close();

        System.out.println("
序列化之后");
        System.out.println("username: " + user.getUsername());
        System.out.println("id: " + user.getId());

    }
}

/**
 * @author 微信公众号:Java技术栈
 */
class User implements Serializable {

    private static final long serialVersionUID = 1L;

    private String username;
    private transient String id;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

}

输出结果:

序列化之前
username: Java技术栈
id: javastack

序列化之后
username: Java技术栈
id: null

示例1在 id 字段上加了 transient 关键字修饰,反序列化出来之后值为 null,说明了被 transient 修饰的变量不能被序列化。

静态变量能被序列化吗?

这个话题也是最近栈长的Java技术栈vip群里面讨论的,大家对这个知识点比较模糊,我就写了这篇文章测试总结一下。

如果你也想加入我们的Java技术栈vip群和各位大牛一起讨论技术,那点击这个链接了解加入吧。

那么,到底静态变量能被序列化吗?废话少说,先动手测试下吧!

示例2:

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

/**
 * @author 微信公众号:Java技术栈
 */
public class TransientStaticTest {

    public static void main(String[] args) throws Exception {

        User2 user = new User2();
        User2.username = "Java技术栈1";
        user.setId("javastack");

        System.out.println("
序列化之前");
        System.out.println("username: " + user.getUsername());
        System.out.println("id: " + user.getId());

        ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("d:/user.txt"));
        os.writeObject(user);
        os.flush();
        os.close();
        
        // 在反序列化出来之前,改变静态变量的值
        User2.username = "Java技术栈2";

        ObjectInputStream is = new ObjectInputStream(new FileInputStream("d:/user.txt"));
        user = (User2) is.readObject();
        is.close();

        System.out.println("
序列化之后");
        System.out.println("username: " + user.getUsername());
        System.out.println("id: " + user.getId());

    }
}

/**
 * @author 微信公众号:Java技术栈
 */
class User2 implements Serializable {

    private static final long serialVersionUID = 1L;

    public static String username;
    private transient String id;

    public String getUsername() {
        return username;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

}

输出结果:

序列化之前
username: Java技术栈1
id: javastack

序列化之后
username: Java技术栈2
id: null

示例2把 username 改为了 public static, 并在反序列化出来之前改变了静态变量的值,结果可以看出序列化之后的值并非序列化进去时的值。

由以上结果分析可知,静态变量不能被序列化,示例2读取出来的是 username 在 JVM 内存中存储的值。

transient 真不能被序列化吗?

继续来看示例3:

import java.io.Externalizable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;

/**
 * @author 微信公众号:Java技术栈
 */
public class ExternalizableTest {

    public static void main(String[] args) throws Exception {

        User3 user = new User3();
        user.setUsername("Java技术栈");
        user.setId("javastack");
        ObjectOutput objectOutput = new ObjectOutputStream(new FileOutputStream(new File("javastack")));
        objectOutput.writeObject(user);

        ObjectInput objectInput = new ObjectInputStream(new FileInputStream(new File("javastack")));
        user = (User3) objectInput.readObject();

        System.out.println(user.getUsername());
        System.out.println(user.getId());

        objectOutput.close();
        objectInput.close();
    }

}

/**
 * @author 微信公众号:Java技术栈
 */
class User3 implements Externalizable {

    private static final long serialVersionUID = 1L;

    public User3() {

    }

    private String username;
    private transient String id;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    @Override
    public void writeExternal(ObjectOutput objectOutput) throws IOException {
        objectOutput.writeObject(id);
    }

    @Override
    public void readExternal(ObjectInput objectInput) throws IOException, ClassNotFoundException {
        id = (String) objectInput.readObject();
    }

}

输出结果:

null
javastack

示例3的 id 被 transient 修改了,为什么还能序列化出来?那是因为 User3 实现了接口 Externalizable,而不是 Serializable。

在 Java 中有两种实现序列化的方式,Serializable 和 Externalizable,可能大部分人只知道 Serializable 而不知道 Externalizable。

这两种序列化方式的区别是:实现了 Serializable 接口是自动序列化的,实现 Externalizable 则需要手动序列化,通过 writeExternal 和 readExternal 方法手动进行,这也是为什么上面的 username 为 null 的原因了。

transient 关键字总结

1)transient修饰的变量不能被序列化;

2)transient只作用于实现 Serializable 接口;

3)transient只能用来修饰普通成员变量字段;

4)不管有没有 transient 修饰,静态变量都不能被序列化;

好了,栈长花了半天时间,终于整理完了。如果对你有帮助,那就转发分享一下吧!如果你也想加入我们的Java技术栈vip群和各位大牛一起讨论技术,那点击这个链接了解加入吧

另外,栈长已经整理了大量 Java 系列核心技术知识点文章,关注Java技术栈微信公众号,在后台回复关键字:java,即可获取最新版。

本文原创首发于微信公众号:Java技术栈(id:javastack),关注公众号在后台回复 "java" 可获取更多,转载请原样保留本信息。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/73573.html

相关文章

  • 到底什么是重入锁,拜托,一次搞清楚!

    摘要:为什么叫重入锁呢,我们把它拆开来看就明了了。释放锁,每次锁持有者数量递减,直到为止。 相信大家在工作或者面试过程中经常听到重入锁这个概念,或者与关键字 synchrozied 的对比,栈长面试了这么多人,80%的面试者都没有答对或没有答到点上,或者把双重效验锁搞混了,哭笑不得。。 那么你对重入锁了解有多少呢?今天,栈长帮大家撕开重入锁的面纱,来见识下重入锁的真实容颜。。 什么是重入锁 ...

    LiuRhoRamen 评论0 收藏0
  • 面试问我 Java 逃逸分析,瞬间被秒杀了。。

    摘要:记得几年前有一次栈长去面试,问到了这么一个问题中的对象都是在堆中分配吗说明为什么当时我被问得一脸蒙逼,瞬间被秒杀得体无完肤,当时我压根就不知道他在考什么知识点,难道对象不是在堆中分配吗最后就没然后了,回去等通知了。。 记得几年前有一次栈长去面试,问到了这么一个问题: Java中的对象都是在堆中分配吗?说明为什么! 当时我被问得一脸蒙逼,瞬间被秒杀得体无完肤,当时我压根就不知道他在考什么...

    CastlePeaK 评论0 收藏0
  • HashMap 真的了解吗?

    摘要:加载因子是哈希表在其容量自动增加之前可以达到多满的一种尺度。当哈希表中的条目数超出了加载因子与当前容量的乘积时,则要对该哈希表进行操作即重建内部数据结构,从而哈希表将具有大约两倍的桶数。 showImg(https://upload-images.jianshu.io/upload_images/4565148-98b22ba5ae7d9723.jpg?imageMogr2/auto-...

    RdouTyping 评论0 收藏0
  • 作用域闭包,真的了吗

    摘要:曾几何时,闭包好像就是一个十分难以捉摸透的东西,看了很多文章,对闭包都各有说法,以致让我十分晕,什么内部变量外部变量的,而且大多数都只描述一个过程,没有给闭包的定义,最后,举几个例子,告诉你这就是闭包。 曾几何时,闭包好像就是一个十分难以捉摸透的东西,看了很多文章,对闭包都各有说法,以致让我十分晕,什么内部变量、外部变量的,而且大多数都只描述一个过程,没有给闭包的定义,最后,举几个例子...

    yangrd 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<