ObjectInputStream выбрасывает исключение EOFexception при чтении файла с объектом в нем

Я пытаюсь прочитать Treemap из файла. Я знаю, что в файле есть TreeMap сериализованный, моя проблема в том, что OIS бросает EOFexception в этот файл.

public class FileDBMapApi {

    private FileOutputStream OutPutter;
    private FileInputStream Inputter;
    private ObjectInputStream InputStream;
    private ObjectOutputStream OutputStream;
    private NavigableMap<Integer, Object> data;
    private File currentFile;
    private int autoIncrement;

    /**
     * @param dataFile is the file to use as a database
     */
    public FileDBMapApi(String dataFile) {
        String fullPath = "data/" + dataFile;
        currentFile = new File(fullPath);
    }

    // initialiserar databasen
    public void init() {
        // checkar om filen existerar och isåfall assignar inputter och outputter till filen
        System.out.println(currentFile.exists() && !currentFile.isDirectory());
        if (currentFile.exists() && !currentFile.isDirectory()) {
            try {
                OutPutter = new FileOutputStream(currentFile);
                OutPutter.flush();
                Inputter = new FileInputStream(currentFile);
                OutputStream = new ObjectOutputStream(OutPutter);
                OutputStream.flush();
                InputStream = new ObjectInputStream(Inputter);
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            // annars så skapar den filen och assignar det istället
            try {
                boolean temp1 = currentFile.getParentFile().mkdirs();
                boolean temp2 = currentFile.createNewFile();
                if (temp2) {
                    OutPutter = new FileOutputStream(currentFile);
                    Inputter = new FileInputStream(currentFile);
                    OutputStream = new ObjectOutputStream(OutPutter);
                    InputStream = new ObjectInputStream(Inputter);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        // läser in hashmapen från filen
        this.convertData();
    }

    public void write() {
        try {
            PrintWriter n = new PrintWriter(currentFile);
            n.close();
            OutputStream.writeObject(data);
            OutputStream.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public int add(Object o) {
        int id = autoIncrement;
        data.put(autoIncrement, o);
        autoIncrement++;
        return id;
    }

    public Object get(int i) {
        return data.get(i);
    }

    public void close() {
        try {
            OutPutter.close();
            Inputter.close();
            InputStream.close();
            OutputStream.flush();
            OutputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // returnar data
    public NavigableMap<Integer, Object> getAll() {
        return data;
    }

    public long getFileSize() {
        return currentFile.length();
    }

    // tar data från en fil och sätter in det till en hashmap
    private void convertData() {
        try {
            if (InputStream.read() != -1) {
                data = (NavigableMap) InputStream.readObject();
            } else {
                data = new TreeMap<>();
                System.out.println(data);
            }
            if (!data.isEmpty()) autoIncrement = data.lastKey();
            else autoIncrement = 0;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void closeAndWrite() {
        write();
        close();
    }

}

Это convertData(0 метод, который вызывает исключение. Я проверил рассматриваемый файл, и он действительно содержит сериализованный Treemap, который был сериализован с использованием метода write(), а также метода close(). Теперь мой вопрос: где я делаю что-то не так, сохраняя или получая данные?


person maxcodes    schedule 11.12.2016    source источник


Ответы (1)


Я доберусь до того, где ошибка, но на самом деле ваш код не имеет никакого смысла и требует тщательной проверки:

public class FileDBMapApi {

    private FileOutputStream OutPutter;
    private FileInputStream Inputter;

Вам не нужны FileInput/OutputStreams отдельно как члены данных. Удалять.

    // initialiserar databasen
    public void init() {
        // checkar om filen existerar och isåfall assignar inputter och outputter till filen
        System.out.println(currentFile.exists() && !currentFile.isDirectory());
        if (currentFile.exists() && !currentFile.isDirectory()) {
            try {
                OutPutter = new FileOutputStream(currentFile);
                OutPutter.flush();
                Inputter = new FileInputStream(currentFile);
                OutputStream = new ObjectOutputStream(OutPutter);
                OutputStream.flush();
                InputStream = new ObjectInputStream(Inputter);
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            // annars så skapar den filen och assignar det istället
            try {
                boolean temp1 = currentFile.getParentFile().mkdirs();
                boolean temp2 = currentFile.createNewFile();
                if (temp2) {
                    OutPutter = new FileOutputStream(currentFile);
                    Inputter = new FileInputStream(currentFile);
                    OutputStream = new ObjectOutputStream(OutPutter);
                    InputStream = new ObjectInputStream(Inputter);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

Вы можете свести все эти запутанные двусмысленные разговоры к следующему:

try {
    currentFile.getParentFile().mkdirs();
    OutputStream = new ObjectOutputStream(new FileOutputStream(currentFile));
    InputStream = new ObjectInputStream(new FileInputStream(currentFile));
} catch (IOException exc) {
    exc.printStackTrace();
}

... хотя было бы лучше, если бы init() бросил IOException, а не ловил и поглощал его внутренне. Весь сложный код, который вы написали, выполняет только это. И открывать файл для вывода и ввода одновременно действительно не имеет смысла и не работает на некоторых платформах, таких как Windows. Вам нужно переосмыслить это.

    public void write() {
        try {
            PrintWriter n = new PrintWriter(currentFile);
            n.close();

Проблема здесь. PrintWriter не используется, поэтому его следует удалить, но настоящая проблема здесь в том, что вы только что усекли выходной файл, который гарантированно даст IOException при чтении через ObjectInputStream. Удалять.

    public void close() {
        try {
            OutPutter.close();
            Inputter.close();
            InputStream.close();
            OutputStream.flush();
            OutputStream.close();

Все это ерунда. flush() перед close() является избыточным, и вам нужно только закрыть InputStream и OutputStream. Они закроют для вас свои вложенные потоки, и даже если они этого не сделали, нет смысла закрывать вложенный выходной поток и затем пытаться сбросить и закрыть внешний выходной поток.

        } catch (Exception e) {
            e.printStackTrace();
        }

Опять же метод должен выбросить IOException, а не поглотить его.

    public long getFileSize() {
        return currentFile.length();
    }

Это бессмысленно. В некоторых операционных системах, таких как Windows, запись в каталоге не обновляется, пока вы не закроете файл, поэтому здесь можно просто вернуть ноль. Вам не нужно знать текущую длину файла.

        try {
            if (InputStream.read() != -1) {

В этом нет никакого смысла. Здесь вы прочитали байт и выбросили его. Поскольку вы не записали никаких дополнительных байтов, это может только нарушить синхронизацию с потоком. Удали это.

                data = (NavigableMap) InputStream.readObject();
            } else {
                data = new TreeMap<>();
                System.out.println(data);
            }

Это «еще» тоже не имеет смысла. Если файл пуст или больше не содержит объектов, выдается EOFException, и именно там вы должны предпринять какие-либо действия по уклонению. Фактически, опять же, вы должны позволить бросить сюда IOExceptions всех видов, вместо того, чтобы бессмысленно подставлять пустой TreeMap.

        } catch (Exception e) {
            e.printStackTrace();
        }

См. Выше повторное бросание IOException. Не пишите такой код.

    public void closeAndWrite() {

Этот метод должен называться writeAndClose(), если он вам действительно нужен, чего нет.

Я проверил рассматриваемый файл, и он действительно содержит сериализованный Treemap.

Проверял как? Есть только один способ проверить это - позвонить readObject().

person user207421    schedule 11.12.2016
comment
Окей, большое спасибо за резкую критику! (Нет уж, спасибо!) Я изменил код, как вы предлагали, и буду учиться на своих ошибках. (Все еще учусь!) Но когда я пытаюсь запустить тестовый файл с записью, а затем, когда я запускаю его снова, он не получает Object .. это просто пустой Treemap. Теперь это может означать, что он выбросил EOFException, но почему он это делает, если я записывал в файл при предыдущем запуске? Еще раз огромное спасибо за помощь и критику! - person maxcodes; 12.12.2016
comment
Вы получили пустой TreeMap из-за EOFException? Если это так, вы все еще не сериализовали его, и NB, вы не должны ловить EOFException в этом случае и подставлять new TreeMap(): иначе вы не сможете понять, что происходит. Просто позвольте исключению быть сгенерированным, как было несколько раз сказано выше, и позвольте вызывающему объекту обработать его. В противном случае вы, возможно, сериализовали его, когда он был пуст, заполните его, а затем снова сериализуете? В таком случае см. ObjectOutputStream.writeUnshared() и ObjectOutputStream.reset(). - person user207421; 13.12.2016
comment
Да, я делаю, как вы сказали, и ловлю Exception, и если он выдает EOF, я делаю new TreeMap(). я должен проверить методы, которые вы предложили, спасибо! - person maxcodes; 13.12.2016
comment
Ладно, странно, я просто попытался использовать readObject() после написания, и это сработало, я получил TreeMap. Но когда я снова запускаю программу, она не читает объект, а просто создает пустой TreeMap. - person maxcodes; 14.12.2016
comment
Вы не делаете того, что я сказал, если вы ловите EOFException или делаете новый TreeMap. Вы просто скрываете от себя проблемы. Не пишите такой код. - person user207421; 14.12.2016