transient关键字

参考:Javatransient关键字使用小记java中的关键字transient,这篇文章你再也不发愁了Java基础学习总结——Java对象的序列化和反序列化1、概述给不需要序列化的属性前
强烈推介IDEA2021.1.3破解激活,IntelliJ IDEA 注册码,2021.1.3IDEA 激活码  

参考:Java transient关键字使用小记

java中的关键字transient,这篇文章你再也不发愁了

Java基础学习总结——Java对象的序列化和反序列化

1、概述

  • 给不需要序列化的属性前添加transient关键字,序列化对象时,这个被修饰的属性就不会被序列化
  • 比如用户对象中一些敏感信息(密码,银行卡号等),不希望在网络操作中传输或者存在磁盘中,即这些敏感信息只存在于内存中,不希望被序列化到磁盘存储,这些信息的生命周期只存在于调用者的内存而不会写到磁盘持久化。

2、序列化和反序列化

  • 序列化:把对象转化为字节序列的过程。
  • 反序列化:把字节序列恢复为对象的过程。
  • 对象的序列化用途:
    • 把对象的字节序列永久保存到磁盘中;
    • 在网络上传送对象的字节序列。

​ 在很多应用中,需要对某些对象进行序列化,让它们离开内存空间,入住物理硬盘,以便长期保存。比如最常见的是Web服务器中的Session对象,当有 10万用户并发访问,就有可能出现10万个Session对象,内存可能吃不消,于是Web容器就会把一些seesion先序列化到硬盘中,等要用了,再把保存在硬盘中的对象还原到内存中。

  当两个进程在进行远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。发送方需要把这个Java对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为Java对象

3、序列化的两种方式

(1)实现Serializable接口

只要实现此接口,会自动序列化处理


/**
 * @description 使用transient关键字不序列化某个变量
 *        注意读取的时候,读取数据的顺序一定要和存放数据的顺序保持一致
 *        
 * @author Alexia
 * @date  2013-10-15
 */
class User implements Serializable {
    private static final long serialVersionUID = 8294180014912103005L;  
    
    private String username;
    private transient String passwd;
    
    //setter、getter方法生成
}
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 (Exception 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 (Exception e) {
            e.printStackTrace();
        }
    }
}
只听到从山间传来架构君的声音:
戎马无休歇,关山正渺茫。有谁来对上联或下联?

solution:

此代码由Java架构师必看网-架构君整理
read before Serializable: username: Alexia password: 123456 read after Serializable: username: Alexia password: null

因为private transient String passwd;passwd字段被transient修饰,所以password属性值不会被存储到磁盘中,反序列时就无法从文件中读取到该字段的属性值。

  • transient使用:

    • 被transient修饰的变量无法被序列化
    • transient只能修饰变量,不能修饰方法和类。本地变量(局部变量)不能被transient修饰。
    • 静态变量(全局量)不管是否被transient修饰,都不能被序列化

    针对第三点说明:因为发现在User类中的username字段前加上static关键字后,程序运行结果依然不变,即static类型的username也读出来为“Alexia”了,这不与第三点说的矛盾吗?实际上是这样的:第三点确实没错(一个静态变量不管是否被transient修饰,均不能被序列化),反序列化后类中static型变量username的值为当前JVM中对应static变量的值,这个值是JVM中的,不是反序列化得出的。因为静态变量在全局区,程序读取的时候会先到全局区读取数据。


/**
 * @description 使用transient关键字不序列化某个变量
 *        注意读取的时候,读取数据的顺序一定要和存放数据的顺序保持一致
 *        
 * @author Alexia
 * @date  2013-10-15
 */
class User implements Serializable {
    private static final long serialVersionUID = 8294180014912103005L;  
    
    public static String username;
    private transient String passwd;
    
   //getter,setter方法生成

}
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 (Exception 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 (Exception e) {
            e.printStackTrace();
        } 
    }
}

solution:

此代码由Java架构师必看网-架构君整理
read before Serializable: username: Alexia password: 123456 read after Serializable: username: jmwang password: null

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

(2)实现Externalizable接口

实现此接口,需要重写writeExternal和readExternal方法,决定了哪些属性需要序列化。即使是transient修饰的属性,也可以指定该属性序列化。

/**
 * @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();
    }
}

solution:

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

原因是在writeExternal和readExternal中指定了对content属性的序列化,此时transient修饰就失效了。

4、总结

  • transient关键字可以使得被修饰属性,不被序列化,保护敏感信息;
  • transient只能修饰变量,不能修饰方法和类,也不能修饰本地变量;
  • 静态变量不管有无transient修饰,都不能被序列化,都是从JVM全局区中读取,而不是从磁盘读取;
  • 序列化实现是Serializable接口自动序列化,如变量被transient修饰,变量就不会被序列化;
  • 序列化实现的是Externalizable接口,需要重写writeExternal和readExternal方法,如果在方法中指定了序列化的变量,那么这个变量必被序列化,不管此变量是否被transient修饰。
架构君码字不易,如需转载,请注明出处:https://javajgs.com/archives/42008
0

发表评论