龙哥网

龙哥网

Java之transient关键字(java的transient关键字)
2022-03-01

transient关键字介绍;

        transient关键字在阅读JDK源码中经常出现,其中Java中transient关键字的作用就是让某些被transient关键字修饰的成员变量不被序列化。

序列化是什么?

专业术语定义的序列化:

        Java提供了一种对象序列化的机制。用一个字节序列可以表示一个对象,该字节序列包含该对象的数据、对象的类型和对象中存储的属性等信息。字节序列写出到文件之后,相当于文件中持久保存了一个对象的信息。反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化。对象的数据、对象的类型和对象中存储的数据信息,都可以用来在内存中创建对象。

简易来说术语定义的序列化即为:

        序列化:对象(数据,类型,属性等信息) ——> 字节序列;

        反序列化:字节序列 ——> 对象(数据,类型,属性等信息);

图解序列化解释:

        

为何Java需要序列化?

        Java中对象的序列化指的是将对象转换成以字节序列的形式来表示,这些字节序列包含了对象的数据和信息,一个序列化后的对象 可以被写到数据库或文件中,也可用于 网络传输,一般当我们使用 缓存cache(内存空间不够有可能会本地存储到硬盘或网络传输的时候,经常需要让我们的实体类实现Serializable接口,目的就是为了让其可序列化;

何时需要使用transient关键词修饰:

        当开发过程中包含一些私密信息或者私密性文件,为了保证数据的安全性,不希望信息在网络操作中被传输,对其加上transient关键字。即该字段的数据的生命周期仅仅在内存,而不被刷入磁盘中。

何时不需要使用transient关键字:

        1.类中的字段值可以被其他字段推出;

        2,内存空间不足时,减少使用transient关键字,节省存储空间;

        3.依据项目需求,可具体选择一些字段不被transient关键字修饰;

序列化和transient的使用:

        1、需要做序列化的对象的类,必须实现序列化接口:Java.lang.Serializable 接口(一个标志接口,没有任何抽象方法),Java 中大多数类都实现了该接口,比如:StringInteger类等,不实现此接口的类将不会使任何状态序列化或反序列化,会抛NotSerializableException异常 。

  未实现Serializable接口进行序列化异常:

package TransientTest;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

class UserInfo {  //没有实现Serializable接口
    private String name;
    private transient String password;
    //函数构造
    public UserInfo(String name,String psw) {
        this.name = name;
        this.password=psw;
    }
//继承object类重写toString方法
    @Override
    public String toString() {
        return "UserInfo{" +
                "name='" + name + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

public class TransientDemo {
	public static void main(String[] args) {
		  UserInfo userInfo=new UserInfo("XXX","123456789");
	        System.out.println("序列化之前信息:"+userInfo);
	        	
	        try {
	            ObjectOutputStream output=new ObjectOutputStream(new FileOutputStream("userinfo.txt"));
	            output.writeObject(new UserInfo("XXX","123456789"));
	            output.close();
	        } catch (IOException e) {
	            e.printStackTrace();
	        }
	}
}

 运行后即出现无法序列的异常;

package TransientTest;


import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;

class UserInfo  implements Serializable { 
    /**
	 * 实现Serializable接口
	 */
	private static final long serialVersionUID = 1L;
	private String name;
    private transient String password;
    //函数构造
    public UserInfo(String name,String psw) {
        this.name = name;
        this.password=psw;
    }
//继承object类重写toString方法
    public String toString() {
        return "UserInfo{" +
                "name='" + name + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}
public class TransientDemo {
	public static void main(String[] args) {
		  UserInfo userInfo=new UserInfo("XXX","123456789");
	        System.out.println("序列化之前信息:"+userInfo);
	        try {
	            ObjectOutputStream output=new ObjectOutputStream(new FileOutputStream("userinfo.txt"));
	            output.writeObject(new UserInfo("XXX","123456789"));
	            output.close();
	        } catch (IOException e) {
	            e.printStackTrace();
	        }
	}
}

        2、底层会判断,如果当前对象是 Serializable 的实例,才允许做序列化,Java对象 instanceof Serializable 来判断。

        3、在 Java 中使用对象流ObjectOutputStream来完成序列化以及ObjectInputStream流反序列化。

        4、该类的所有属性必须是可序列化的。如果有一个属性不需要可序列化的,则该属性必须注明是瞬态的,使用transient 关键字修饰。 

 加入transient关键字后的序列化:

package TransientTest;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

class UserInfo  implements Serializable { 
    /**
	 * 加入transient关键字
	 */
	private static final long serialVersionUID = 1L;
	private String name;
    private transient String password;
    //函数构造
    public UserInfo(String name,String psw) {
        this.name = name;
        this.password=psw;
    }
//继承object类重写toString方法
    public String toString() {
        return "UserInfo{" +
                "name='" + name + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}
public class TransientDemo {
	public static void main(String[] args) {
		 UserInfo userInfo=new UserInfo("XX","123");
	        System.out.println("序列化之前信息:"+userInfo);

	        try {
	            ObjectOutputStream output=new ObjectOutputStream(new FileOutputStream("userinfo.txt")); //第二步开始序列化操作
	            output.writeObject(new UserInfo("XX","123"));
	            output.close();
	        } catch (IOException e) {
	            e.printStackTrace();
	        }

	        try {
	            @SuppressWarnings("resource")
				ObjectInputStream input=new ObjectInputStream(new FileInputStream("userinfo.txt"));//第三步开始反序列化操作
	            Object o = input.readObject();//ObjectInputStream的readObject方法会抛出ClassNotFoundException
	            System.out.println("序列化之后信息:"+o);
	        } catch (IOException e) {
	            e.printStackTrace();
	        } catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

	        
	}
}

普通序列化:

package TransientTest;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

class UserInfo  implements Serializable { 
    /**
	 * 未加入transient关键字
	 */
	private static final long serialVersionUID = 1L;
	private String name;
    private  String password;
    //函数构造
    public UserInfo(String name,String psw) {
        this.name = name;
        this.password=psw;
    }
//继承object类重写toString方法
    public String toString() {
        return "UserInfo{" +
                "name='" + name + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}
public class TransientDemo {
	public static void main(String[] args) {
		 UserInfo userInfo=new UserInfo("XX","123");
	        System.out.println("序列化之前信息:"+ userInfo);

	        try {
	            ObjectOutputStream output=new ObjectOutputStream(new FileOutputStream("userinfo.txt")); //第二步开始序列化操作
	            output.writeObject(new UserInfo("XX","123"));
	            output.close();
	        } catch (IOException e) {
	            e.printStackTrace();
	        }

	        try {
	            @SuppressWarnings("resource")
				ObjectInputStream input=new ObjectInputStream(new FileInputStream("userinfo.txt"));//第三步开始反序列化操作
	            Object  o = input.readObject();
	            System.out.println("序列化之后信息:"+ o );
	        } catch (IOException e) {
	            e.printStackTrace();
	        } catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
	}
}

         transient的属性在对象被序列化的时候不会被保存(变量不会持久化);

java类中serialVersionUID:

        既然提到了transient关键字就不得不提到序列化,既然提到了序列化,就不得不提到serialVersionUID了,基本上有序列化就会存在这个serialVersionUID。

   serialVersionUID适用于Java的序列化机制。简单来说,Java的序列化机制是通过判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常,即是InvalidCastException。

transient关键字小结

1、变量被transient修饰,变量将不会被序列化

2、transient关键字只能修饰变量,而不能修饰方法和类。

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

4、final变量值参与序列化,final transient同时修饰变量,final不会影响transient,一样不会参与序列化

5.本地变量是不能被transient关键字修饰的。变量如果是用户自定义类变量,则该类需要实现Serializable接口

6.反序列化后类中static型变量的值实际上是当前JVM中对应static变量的值,这个值是JVM中的并不是反序列化得出的。

7.被transient关键字修饰导致不被序列化,其优点是可以节省存储空间。优化程序!随之而来的是会导致被transient修饰的字段会重新计算,初始化!

免责声明
本站部分资源来源于互联网 如有侵权 请联系站长删除
龙哥网是优质的互联网科技创业资源_行业项目分享_网络知识引流变现方法的平台为广大网友提供学习互联网相关知识_内容变现的方法。