我读过“Effective Java”,我被告知序列化代理模式与对象图包含圆形的类不兼容。其实我不明白。所以我写了一个样本来验证它。如你所见:
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();
}
}
然而,它执行得非常好。所以,任何人都可以向我进一步解释。非常感谢。
答案 0 :(得分:3)
问题不在于循环引用,而在于具体情况 您可以引用对象图中的根对象。 在以下示例中,Container具有Items列表,但列表中的每个项目都有 对于Container的引用(对象图中的根对象!),因为它是父对象。
如果执行此代码,则输出将为ClassCastException的堆栈跟踪。 但是,如果您从Container的项目设置器中注释 item.setParent(this); 行 并再次运行代码,输出就可以了。
它发生的原因是使用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;
}
}