ObjectInputStream.readObject: примеры (JAVA)
ObjectInputStream.readObject: ObjectОбщее описание и сигнатура метода
Метод ObjectInputStream.readObject() используется для восстановления сериализованных объектов из входного потока байт. Он читает следующее представление объекта из потока и возвращает десериализованный экземпляр в виде java.lang.Object. Восстановление включает проверку версии класса (serialVersionUID), разрешение классов в текущем ClassLoader, применение методов readObject в классах и вызов механизма readResolve при наличии.
Сигнатура метода:
public final Object readObject() throws IOException, ClassNotFoundException
Параметров у метода нет. Возвращаемое значение - десериализованный объект как Object. Возможные исключения:
ClassNotFoundException- если класс объекта не найден во время восстановления.IOException- при ошибках ввода/вывода, повреждении потока или нарушении формата.
Поведение при десериализации включает несколько шагов: чтение метаданных объекта, разрешение класса (вызов resolveClass при пользовательском ObjectInputStream), создание экземпляра без вызова обычного конструктора, восстановление значений полей, вызов специальных методов обратного вызова (например, readObject с сигнатурой private void readObject(ObjectInputStream)), применение readResolve при наличии, и проверку целостности классов по serialVersionUID.
Класс ObjectInputStream также поддерживает связанные API: readUnshared() для чтения «неразделяемой» копии объекта, enableResolveObject и resolveObject для замены объектов во время чтения, а с более новых версий Java - фильтры десериализации через ObjectInputFilter.
Короткие практические примеры
Ниже приведены упрощённые примеры записи и чтения объектов с помощью ObjectOutputStream и ObjectInputStream.readObject().
1) Простая сериализация и десериализация:
import java.io.*;
class Person implements Serializable {
private static final long serialVersionUID = 1L;
String name;
int age;
Person(String name, int age) { this.name = name; this.age = age; }
}
// Сериализация
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (ObjectOutputStream oos = new ObjectOutputStream(baos)) {
oos.writeObject(new Person("Ivan", 30));
}
byte[] data = baos.toByteArray();
// Десериализация
try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data))) {
Object obj = ois.readObject();
Person p = (Person) obj;
System.out.println(p.name + " " + p.age);
}
Ivan 30
2) Обработка отсутствующего класса (ClassNotFoundException):
// Ситуация: на стороне чтения отсутствует определение класса Person
// При попытке ois.readObject() будет выброшено ClassNotFoundException
java.lang.ClassNotFoundException: Person
3) Использование readUnshared для получения отдельной копии:
// Записан один и тот же объект дважды
ByteArrayOutputStream baos2 = new ByteArrayOutputStream();
try (ObjectOutputStream oos = new ObjectOutputStream(baos2)) {
Person p = new Person("Anna", 25);
oos.writeObject(p);
oos.writeObject(p);
}
byte[] d2 = baos2.toByteArray();
try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(d2))) {
Person a = (Person) ois.readObject();
Person b = (Person) ois.readUnshared(); // отдельная копия
System.out.println(a == b); // false
}
false
Похожие механизмы в Java
В Java присутствуют несколько близких по назначению API:
- readUnshared() - возвращает экземпляр, который гарантированно не будет тот же объект в пуле ссылок десериализатора. Предпочтителен, когда требуется независимость объектов.
- Externalizable (вместо Serializable) - даёт полный контроль над форматом через методы
writeExternalиreadExternal. Предпочтение в случаях, когда требуется явный формат и совместимость между версиями. - ObjectInputStream с переопределением resolveClass/resolveProxyClass/resolveObject - применяется для кастомного разрешения классов и подмен объектов во время чтения.
- JSON-библиотеки (Jackson, Gson) - альтернатива для человекочитаемых форматов и межплатформенной совместимости.
Выбор между ними зависит от требований: бинарная сериализация Java удобна для JVM-to-JVM обмена с сохранением внутренней структуры объектов; Externalizable и кастомные resolve-методы дают больше контроля; JSON и прочие текстовые форматы обеспечивают переносимость и безопасность.
Аналоги и их отличия в других языках
Короткие примеры сопоставимых механизмов в разных языках и ключевые отличия от Java-подхода.
- Python: модуль
pickle. Похож по задумке, но формат Python-специфичен. Пример:
import pickle
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
p = Person('Ivan', 30)
data = pickle.dumps(p)
p2 = pickle.loads(data)
print(p2.name, p2.age)
Ivan 30
- JavaScript (Node.js): стандартной двоичной сериализации объектов нет; чаще применяется
JSON.stringify/parseили библиотеки (protobuf, msgpack). JSON не сохраняет методы и прототипы.
const obj = {name: 'Ivan', age: 30};
const data = JSON.stringify(obj);
const o = JSON.parse(data);
console.log(o.name, o.age);
Ivan 30
- PHP: функции
serializeиunserialize. Формат PHP-специфичен и небезопасен при работе с недоверенными данными.
$obj = ['name' => 'Ivan', 'age' => 30];
$data = serialize($obj);
$o = unserialize($data);
print_r($o);
Array
(
[name] => Ivan
[age] => 30
)
- C#: раньше использовался
BinaryFormatter.Deserialize, но он признан небезопасным и устаревшим. РекомендуютсяSystem.Text.Jsonили protobuf для совместимости.
// System.Text.Json пример
using System.Text.Json;
var obj = new { name = "Ivan", age = 30 };
var json = JsonSerializer.Serialize(obj);
var o = JsonSerializer.Deserialize<Dictionary<string, object>>(json);
Console.WriteLine(o["name"] + " " + o["age"]);
Ivan 30
- Go: пакет
encoding/gobпредоставляет бинарную сериализацию для Go-типов и похож по назначению, но совместим только между Go-программами.
// Пример с gob
package main
import (
"bytes"
"encoding/gob"
"fmt"
)
type Person struct { Name string; Age int }
func main() {
var buf bytes.Buffer
enc := gob.NewEncoder(&buf)
dec := gob.NewDecoder(&buf)
p := Person{"Ivan", 30}
enc.Encode(p)
var p2 Person
dec.Decode(&p2)
fmt.Println(p2.Name, p2.Age)
}
Ivan 30
Ключевые отличия: Java-serializable сохраняет полную JVM-метаданные и может автоматически восстановить сложные объекты, но это делает формат небезопасным и тесно связанным с версиями классов. Большинство других языков либо имеют свои бинарные форматы, либо рекомендуют текстовые форматы для переносимости.
Типичные ошибки и исключения
Список распространённых проблем при использовании readObject() с примерами.
1) ClassNotFoundException - отсутствует определение класса на стороне чтения:
// Попытка десериализации класса, которого нет в classpath
byte[] data = ...; // данные, содержащие объект класса UnknownClass
try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data))) {
Object o = ois.readObject();
} catch (ClassNotFoundException e) {
System.out.println("Класс не найден: " + e.getMessage());
}
Класс не найден: UnknownClass
2) InvalidClassException - несовпадение serialVersionUID или несовместимость полей:
// Если serialVersionUID изменён между версиями классов
// при readObject будет выброшено InvalidClassException
java.io.InvalidClassException: Person; local class incompatible: stream classdesc serialVersionUID = 123, local class serialVersionUID = 456
3) StreamCorruptedException и EOFException - повреждение данных или неожиданное окончание:
// Если поток усечён
byte[] truncated = Arrays.copyOf(data, data.length / 2);
try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(truncated))) {
ois.readObject();
} catch (IOException e) {
System.out.println(e.getClass().getSimpleName());
}
StreamCorruptedException
4) NotSerializableException - попытка сериализовать объект без интерфейса Serializable:
class NoSer { }
// При записи oos.writeObject(new NoSer())
// будет выброшено NotSerializableException
java.io.NotSerializableException: NoSer
5) Уязвимости безопасности - десериализация недоверенных данных может привести к исполнению вредоносного кода. Начиная с Java 9 доступен механизм фильтрации с помощью ObjectInputFilter для ограничения типов, разрешённых к десериализации.
Изменения и улучшения в последних релизах
За последние версии Java появились механизмы, повышающие безопасность и контроль над процессом десериализации:
- ObjectInputFilter (JEP 290, Java 9) - позволяет задать правило фильтрации объектов при десериализации, предотвращая восстановление нежелательных типов.
- Улучшены возможности для настройки поведения в подсистеме сериализации: методы для установки и комбинирования фильтров, более гибкие точки подмены объектов через
resolveObject. - Рост рекомендаций по отказу от Java-бинарной сериализации для межсистемного обмена: предпочтение отдаётся JSON, protobuf и другим форматам, что повлияло на руководство по безопасности и практики разработки.
Сам метод readObject() сигнатурно не менялся, но экосистема вокруг него получила средства контроля и фильтрации. Нативной депрекации для метода не выявлено.
Расширенные и редкие сценарии использования
Ниже несколько подробных примеров, демонстрирующих тонкие возможности и обходные приёмы.
1) Кастомное разрешение классов при десериализации (подмена имён классов):
import java.io.*;
class CustomOIS extends ObjectInputStream {
CustomOIS(InputStream in) throws IOException { super(in); }
@Override
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
String name = desc.getName();
if ("old.pkg.Person".equals(name)) {
return Class.forName("new.pkg.Person");
}
return super.resolveClass(desc);
}
}
// Использование: позволяет читать объекты, записанные старой версией с другим именем пакета
// Результат: объекты старого пакета будут восстановлены как new.pkg.Person
2) Фильтрация типов через ObjectInputFilter для безопасности:
ObjectInputFilter filter = info -> {
Class<?> serialClass = info.serialClass();
if (serialClass != null && serialClass.getName().startsWith("com.myapp.")) {
return ObjectInputFilter.Status.ALLOWED;
}
return ObjectInputFilter.Status.REJECTED;
};
try (ObjectInputStream ois = new ObjectInputStream(in)) {
ObjectInputFilter.Config.setObjectInputFilter(ois, filter);
Object o = ois.readObject();
}
// Результат: десериализация только разрешённых типов, прочие - отклоняются
3) Замена объектов во время чтения через resolveObject:
ObjectInputStream ois = new ObjectInputStream(in) {
@Override
protected Object resolveObject(Object obj) throws IOException {
if (obj instanceof Date) {
return new java.sql.Date(((Date) obj).getTime());
}
return super.resolveObject(obj);
}
};
// При readObject() все Date будут заменены на java.sql.Date
// Результат: получаем нужный тип даты без дополнительной обработки после чтения
4) Обработка версий классов: использование serialVersionUID и readObject/readObjectNoData
class Person implements Serializable {
private static final long serialVersionUID = 2L; // управляемая версия
String name;
int age; // новое поле в версии 2
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
if (age == 0) {
age = -1; // значение по умолчанию для обратной совместимости
}
}
}
// При чтении старого потока, где age отсутствует, поле получит значение -1
// Результат: корректное восстановление объекта при изменившейся структуре класса
5) Чтение потоков с несколькими типами и обработка ClassNotFound для частичного восстановления:
try (ObjectInputStream ois = new ObjectInputStream(in)) {
while (true) {
try {
Object o = ois.readObject();
// обработка известных типов
} catch (ClassNotFoundException e) {
// логирование и продолжение чтения следующих объектов
}
}
} catch (EOFException e) {
// конец потока
}
// Результат: восстановление всех доступных объектов, пропуск неизвестных типов
6) Производительность: использование буферов и предварительной десериализации в отдельных потоках для снижения влияния GC и блокировок ввода/вывода. В многопоточной среде следует избегать совместного использования одного ObjectInputStream из нескольких потоков без синхронизации, так как внутреннее состояние не потокобезопасно.
джава ObjectInputStream.readObject function comments
- джава ObjectInputStream.readObject - аргументы и возвращаемое значение
- Функция java ObjectInputStream.readObject - описание
- ObjectInputStream.readObject - примеры
- ObjectInputStream.readObject - похожие методы на java
- ObjectInputStream.readObject на javascript, c#, python, php
- ObjectInputStream.readObject изменения java
- Примеры ObjectInputStream.readObject на джава