Java transient关键字使用小记

1. transient的作用及使用方法

我们都知道一个对象只要实现了Serilizable接口,这个对象就可以被序列化,java的这种序列化模式为开发者提供了很多便利,我们可以不必关系具体序列化的过程,只要这个类实现了Serilizable接口,这个类的所有属性和方法都会自动序列化。

然而在实际开发过程中,我们常常会遇到这样的问题,这个类的有些属性需要序列化,而其他属性不需要被序列化,打个比方,如果一个用户有一些敏感信息(如密码,银行卡号等),为了安全起见,不希望在网络操作(主要涉及到序列化操作,本地序列化缓存也适用)中被传输,这些信息对应的变量就可以加上transient关键字。换句话说,这个字段的生命周期仅存于调用者的内存中而不会写到磁盘里持久化。

总之,java 的transient关键字为我们提供了便利,你只需要实现Serilizable接口,将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会序列化到指定的目的地中。

示例code如下:

import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.io.Serializable;
/** * @description 使用transient关键字不序列化某个变量 *        注意读取的时候,读取数据的顺序一定要和存放数据的顺序保持一致 *         * @author Alexia * @date  2013-10-15 */public class TransientTest {        public static void main(String[] args) {                User user = new User();        user.setUsername("Alexia");        user.setPasswd("123456");                System.out.println("read before Serializable: ");        System.out.println("username: " + user.getUsername());        System.err.println("password: " + user.getPasswd());                try {            ObjectOutputStream os = new ObjectOutputStream(                    new FileOutputStream("C:/user.txt"));            os.writeObject(user); // 将User对象写进文件            os.flush();            os.close();        } catch (FileNotFoundException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        }        try {            ObjectInputStream is = new ObjectInputStream(new FileInputStream(                    "C:/user.txt"));            user = (User) is.readObject(); // 从流中读取User的数据            is.close();                        System.out.println("\nread after Serializable: ");            System.out.println("username: " + user.getUsername());            System.err.println("password: " + user.getPasswd());                    } catch (FileNotFoundException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        } catch (ClassNotFoundException e) {            e.printStackTrace();        }    }}
class User implements Serializable {    private static final long serialVersionUID = 8294180014912103005L;          private String username;    private transient String passwd;        public String getUsername() {        return username;    }        public void setUsername(String username) {        this.username = username;    }        public String getPasswd() {        return passwd;    }        public void setPasswd(String passwd) {        this.passwd = passwd;    }
}

输出为:

read before Serializable: username: Alexiapassword: 123456
read after Serializable: username: Alexiapassword: null

密码字段为null,说明反序列化时根本没有从文件中获取到信息。

2. transient使用小结

1)一旦变量被transient修饰,变量将不再是对象持久化的一部分,该变量内容在序列化后无法获得访问。

2)transient关键字只能修饰变量,而不能修饰方法和类。注意,本地变量是不能被transient关键字修饰的。变量如果是用户自定义类变量,则该类需要实现Serializable接口。

3)被transient关键字修饰的变量不再能被序列化,一个静态变量不管是否被transient修饰,均不能被序列化。

第三点可能有些人很迷惑,因为发现在User类中的username字段前加上static关键字后,程序运行结果依然不变,即static类型的username也读出来为“Alexia”了,这不与第三点说的矛盾吗?实际上是这样的:第三点确实没错(一个静态变量不管是否被transient修饰,均不能被序列化),反序列化后类中static型变量username的值为当前JVM中对应static变量的值,这个值是JVM中的不是反序列化得出的,不相信?好吧,下面我来证明:

import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.io.Serializable;
/** * @description 使用transient关键字不序列化某个变量 *        注意读取的时候,读取数据的顺序一定要和存放数据的顺序保持一致 *         * @author Alexia * @date  2013-10-15 */public class TransientTest {        public static void main(String[] args) {                User user = new User();        user.setUsername("Alexia");        user.setPasswd("123456");                System.out.println("read before Serializable: ");        System.out.println("username: " + user.getUsername());        System.err.println("password: " + user.getPasswd());                try {            ObjectOutputStream os = new ObjectOutputStream(                    new FileOutputStream("C:/user.txt"));            os.writeObject(user); // 将User对象写进文件            os.flush();            os.close();        } catch (FileNotFoundException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        }        try {            // 在反序列化之前改变username的值            User.username = "jmwang";                        ObjectInputStream is = new ObjectInputStream(new FileInputStream(                    "C:/user.txt"));            user = (User) is.readObject(); // 从流中读取User的数据            is.close();                        System.out.println("\nread after Serializable: ");            System.out.println("username: " + user.getUsername());            System.err.println("password: " + user.getPasswd());                    } catch (FileNotFoundException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        } catch (ClassNotFoundException e) {            e.printStackTrace();        }    }}
class User implements Serializable {    private static final long serialVersionUID = 8294180014912103005L;          public static String username;    private transient String passwd;        public String getUsername() {        return username;    }        public void setUsername(String username) {        this.username = username;    }        public String getPasswd() {        return passwd;    }        public void setPasswd(String passwd) {        this.passwd = passwd;    }
}

运行结果为:

read before Serializable: username: Alexiapassword: 123456
read after Serializable: username: jmwangpassword: null

这说明反序列化后类中static型变量username的值为当前JVM中对应static变量的值,为修改后jmwang,而不是序列化时的值Alexia。

3. transient使用细节——被transient关键字修饰的变量真的不能被序列化吗?

思考下面的例子:

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;
/** * @descripiton Externalizable接口的使用 *  * @author Alexia * @date 2013-10-15 * */public class ExternalizableTest implements Externalizable {
    private transient String content = "是的,我将会被序列化,不管我是否被transient关键字修饰";
    @Override    public void writeExternal(ObjectOutput out) throws IOException {        out.writeObject(content);    }
    @Override    public void readExternal(ObjectInput in) throws IOException,            ClassNotFoundException {        content = (String) in.readObject();    }
    public static void main(String[] args) throws Exception {                ExternalizableTest et = new ExternalizableTest();        ObjectOutput out = new ObjectOutputStream(new FileOutputStream(                new File("test")));        out.writeObject(et);
        ObjectInput in = new ObjectInputStream(new FileInputStream(new File(                "test")));        et = (ExternalizableTest) in.readObject();        System.out.println(et.content);
        out.close();        in.close();    }}

content变量会被序列化吗?好吧,我把答案都输出来了,是的,运行结果就是:

是的,我将会被序列化,不管我是否被transient关键字修饰

这是为什么呢,不是说类的变量被transient关键字修饰以后将不能序列化了吗?

我们知道在Java中,对象的序列化可以通过实现两种接口来实现,若实现的是Serializable接口,则所有的序列化将会自动进行,若实现的是Externalizable接口,则没有任何东西可以自动序列化,需要在writeExternal方法中进行手工指定所要序列化的变量,这与是否被transient修饰无关。因此第二个例子输出的是变量content初始化的内容,而不是null。

  • 原文:http://www.cnblogs.com/lanxuezaipiao/p/3369962.html

原文发布于微信公众号 - Java团长(javatuanzhang)

原文发表时间:2018-01-15

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏用户2442861的专栏

Java的 transient关键字

哎,虽然自己最熟的是Java,但很多Java基础知识都不知道,比如transient关键字以前都没用到过,所以不知道它的作用是什么,今天做笔试题时发现有一题是...

832
来自专栏coding...

C语言基础 - 实现单向链表

1723
来自专栏Ryan Miao

java删除文件夹

想删除本地一个项目目录,结果windows说路径太长,不能删除。于是试了试java删除。一切ok。以后一定要抓紧时间学python。 /** * Create...

29210
来自专栏Python攻城狮

Python数据科学(七)- 资料清理(Ⅱ)1.资料转换2.处理时间格式资料3.重塑资料4.学习正则表达式5.实例处理

注意:这里的时间转换后的格式可以根据需要设定,eg:dt.strftime('%Y/%m/%d')

1163
来自专栏闵开慧

spark使用zipWithIndex和zipWithUniqueId为rdd中每条数据添加索引数据

spark的rdd中数据需要添加自增主键,然后将数据存入数据库,使用map来添加有的情况是可以的,有的情况是不可以的,所以需要使用以下两种中的其中一种来进行添加...

7579
来自专栏AI星球

Spark常用的算子以及Scala函数总结

首先,介绍一下scala语言: Scala 是一种把面向对象和函数式编程理念加入到静态类型语言中的混血儿。

1.2K2
来自专栏个人分享

spark RDD transformation与action函数整理

3.过滤 filter 需要注意的是 filter并不会在原有RDD上过滤,而是根据filter的内容重新创建了一个RDD

2022
来自专栏友弟技术工作室

golang之Struct什么是结构体struct?

最近在复习golang,学习的东西,如果不使用,很快就会忘记。所以,准备复习完golang,做一个练手的小项目,加深对golang的学习。今天开始公司,进入封闭...

5766
来自专栏Albert陈凯

2018-11-17 Spark算子练习常用Transformation(即转换,延迟加载)通过并行化scala集合创建RDD查看该rdd的分区数量union求并集,注意类型要一致intersecti

################################################################################...

1812
来自专栏从零开始学自动化测试

Selenium2+python自动化42-判断元素(expected_conditions)

前言 经常有小伙伴问,如何判断一个元素是否存在,如何判断alert弹窗出来了,如何判断动态的元素等等一系列的判断,在selenium的expected_cond...

4217

扫码关注云+社区

领取腾讯云代金券