dou*_*lep 59 java serialization final
final transient在Java中进行序列化后,是否可以将字段设置为任何非默认值?我的用例是一个缓存变量 - 这就是它的原因transient.我也习惯于制作Map不会改变的字段(即地图的内容被改变,但是对象本身保持不变)final.但是,这些属性似乎是矛盾的 - 虽然编译器允许这样的组合,但我不能将字段设置为除了反null序列化之外的任何东西.
我尝试了以下内容,没有成功:
readObject()- 因为字段是,所以无法完成final.在该示例cache中public仅用于测试.
import java.io.*;
import java.util.*;
public class test
{
public static void main (String[] args) throws Exception
{
X x = new X ();
System.out.println (x + " " + x.cache);
ByteArrayOutputStream buffer = new ByteArrayOutputStream ();
new ObjectOutputStream (buffer).writeObject (x);
x = (X) new ObjectInputStream (new ByteArrayInputStream (buffer.toByteArray ())).readObject ();
System.out.println (x + " " + x.cache);
}
public static class X implements Serializable
{
public final transient Map <Object, Object> cache = new HashMap <Object, Object> ();
}
}
Run Code Online (Sandbox Code Playgroud)
输出:
test$X@1a46e30 {}
test$X@190d11 null
Run Code Online (Sandbox Code Playgroud)
mdm*_*dma 34
不幸的是,简短的答案是"不" - 我经常想要这个.但瞬态不可能是最终的.
必须通过直接赋值初始值或在构造函数中初始化final字段.在反序列化期间,这些都不会被调用,因此必须在反序列化期间调用的"readObject()"私有方法中设置瞬态的初始值.为了实现这一目标,瞬态必须是非最终的.
(严格来说,决赛只是在他们第一次阅读时才是最终的,所以有些黑客可以在阅读之前分配一个值,但对我来说这是一步太远了.)
Pin*_*juh 16
您可以使用Reflection更改字段的内容.适用于Java 1.5+.它可以工作,因为序列化是在一个线程中执行的.在另一个线程访问同一个对象之后,它不应该更改最终字段(因为内存模型和reflaction中的怪异).
所以,在readObject(),你可以做类似这个例子的事情:
import java.lang.reflect.Field;
public class FinalTransient {
private final transient Object a = null;
public static void main(String... args) throws Exception {
FinalTransient b = new FinalTransient();
System.out.println("First: " + b.a); // e.g. after serialization
Field f = b.getClass().getDeclaredField("a");
f.setAccessible(true);
f.set(b, 6); // e.g. putting back your cache
System.out.println("Second: " + b.a); // wow: it has a value!
}
}
Run Code Online (Sandbox Code Playgroud)
记住:决赛不再是决赛!
Boa*_*ann 14
是的,通过实现(显然鲜为人知的!)readResolve()方法很容易实现.它允许您在反序列化后替换对象.您可以使用它来调用构造函数,以便根据需要初始化替换对象.一个例子:
import java.io.*;
import java.util.*;
public class test {
public static void main(String[] args) throws Exception {
X x = new X();
x.name = "This data will be serialized";
x.cache.put("This data", "is transient");
System.out.println("Before: " + x + " '" + x.name + "' " + x.cache);
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
new ObjectOutputStream(buffer).writeObject(x);
x = (X)new ObjectInputStream(new ByteArrayInputStream(buffer.toByteArray())).readObject();
System.out.println("After: " + x + " '" + x.name + "' " + x.cache);
}
public static class X implements Serializable {
public final transient Map<Object,Object> cache = new HashMap<>();
public String name;
public X() {} // normal constructor
private X(X x) { // constructor for deserialization
// copy the non-transient fields
this.name = x.name;
}
private Object readResolve() {
// create a new object from the deserialized one
return new X(this);
}
}
}
Run Code Online (Sandbox Code Playgroud)
输出 - 保留字符串,但瞬态映射重置为空(但非空!)映射:
Before: test$X@172e0cc 'This data will be serialized' {This data=is transient}
After: test$X@490662 'This data will be serialized' {}
Run Code Online (Sandbox Code Playgroud)
这类问题的一般解决方案是使用"串行代理"(参见Effective Java 2nd Ed).如果您需要在不破坏串行兼容性的情况下将其改装为现有的可序列化类,那么您将需要进行一些黑客攻击.
| 归档时间: |
|
| 查看次数: |
23388 次 |
| 最近记录: |