无法在Java中访问序列化对象的字段

时间:2011-01-16 02:42:10

标签: java serialization object stream

我已经实例化了一个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

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

奖金

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

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

2 个答案:

答案 0 :(得分:2)

回答你的第一个问题:

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

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

写作时不要忘记刷新输出流!

第二个问题有点复杂。序列化机制的作用是将类标识符写入流,然后是序列化对象数据。当它反序列化时,它将读取类标识符并尝试加载该类(如果它尚未加载)。然后,它将使用no-arg构造函数实例化对象,并调用私有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以及如何处理可序列化类的更改。

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

答案 1 :(得分:0)

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