Проблемы с доступом к полям сериализованного объекта в Java - PullRequest
1 голос
/ 16 января 2011

Я создал экземпляр класса, который 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, но я, вероятно, тоже использовал его неправильно, потому что не мог заставить его работать.Пожалуйста, просветите меня.

Ответы [ 2 ]

2 голосов
/ 16 января 2011

Чтобы ответить на ваш первый вопрос:

Вам необходимо использовать 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;
    }
}

Теперь предположим, что вы поставили теКлассы на три банки (один класс на банку): 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), но вы не можете вызвать его извне механизма десериализации.Имеет ли это смысл?Это немного сложно объяснить.

0 голосов
/ 16 января 2011

Глядя на код для ObjectInputStream.readFields(), вызывается это исключение, потому что поле curContext пусто. Вы должны позвонить fromClient.readObject() перед вызовом readFields(), так как он установит curContext. Обратите внимание, что readObject() вернет экземпляр, который сериализуется, что может быть более полезным для вас.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...