【问题标题】:Trouble accessing fields of a serialized object in Java在 Java 中访问序列化对象的字段时遇到问题
【发布时间】:2011-06-09 20:43:57
【问题描述】:

我已经实例化了一个 implements Serializable 的类,我正在尝试像这样流式传输该对象:

try{
    Socket socket = new Socket("localhost", 8000);
    ObjectOutputStream toServer = new ObjectOutputStream(socket.getOutputStream());
    toServer.writeObject(myObject);
} catch (IOException ex) {
    System.err.println(ex);
}

到目前为止一切都很好,对吧?然后我尝试像这样读取该对象的字段:

//This is an inner class
class HandleClient implements Runnable{

private ObjectInputStream fromClient;
private Socket socket; // This socket was established earlier

try {
    fromClient = new ObjectInputStream(socket.getInputStream());
    GetField inputObjectFields = fromClient.readFields();

    double myFristVariable = inputObjectFields.get("myFirstVariable", 0);
    int mySecondVariable = inputObjectFields.get("mySecondVariable", 0);

    //do stuff

    } catch (IOException ex) {
        System.err.println(ex);
    } catch (ClassNotFoundException ex) {
        System.err.println(ex);
    } finally {
        try {
            fromClient.close();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}

但我总是得到错误:

java.io.NotActiveException: not in call to readObject

这是我第一次流式传输对象而不是原始数据类型,我做错了什么?

奖金

当我让它正常工作时,整个类是否与序列化对象一起传递(即我可以访问对象类的方法)吗?我的阅读表明整个类与对象一起传递,但到目前为止我一直无法使用对象方法。我究竟如何调用对象的方法?

除了上面的代码之外,我还尝试了readObject 方法,但我可能也用错了,因为我无法让它工作。请赐教。

【问题讨论】:

    标签: java serialization object stream


    【解决方案1】:

    查看ObjectInputStream.readFields() 的代码,调用该异常是因为curContext 字段为空。您应该在调用readFields() 之前调用fromClient.readObject(),因为它将设置curContext。请注意,readObject() 将返回正在序列化的实例,这可能对您更有用。

    【讨论】:

      【解决方案2】:

      回答你的第一个问题:

      需要使用ObjectInputStream.readObject进行反序列化。您无法从流中读取单个字段*。

      fromClient = new ObjectInputStream(socket.getInputStream());
      Object myObject = fromClient.readObject();
      

      写的时候别忘了刷新输出流!

      第二个问题稍微复杂一些。序列化机制所做的是将类标识符写入流,然后是序列化的对象数据。当它反序列化时,它将读取类标识符并尝试加载该类(如果尚未加载)。然后它将使用无参数构造函数实例化对象并调用私有readObject(ObjectInputStream) 方法。是的,没错,它从类外调用了一个私有方法。 Java 序列化是特殊的。

      如果找不到类(即,如果它不在类路径上),则会抛出异常;否则,假设没有发现其他错误,您将获得正确类型的完全反序列化对象。

      例如,假设您有以下类:

      class Server {
          public static void main(String[] args) {
              // Set up an OutputStream sink, e.g. writing to a socket (not shown)
              ...
              ObjectOutputStream out = new ObjectOutputStream(sink);
              out.writeObject(new Data("data goes here"));
              out.flush();
              out.close();
          }
      }
      
      class Client {
          public static void main(String[] args) {
              // Set up an InputStream source (not shown)
              ...
              ObjectInputStream in = new ObjectInputStream(source);
              Data d = (Data)in.readObject();
              System.out.println(d.getData());
          }
      }
      
      class Data implements java.io.Serializable {
          private String data;
          public Data(String d) {
              data = d;
          }
          public String getData() {
              return data;
          }
      }
      

      现在假设您将这些类放入三个 jar 中(每个 jar 一个类):server.jar、client.jar 和 data.jar。如果您运行以下命令,那么它应该都可以工作:

      java -cp server.jar:data.jar Server
      java -cp client.jar:data.jar Client
      

      但如果你这样做:

      java -cp server.jar:data.jar Server
      java -cp client.jar Client
      

      然后你会得到一个ClassNotFoundException,因为客户端不知道如何找到Data 类。

      长话短说:类本身没有写入流。如果反序列化成功,那么您将可以访问该对象,就像它是在本地创建的一样,但您必须将 readObject 的结果向下转换为预期的类型。

      我暂时忽略了版本控制的一些复杂性。看看serialVersionUID,如果版本控制可能是一个问题,如何处理对可序列化类的更改。

      *严格来说并非如此。您可以在可序列化对象的readObject 方法(或readResolve)内调用readFields,但不能从反序列化机制之外调用它。那有意义吗?有点难以解释。

      【讨论】:

      • 谢谢,您为我解决了两个重要问题。我没有意识到“服务器”和“客户端”都需要相关类的本地副本。我也没有意识到我需要转换对象类型。我认为现在一切都很好。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多