java关键字之transient

一、作用

  • 被transient修饰的变量不参与序列化和反序列化

  • transient关键字只能修饰变量,而不能修饰方法和类。注意,局部变量是不能被transient关键字修饰的。

  • 一个静态变量不管是否被transient修饰,均不能被序列化。

二、实践测试

import java.io.Serializable;
​
public class User implements Serializable{
​
    /**
     * 自动生成的序列号
     */
    //private static final long serialVersionUID = 3810909334566607723L;
    private static final long serialVersionUID = 1L;
    
    private String name;
    private transient int age;
    public static String sex;
    public static transient String password;
    
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public static String getSex() {
        return sex;
    }
    public static void setSex(String sex) {
        User.sex = sex;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public static String getPassword() {
        return password;
    }
    public static void setPassword(String password) {
        User.password = password;
    }
​
    public String show() {
        return "姓名:"+name+"\t"+"密码:"+password+"\t"+"性别:"+sex+"\t"+"年龄:"+age;
    }
    
}
​
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
​
import com.depart.entity.User;
​
public class TestTransient {
    
    @SuppressWarnings("resource")
    public static void main(String[] args) {
        User user = new User();
        user.setAge(22);
        user.setName("chenfuzhe");
        User.setPassword("csdn");//设置静态变量的值
        User.setSex("man");//设置静态变量的值
        System.out.println(user.show());
        try {
            //序列化
            ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("tempFile"));
            outputStream.writeObject(user);
            outputStream.close();
            
            System.out.println("==========我是一条温暖的分隔线==========");
            
            //反序列化
            ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("tempFile"));
            User userOut = (User)inputStream.readObject();
            
            System.out.println(userOut.show());
            //姓名:chenfuzhe  密码:csdn 性别:man  年龄:0,其中输出了密码和性别的值,年龄没有值
            //输出结果为什么有值呢,难道static能被序列化?不是的,其实是因为在反序列化之前set了值,
            //相当于在给静态变量User.sex设置了值,在show()中,我调用就是静态变量的值,所以密码和性别被打印出来了,
            //当把反序列化移到另外一个测试类中,也就是不在同一线程中时,静态变量User.sex也就没有值了
            
            
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        
    }
    
}
​
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
​
import com.depart.entity.User;
​
public class TestTransient2 {
​
    public static void main(String[] args) {
        ObjectInputStream inputStream;
        User userOut;
        try {
            //反序列化
            inputStream = new ObjectInputStream(new FileInputStream("tempFile"));
            userOut = (User)inputStream.readObject();
            System.out.println(userOut.show());
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
    }
​
}
​
​

三、序列化扩展

1. 用法

序列化时,被序列化的类需要实现一个Serializable 接口,这个接口是空接口,没有任何方法,相当于一个标识,同时,需要生成一个 serialVersionUID ,且必须是静态常量,如:private static final long serialVersionUID = 3810909334566607723L;

注意 :使用static final 修饰,final修饰是为了避免修改,而使用private修饰则使得值难以被获取。

2. 问题

如果我们不生成,序列化时,java会根据class文件自动生成一个serialVersionUID ,同时在反序列化时再根据class文件生成一个serialVersionUID,值是一样的,反序列化成功,通过这种前后对比的方式,可以保证了程序的安全性,可以防止外部文件被修改,从而携带恶意脚本,造成程序错误的问题。 但是如果我们在序列化后修改了java文件的serialVersionUID,class文件也发生了改变,序列化和反序列化前后的class文件不一样,serialVersionUID也不一样,就会抛出InvalidClassException异常

例如:当我们不声明类的serialVersionUID,序列化成功后,修改java文件的serialVersionUID = 3810909334566607723,再去反序列化,就会出现如下异常

java.io.InvalidClassException: com.depart.entity.User; local class incompatible: stream classdesc serialVersionUID = 8733796526853314695, local class serialVersionUID = 3810909334566607723
    at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:699)
    at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:2001)
    at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1848)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2158)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1665)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:501)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:459)
    at com.depart.test.TestTransient2.main(TestTransient2.java:17)

3. serialVersionUID获取

使用serialver 命令生成serialVersionUID

注意:类中没有声明serialVersionUID,他会自动生成一个值,如:private static final long serialVersionUID = 3810909334566607723L; 如果有会返回声明的 serialVersionUID, 如:private static final long serialVersionUID = 1L;

image-20220910200609713

4. static 和 transient在序列化和反序列化的区别

transient 关键字修饰的变量在序列化时,会被一同序列化,但是值会被清空,所以反序列化出来就是默认值了

static 关键字修饰的变量在序列化时,不会被序列化,反序列化后,显示的值只是默认值

image-20240310181056774

image-20240310181107002