我正在阅读有关transient和final关键字的内容,我发现答案是我们不能使用带有final关键字的transient关键字。我试过并感到困惑,因为它在这里工作正常。
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import java.io.Serializable;
public class SerExample{
public static void main(String... args){
Student foo = new Student(3,2,"ABC");
Student koo = new Student(6,4,"DEF");
try
{
FileOutputStream fos = new FileOutputStream("abc.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(foo);
oos.writeObject(koo);
oos.close();
fos.close();
}
catch(Exception e){/**/}
try{
FileInputStream fis = new FileInputStream("abc.txt");
ObjectInputStream ois = new ObjectInputStream(fis);
System.out.println(ois.readObject());
System.out.println(ois.readObject());
fis.close();
ois.close();
}catch(Exception e){/**/}
}
}
这是Serializable Student类代码:
class Student implements Serializable{
private transient final int id;
private transient static int marks;
private String name;
public Student(int id, int marks, String name){
this.id = id;
this.marks = marks;
this.name = name;
}
public Student(){
id=0;
}
@Override
public String toString(){
return (this.name + this.id + this.marks);
}
}
带有transient关键字的代码输出。
ABC04
DEF04
没有临时关键字的输出。
ABC34
DEF64
你能解释为什么它工作正常吗?有没有错误?
最后应该使用final关键字进行瞬态行为?
答案 0 :(得分:6)
您的问题与此有些重复:
必须通过直接赋值初始值或在构造函数中初始化final字段。在反序列化期间,这些都不会被调用,因此必须在' readObject()'中设置瞬态的初始值。在反序列化期间调用的私有方法。为了实现这一目标,瞬态必须是非最终的。
和
任何声明为瞬态的字段都不是序列化的。此外,根据此博客文章,字段值甚至不会初始化为默认构造函数设置的值。当瞬态场是最终的时,这会产生挑战。
带有transient关键字的代码输出。 ABC04 DEF04
输出没有临时关键字。 ABC34 DEF64
显然,Session.Abandon()
字段(第4个字符)未被序列化/反序列化(ABC 3 4-> ABC 0 4且DEF 6 强> 4-> DEF的 0 强> 4)
transient
字段(第5个字符)也没有被反序列化!这只是因为您在同一个内存空间中执行操作,静态字段保留在所有实例中。因此,当您在学生上设置静态字段,然后反序列化另一个学生时,静态字段当然仍然具有相同的值!
这也解释了为什么在测试中首先将静态字段设置为static
和
然后2
,但只打印4
。在这种情况下,与序列化无关,只是静态字段行为。
答案 1 :(得分:2)
你的例子有效的结论是错误的。
name
不是transient
,因此正确存储,恢复和打印。marks
声明为static
,因此不属于对象的状态,也从不存储或恢复。它始终显示最后写入的值4
,尽管您的第一个对象已向其写入2
,因为第二个对象会在您的测试开始之前用4
覆盖它。id
是未存储的transient
实例字段,因此显示默认值0
。删除transient
关键字后,系统会对其进行存储和恢复,第一个显示3
,第二个对象显示6
。如果你添加间距甚至是像
这样的标识符,也许它会对你有所帮助
字符串表示中的"name="+name+", id="+id+""+", marks="+marks
由toString()
返回。
如果添加字段
,则添加另一个案例,产生反直觉行为transient final int foo = 42;
到您的课程,您还将体验它在恢复后显示正确的值,因为它是一个编译时常量。因此,引用此变量的任何代码都将始终使用常量值,并且永远不会实际读取实例字段,因此未恢复的事实不会被忽视。但是,当然,这样的常量更好地声明为static
,以避免为永不读取的实例字段浪费内存。
另一个可能令人惊讶的例子是在transient final
中声明enum
字段。它们总是显示正确的值,因为永远不会存储enum
个对象的状态,但在反序列化enum
时会解析enum
类型的实际已初始化的常量对象}值。