问题
I was reading about transient and final keyword and I found the answer that we can't use transient keyword with final keyword. I tried and got confused because here it working fine.
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){/**/}
}
}
Here is the Serializable Student class Code:
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);
}
}
Code output with transient keyword.
ABC04
DEF04
Output without transient keyword.
ABC34
DEF64
Can you explain why it's working fine? is there a bug?
At the end what should be behavior of transient with final keyword?
回答1:
Your question is somewhat a duplicate of this:
- final-transient-fields-and-serialization
- a-transient-final-field-used-as-a-lock-is-null
A final field must be initialized either by direct assignment of an initial value or in the constructor. During deserialization, neither of these are invoked, so initial values for transients must be set in the 'readObject()' private method that's invoked during deserialization. And for that to work, the transients must be non-final.
and
Any field that is declared transient is not serialized. Moreover, according to this blog post, field values are not even initialized to the values that would be set by a default constructor. This creates a challenge when a transient field is final.
As for the results of your test:
Code output with transient keyword. ABC04 DEF04
Output without transient keyword. ABC34 DEF64
transient
Clearly, the transient
field (4th character) is not being serialized/deserialized (ABC34->ABC04 and DEF64->DEF04)
static
The static
field (5th char) is also not being deserialized! It's simply because your are performing the operation in the same memory space, and the static field remains across all instances. So when you set the static field on student, and later deserialize another student, of course the static field still has the same value!
This also explain why in your test you first set the static field to 2
and
then 4
, but only 4
gets printed. Nothing to do with serialization in this case, simply static field behavior.
回答2:
Your conclusion that your example works, is wrong.
- The field
name
is nottransient
, therefore correctly stored, restored and printed. - The field
marks
is declaredstatic
, hence not part of the object’s state and never stored nor restored. It always shows the last written value,4
, despite your first object has written2
to it, because the second object overwrites it with4
before your test even starts. - Only the field
id
is atransient
instance field which is not stored, thus, showing the default value0
. When removing thetransient
keyword, it gets stored and restored, showing3
for the first and6
for the second object.
Maybe it would help you, if you add spacing or even the identifiers like"name="+name+", id="+id+""+", marks="+marks
in the string representation
returned by toString()
.
To add another corner case, producing counter-intuitive behavior, if you add a field
transient final int foo = 42;
to your class, you will also experience it to show the correct value after restoring, because it is a compile-time constant. So any code referencing this variable will invariably use the constant value and never actually read the instance field, so the fact that it wasn’t restored stays unnoticed. But, of course, such a constant is better declared static
to avoid wasting memory for a never-read instance field.
Another perhaps surprising example would be declaring transient final
fields in an enum
. They will always appear to show the correct value, because the state of enum
objects is never stored, but the actual, already initialized, constant objects of that enum
type are resolved when deserializing an enum
value.
来源:https://stackoverflow.com/questions/37614088/how-transient-works-with-final-in-serialization-java