反序列化对象与原始对象的实例相同

时间:2015-03-14 17:14:52

标签: java serialization

当我从类中实例化一个对象时,一个对象被保存在java堆中。当我通过序列化保存对象并稍后反序列化对象时,我是否正确理解该对象现在将具有新的堆地址但仍将是该类的EXACT SAME实例。

5 个答案:

答案 0 :(得分:21)

你的问题的答案不能只是肯定或否定。分析这个概念是必需的。我会建议你拿一支铅笔和一张纸自己做,并牢记以下几点。

  • 所有java对象都是在java堆中创建的(除了一些是 保持在游泳池,但对于你的问题,我们现在将跳过它们。)
  • 使用new关键字创建类的实例时, 反序列化,克隆方法或反射api的newInstance方法, 堆中的新空间被保留,我们将其分配给对象引用 (引用可以是对象的类,也可以是超类之一 对象类的类 - 我们再次可以忽略这个细节 现在)。
  • 保存对象时,对象的状态将全部保存 嵌套对象。
  • 反序列化对象时,对象将创建一个新条目 在堆中,它不会引用任何对象。

请查看下图,了解您在上下文中的上述概念:

enter image description here

所有对象A引用都指向一个堆条目,如果您尝试使用objectB.getObjectA()== objectC.getObjectA()或任何其他此类操作,您将得到真实。

案例1 当您单独保存对象并反序列化它们时,堆中会发生什么:

enter image description here

正如你现在可以弄清楚的那样,objectBcopy.getObjectA()== objectCcopy.getObjectA()不会返回true,因为复制对象的对象A的引用不再相同。

案例2 相反,当您将对象保存在单个文件中并稍后对其进行反序列化时,堆中会发生以下情况:

enter image description here

正如你现在可以弄清楚的那样,objectBcopy.getObjectA()== objectCcopy.getObjectA()现在将为true,因为对象A副本的引用是相同的,但它仍然是对象A的新副本。

支持我的扣除的快速计划(案例1和案例2):

public class Test{

    public static void main (String args[]) throws IOException, ClassNotFoundException{
        A a = new A();

        B b = new B();
        b.a = a;

        C c = new C();
        c.a = a;

        System.out.println("b.a == c.a is " + (b.a == c.a));

        // Case 1 - when two diferent files are used to write the objects
        FileOutputStream fout = new FileOutputStream("c:\\b.ser");
        ObjectOutputStream oos = new ObjectOutputStream(fout);
        oos.writeObject(b);
        oos.close();
        fout.close();

        fout = new FileOutputStream("c:\\c.ser");
        oos = new ObjectOutputStream(fout);
        oos.writeObject(c);
        oos.close();
        fout.close();

        FileInputStream fileIn = new FileInputStream("c:\\b.ser");
        ObjectInputStream in = new ObjectInputStream(fileIn);
        B bCopy = (B) in.readObject();
        in.close();
        fileIn.close();

        fileIn = new FileInputStream("c:\\c.ser");
        in = new ObjectInputStream(fileIn);
        C cCopy = (C) in.readObject();
        in.close();
        fileIn.close();
        System.out.println("Case 1 - bCopy.a == cCopy.a is " + (bCopy.a == cCopy.a));

        // Case 2 - when both the objects are saved in the same file
        fout = new FileOutputStream("c:\\both.ser");
        oos = new ObjectOutputStream(fout);
        oos.writeObject(b);
        oos.writeObject(c);
        oos.close();
        fout.close();


        fileIn = new FileInputStream("c:\\both.ser");
        in = new ObjectInputStream(fileIn);
        bCopy = (B) in.readObject();
        cCopy = (C) in.readObject();
        in.close();
        fileIn.close();
        System.out.println("Case 2 - bCopy.a == cCopy.a is " + (bCopy.a == cCopy.a));
    }
}

class A implements Serializable{

}

class B implements Serializable{
    A a;
}

class C implements Serializable{
    A a;
}

使用以下输出:

 b.a == c.a is true
 Case 1 - bCopy.a == cCopy.a is false
 Case 2 - bCopy.a == cCopy.a is true

答案 1 :(得分:3)

序列化之前:

A originalA = ...;
B.a == C.a == D.a == E.a == originalA

所有B.aC.aD.aE.a都指向AoriginalA的相同引用。

序列化和反序列化后:

A otherA = ...;
B.a == C.a == D.a == E.a == otherA

所有B.aC.aD.aE.a都指向AotherA的相同引用。

然而:

originalA != otherA

虽然

originalA.equals(otherA) == true

注意: equals()只有在覆盖 始终检查基于序列化的相等性时才会返回true领域。否则,可能会返回false


修改

证明:

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class Sample {

    static class A implements Serializable {
        private static final long serialVersionUID = 1L;
    }

    static class B implements Serializable {
        private static final long serialVersionUID = 1L;

        A a;
    }

    static class C implements Serializable {
        private static final long serialVersionUID = 1L;

        A a;
    }

    public static void main(String args[]) throws IOException, ClassNotFoundException {
        A originalA = new A();

        B b = new B();
        b.a = originalA;

        C c = new C();
        c.a = originalA;

        System.out.println("b.a == c.a is " + (b.a == c.a));

        FileOutputStream fout = new FileOutputStream("ser");
        ObjectOutputStream oos = new ObjectOutputStream(fout);
        oos.writeObject(b);
        oos.writeObject(c);
        oos.close();
        fout.close();

        FileInputStream fileIn = new FileInputStream("ser");
        ObjectInputStream in = new ObjectInputStream(fileIn);
        B bDeserialized = (B) in.readObject();
        C cDeserialized = (C) in.readObject();
        in.close();
        fileIn.close();

        System.out.println("bDeserialized.a == cDeserialized.a is " + (bDeserialized.a == cDeserialized.a));
    }
}

答案 2 :(得分:1)

不,简单的回答是反序列化的对象在内存中不是同一个实例!它将为同一个分配新的内存。还要浏览http://www.javalobby.org/java/forums/t17491.html链接,其中包含使用单例反序列化检索对象的示例!同样深入了解readResolve()方法,在某些情况下会有所帮助。

答案 3 :(得分:1)

反序列化的实例肯定是与原始实例不同的实例,因为deserialized != original将始终为真。

反序列化的实例可能与原始实例相同,也可能不同,如deserialized.equals(original)中所示。为了合理地实现Serializable类,反序列化后equals可能会成立,但创建一个不适用的类是微不足道的:

class Pathological implements Serializable {
  transient int value;

  Pathological(int value) { this.value = value; }

  @Override public int hashCode() { return value; }

  @Override public boolean equals(Object other) {
    if (other == this) { return true; }
    if (other instanceof Pathological) {
      return ((Pathological) other).value == this.value;
    }
    return false;
  }
}

除非在构造Pathological时碰巧传递零,否则序列化/反序列化后实例不会相等,因为value的值不会被序列化(如这是短暂的。)

答案 4 :(得分:1)

不,它们不会是内存中的同一个对象originalObj == deserilized将是错误的,但originalObj.equals(deserilized)应该是真的。

  

对象B,C,D和E.所有这些对象在实例化时都有对象A.然后,让我们假设我对所有这些进行序列化和反序列化。当我在反序列化后更改对象A的字段时,有没有办法在BCDE中的对象A中反映这种变化?

如果我理解正确答案为否,则引用将不会指向相同的对象A

但是,如果您愿意,可以在每个对象B,C,D和E中明确设置对象A的所有引用,以指向对象A的相同实例

这是一个演示来说明所提出的观点。

import java.io.*;
import java.util.*;

public class Demo {  
  public static void main(String... aArguments) {  
    List<Quark> quarks = Arrays.asList(
      new Quark("up"), new Quark("down")
    );

    serialize(quarks);
    List<Quark> recoveredQuarks = deserialize();

    System.out.println(quarks == recoveredQuarks);               // false
    System.out.println(quarks.equals(recoveredQuarks));          // true

    System.out.println(quarks.get(0) == recoveredQuarks.get(0)); // false

    // but you can set it to the same instance
    recoveredQuarks.set(0, quarks.get(0));
    System.out.println(quarks.get(0) == recoveredQuarks.get(0)); // true

    quarks.get(0).name = "Charm";
    boolean b = quarks.get(0).name == recoveredQuarks.get(0).name;
    System.out.println(b);                                       // true
  }

  static void serialize(List<Quark> quarks) {
    try {
      OutputStream file = new FileOutputStream("quarks.ser");
      OutputStream buffer = new BufferedOutputStream(file);
      ObjectOutput output = new ObjectOutputStream(buffer);
      output.writeObject(quarks);
      output.close();
    }
    catch(IOException ex) { ex.printStackTrace(); }
  }

  static List<Quark> deserialize() {
    List<Quark> recoveredQuarks = null;
    try {
      InputStream file = new FileInputStream("quarks.ser");
      InputStream buffer = new BufferedInputStream(file);
      ObjectInput input = new ObjectInputStream(buffer);
      recoveredQuarks = (List<Quark>)input.readObject();
      input.close();
    } 
    catch(ClassNotFoundException ex){ }
    catch(IOException ex){ ex.printStackTrace(); }
    return recoveredQuarks;
  }
}

class Quark implements Serializable {
    String name;
    Quark(String name) {
      this.name = name;
    }

    @Override
    public boolean equals(Object o) {
      if (o != null && o instanceof Quark) {
        return this.name.equals(((Quark)o).name);
      }
      return false;
    }
}