言成言成啊 | Kit Chen's Blog

关键字transient

发布于2022-02-12 23:51:42,更新于2022-02-13 23:40:51,标签:java  文章会持续修订,转载请注明来源地址:https://meethigher.top/blog

抄袭来源

  1. java关键字之transient - 知乎
  2. Java中关键字transient解析 - 知乎
  3. 静态成员变量能不能被序列化????-CSDN社区
  4. Java中创建对象的5种方式 - _1900 - 博客园

一、transient概念

transient只能用来修饰成员变量(field),被transient修饰的成员变量不参与序列化过程。

Java中的对象如果想要在网络上传输或者存储在磁盘时,就必须要序列化。Java中序列化的本质是Java对象转换为字节序列。但是在序列化的过程中,可以允许被序列对象中的某个成员变量不参与序列化,即该对象完成序列化之后,被transient修饰的成员变量会在字节序列中消失。

通过阅读序列化接口Serializable,可知,我们可以通过读写流来模拟序列化与反序列化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
public class Person implements Serializable {

private transient String name;

private static String sex;

private String nickName;

public Person(String name, String sex, String nickName) {
this.name = name;
this.sex = sex;
this.nickName = nickName;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getSex() {
return sex;
}

public void setSex(String sex) {
this.sex = sex;
}

public String getNickName() {
return nickName;
}

public void setNickName(String nickName) {
this.nickName = nickName;
}

@Override
public String toString() {
return String.format("Halo, My name is %s, my sex is %s, " +
"my nickName is %s",
name,
sex,
nickName);
}


public static void main(String[] args) throws Exception {
Person person = new Person("向晚", "girl", "Ava");
System.out.println(String.format("序列化前:%s",person));

//序列化后写入到磁盘里,再读取出来反序列化。添加transient的字段,在序列化时,就已经忽略掉了
File file = new File("C:\\Users\\meethigher\\Desktop\\aaa\\src\\main\\resources\\index.txt");
ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream(file));
os.writeObject(person);

ObjectInputStream is = new ObjectInputStream(new FileInputStream(file));
person = (Person) is.readObject();
System.out.println(String.format("序列化后:%s",person));

//通过fastjson模拟序列化,添加transient的字段不参与序列化
String s = JSON.toJSONString(person);
System.out.println(String.format("使用fastjson序列化后的内容:%s",s));


}
}

运行结果

1
2
3
序列化前:Halo, My name is 向晚, my sex is girl, my nickName is Ava
序列化后:Halo, My name is null, my sex is girl, my nickName is Ava
使用fastjson序列化后的内容:{"nickName":"Ava","sex":"girl"}

可以看出,使用transient关键字修饰的成员变量没有被序列化。

通过Serializable的注释中,可知,其实static和transient的变量都不能序列化。

但是上面的例子中,static看上去也被序列化了。但并不是。

原因是因为静态变量是属于类的,只要这个类的静态变量值没被变过,创建多少个对象,都是该值。

我们可以给Person,再加一个构造函数,不去改变静态变量的值。

1
2
3
4
5
6
7
8
public static void main(String[] args) {
Person person = new Person("a", "init", "a");
System.out.println(person);
Person person1 = new Person("b", "b");
System.out.println(person1);
Person person2 = new Person("c", "c");
System.out.println(person2);
}

输出结果

1
2
3
Halo, My name is a, my sex is init, my nickName is a
Halo, My name is b, my sex is init, my nickName is b
Halo, My name is c, my sex is init, my nickName is c

序列化写入文件时,其实静态变量的值没写进去,那么读取出来时,就相当于创建了一个对象(创建对象,需要了解创建对象的几种方式,像反序列化,是不需要通过构造函数的)。

反序列出来时,由于静态变量的值没有发生改变,所以看样子像是又反序列化出来了一样。

我们新建另一个Test类,读取Person类写入的index.txt文件。

1
2
3
4
5
6
7
8
9
10
public class Test {

public static void main(String[] args) throws Exception {
File file = new File("C:\\Users\\meethigher\\Desktop\\aaa\\src\\main\\resources\\index.txt");

ObjectInputStream is = new ObjectInputStream(new FileInputStream(file));
Person person = (Person) is.readObject();
System.out.println(String.format("序列化后:%s",person));
}
}

就能看出,其实static修饰的变量也没有被序列化

1
序列化后:Halo, My name is null, my sex is null, my nickName is Ava

二、transient本质

为什么要有transient?

毫无疑问,这是一个平常的编程语言设计思路,即实现两种编码转化的时候,我们希望用户在转化过程中可以控制一些内容。

理解transient的关键在于理解序列化,序列化是Java对象转换为字节序列。

详细的说,就是Java对象在电脑中是存于内存之中的,内存之中的存储方式毫无疑问和磁盘中的存储方式不同(一个显而易见的区别就是对象在内存中的存储分为堆和栈两部分,两部分之间还有指针;但是存到磁盘中肯定不可能带指针,一定是某种文本形式)。序列化和反序列化就是在这两种不同的数据结构之间做转化。

序列化:JVM中的Java对象转化为字节序列。

反序列化:字节序列转化为JVM中的Java对象。

理解到这里,实现原理也是显而易见的,只要在处理两个数据结构转化的过程中,把标为transient的成员变量特殊处理一下就好了。

三、自定义序列化

如果想要实现自定义的序列化,我们可以实现Externalizable接口。

通过实现接口,在重写方法时,指定我们想要序列化的字段。在这种情况下,static与transient都不再生效。

测试发现fastjson不支持Externalizable

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
public class Person implements Externalizable {

private transient String name;

private static String sex;

private String nickName;

public Person() {
}

public Person(String name, String sex, String nickName) {
this.name = name;
this.sex = sex;
this.nickName = nickName;
}

public Person(String name, String nickName) {
this.name = name;
this.nickName = nickName;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getSex() {
return sex;
}

public void setSex(String sex) {
this.sex = sex;
}

public String getNickName() {
return nickName;
}

public void setNickName(String nickName) {
this.nickName = nickName;
}

@Override
public String toString() {
return String.format("Halo, My name is %s, my sex is %s, " +
"my nickName is %s",
name,
sex,
nickName);
}


public static void main(String[] args) throws Exception {
Person person = new Person("向晚", "girl", "Ava");
System.out.println(String.format("序列化前:%s", person));

//序列化后写入到磁盘里,再读取出来反序列化。添加transient的字段,在序列化时,就已经忽略掉了
File file = new File("C:\\Users\\meethigher\\Desktop\\aaa\\src\\main\\resources\\index.txt");
ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream(file));
os.writeObject(person);

ObjectInputStream is = new ObjectInputStream(new FileInputStream(file));
person = (Person) is.readObject();
System.out.println(String.format("序列化后:%s", person));

//通过fastjson模拟序列化,添加transient的字段不参与序列化
String s = JSON.toJSONString(person);
System.out.println(String.format("使用fastjson序列化后的内容:%s", s));


}

@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeUTF(nickName);
out.writeUTF(name);
out.writeUTF(sex);
}

@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
nickName=in.readUTF();
name=in.readUTF();
sex=in.readUTF();
}
}

write与read要保持顺序一致

输出结果

1
2
3
序列化前:Halo, My name is 向晚, my sex is girl, my nickName is Ava
序列化后:Halo, My name is 向晚, my sex is girl, my nickName is Ava
使用fastjson序列化后的内容:{"nickName":"Ava","sex":"girl"}
发布:2022-02-12 23:51:42
修改:2022-02-13 23:40:51
链接:https://meethigher.top/blog/2022/transient/
标签:java 
付款码 打赏 分享
Shift+Ctrl+1 可控制工具栏