tua*_*ong 3 java serialization
我阅读了“Effective Java”,有人告诉我,Serialization Proxy Pattern 与对象图包含循环的类不兼容。其实我不明白。所以我写了一个样本来验证它。正如你看到的:
public class A implements Serializable{
private B b;
public A(){
}
public A(B b){
this.b = b;
}
}
public class B implements Serializable{
private A a;
public B(){
}
public B(A a){
this.a = a;
}
}
public class C implements Serializable{
private A a;
private B b;
public C(A a, B b){
this.a = a;
this.b = b;
}
private Object writeReplace(){
return new SerializationProxy(this);
}
private Object readObject(ObjectInputStream in) throws InvalidObjectException{
throw new InvalidObjectException("Proxy Required!");
}
private static final class SerializationProxy implements Serializable{
private final A a;
private final B b;
SerializationProxy(C c){
this.a = c.a;
this.b = c.b;
}
private Object readResolve(){
return new C(a, b);
}
}
public static void main(String args[]) throws Exception{
C c = new C(new A(), new B());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(baos);
out.writeObject(c);
ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));
c = (C)in.readObject();
}
}
Run Code Online (Sandbox Code Playgroud)
然而,它执行得非常好。所以,任何人都可以向我进一步解释。非常感谢。
小智 6
问题通常不在于循环引用,而在于当您引用对象图中的根对象时的特定情况。在下面的示例中,Container 有一个 Items 列表,但列表中的每个项目都有对 Container(对象图中的根对象!)的引用,因为它是父对象。
如果执行此代码,输出将是 ClassCastException 的堆栈跟踪。但是,如果您评论该行item.setParent(this); 从 Container 的 item setter 中再次运行代码,输出会很好。
它发生的原因是使用 writeReplace() 实现序列化的方式。当您用 SerializationProxy 替换原始对象时,然后在 SerializationProxy 的流中,JVM 将使用对 SerializationProxy 对象的引用替换对原始对象的任何引用。(我不知道为什么它是这样实现的,但确实如此)。 http://docs.oracle.com/javase/6/docs/platform/serialization/spec/input.html#5903
在我们的例子中,我们将项目放在 SerializationProxy 对象中,但项目有对原始对象(它们的父对象)的引用。然后当你想要反序列化一个流时,反序列化的 SerializationProxy 的引用被分配给 Item 的父字段中的原始对象的引用,并且你有一个 ClassCastException。
public class Main {
public static void main(String[] args) throws Exception {
try {
Container c = new Container("Circular References Test");
c.addItem(new Item("Item 1"));
c.addItem(new Item("Item 2"));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(baos);
out.writeObject(c);
ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));
c = (Container)in.readObject();
System.out.println("Container c has label '" + c.getLabel() + "' and has " + c.getItems().size() + " items.");
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
class Container implements Serializable {
private static final long serialVersionUID = 1L;
private List<Item> items = new ArrayList<>();
private String label = "";
public Container() {
}
public Container(String label) {
this.label = label;
}
public String getLabel() {
return this.label;
}
public void setLabel(String label) {
this.label = label;
}
public boolean addItem(Item item) {
if(item != null) {
item.setParent(this);
return this.items.add(item);
} else {
return false;
}
}
public boolean removeItem(Item item) {
if(item != null) {
item.setParent(null);
return this.items.remove(item);
} else {
return false;
}
}
public List<Item> getItems() {
return new ArrayList<Item>(this.items);
}
private static final class SerializationProxy implements Serializable {
private static final long serialVersionUID = 1L;
private String containerLabel = "";
private List<Item> items;
public SerializationProxy(Container c){
this.containerLabel = c.getLabel();
this.items = new ArrayList<>(c.getItems());
}
private Object readResolve(){
Container c = new Container(this.containerLabel);
for(int i = 0; this.items != null && i < this.items.size(); i++) {
c.addItem(this.items.get(i));
}
return c;
}
}
private Object writeReplace(){
return new SerializationProxy(this);
}
private Object readObject(ObjectInputStream in) throws InvalidObjectException{
throw new InvalidObjectException("Proxy Required!");
}
}
class Item implements Serializable {
private static final long serialVersionUID = 1L;
private Container parent = null;
private String name = "";
public Item() {
}
public Item(String name) {
this.name = name;
}
public Container getParent() {
return this.parent;
}
public void setParent(Container parent) {
this.parent = parent;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
Run Code Online (Sandbox Code Playgroud)