开发者

Trouble accessing fields of a serialized object in Java

I have instantized a class that implements Serializable and I am trying to stream that object like this:

try{
    Socket socket = new Socket("localhost", 8000);
    ObjectOutputStream toServer = new ObjectOutputStream(socket.getOutputStream());
    toServer.writeObject(myObject);
} catch (IOException ex) {
    System.err.开发者_如何学Goprintln(ex);
}

All good so far right? Then I am trying to read the fields of that object like this:

//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();
        }
    }
}

But I always get the error:

java.io.NotActiveException: not in call to readObject

This is my first time streaming objects instead of primitive data types, what am I doing wrong?

BONUS

When I do get this working correctly, is the ENTIRE CLASS passed with the serialized object (i.e. will I have access to the methods of the object's class)? My reading suggests that the entire class is passed with the object, but I have been unable to use the objects methods thus far. How exactly do I call on the object's methods?

In addition to my code above I also experimented with the readObject method, but I was probably using it wrong too because I couldn't get it to work. Please enlighten me.


To answer your first question:

You need to use ObjectInputStream.readObject to deserialize. You cannot read individual fields from the stream*.

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

Don't forget to flush the output stream when writing!

The second question is a little more complex. What the serialization mechanism does is write a class identifier to the stream followed by the serialized object data. When it deserializes it will read the class identifier and attempt to load that class (if it isn't already loaded). It will then instantiate the object using the no-arg constructor and call the private readObject(ObjectInputStream) method. Yes, that's right, it calls a private method from outside the class. Java serialization is special.

If the class cannot be found (i.e. if it's not on the classpath) then an exception will be thrown; otherwise you'll get a fully deserialized object of the correct type assuming no other errors are found.

For example, suppose you have the following classes:

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;
    }
}

Now suppose you put those classes into three jars (one class per jar): server.jar, client.jar and data.jar. If you run the following commands then it should all work:

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

But if you do this:

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

then you'll get a ClassNotFoundException because the client doesn't know how to find the Data class.

Long story short: the class itself is not written to the stream. If deserialization succeeds then you will have access to the object as though it had been created locally, but you will have to downcast the result of readObject to the expected type.

There is some complexity around versioning that I've ignored for now. Take a look at serialVersionUID and how to deal with changes to serializable classes if versioning is likely to be an issue.

*Not strictly true. You can call readFields inside the serializable object's readObject method (or readResolve), but you cannot call it from outside the deserialization mechanism. Does that make sense? It's a little hard to explain.


Looking at the code for ObjectInputStream.readFields(), that exception is called because the curContext field is null. You should call fromClient.readObject() before calling readFields(), as it will set the curContext. Note that readObject() will return the instance that is being serialized, which may be of more use to you.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜