Java transient 关键字详解及实例代码

 更新时间:2016年12月09日 08:39:16   投稿:lqh  
本文章向大家介绍Java transient关键字的使用方法和实例,包括的知识点有transient的作用、transient使用小结、transient使用细节,需要的朋友可以参考一下

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
 * http://www.manongjc.com/article/1609.html 
 */
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: Alexia
password: 123456

read after Serializable: 
username: Alexia
password: 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
 * http://www.manongjc.com
 */
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: Alexia
password: 123456

read after Serializable: 
username: jmwang
password: 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。

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

相关文章

  • 浅谈Java内存泄露

    浅谈Java内存泄露

    内存泄漏(Memory Leak)是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。下面我们来一起了解如何解决
    2019-05-05
  • 使用Apache Spark进行Java数据分析的步骤详解

    使用Apache Spark进行Java数据分析的步骤详解

    今天我们将探讨如何使用Apache Spark进行Java数据分析,Apache Spark是一个强大的大数据处理引擎,它支持批处理和流处理,特别适合处理大规模数据集,在Java中使用Spark,我们可以利用其强大的数据处理能力来进行各种数据分析任务,需要的朋友可以参考下
    2024-07-07
  • Java实现将文件或者文件夹压缩成zip的详细代码

    Java实现将文件或者文件夹压缩成zip的详细代码

    这篇文章主要介绍了Java实现将文件或者文件夹压缩成zip的详细代码,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-11-11
  • Java创建线程池的方式实现

    Java创建线程池的方式实现

    本文主要介绍了创建线程池的方式,包括三种方式,第一种是使用Executors框架,第二种是使用ThreadPoolExecutor,第三种是使用ForkJoinPool,感兴趣的可以了解一下
    2024-12-12
  • 基于logback实现纯java版本的SDK组件

    基于logback实现纯java版本的SDK组件

    这篇文章主要介绍了基于logback实现纯java版本的SDK组件,在项目开发过程中通常会使用logback作为日志记录的依赖工具,使用方式是引入logback相关jar包,然后配置logback.xml配置文件的方式来实现,需要的朋友可以参考下
    2023-11-11
  • 老生常谈Java异常处理和设计(推荐)

    老生常谈Java异常处理和设计(推荐)

    下面小编就为大家带来一篇老生常谈Java异常处理和设计(推荐)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-05-05
  • java操作mongodb时,对象bean和DBObject相互转换的方法(推荐)

    java操作mongodb时,对象bean和DBObject相互转换的方法(推荐)

    下面小编就为大家带来一篇java操作mongodb时,对象bean和DBObject相互转换的方法(推荐)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-11-11
  • 深入浅出Java中重试机制的多种方式

    深入浅出Java中重试机制的多种方式

    重试机制在分布式系统中,或者调用外部接口中,都是十分重要的。重试机制可以保护系统减少因网络波动、依赖服务短暂性不可用带来的影响,让系统能更稳定的运行的一种保护机制。本文就来和大家聊聊Java中重试机制的多种方式
    2023-03-03
  • Java判断字符串是否含有乱码实例代码

    Java判断字符串是否含有乱码实例代码

    本文通过实例代码给大家介绍了Java判断字符串是否含有乱码的方法,代码简单易懂,非常不错,具有一定的参考借鉴价值,需要的朋友参考下吧
    2018-11-11
  • java实现图片水平和垂直翻转效果

    java实现图片水平和垂直翻转效果

    这篇文章主要为大家详细介绍了java实现图片水平和垂直翻转效果,图片旋转的灵活运用,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-01-01

最新评论