Эта статья Проект с открытым исходным кодом Github:GitHub.com/Hanson.net 9…Он был включен в , есть подробные маршруты обучения программированию для самообучения, вопросы интервью и личные встречи, материалы по программированию и серия технических статей и т. д. Ресурсы постоянно обновляются...
инструмент человек
Когда-то понимание сериализации Java застряло в: «реализацияSerializbale
интерфейс" не годится, пока...
Так что на этот раз я нашел время, чтобы снова собрать давние «Идеи программирования на Java», точно так же, как раньше«Перечисление частей знания»Точно так же я перепроверил знания о «сериализации и десериализации».
Для чего используется сериализация?
Первоначальная цель сериализации состоит в том, чтобы «преобразовать» объект Java в последовательность байтов, чтобы упростить постоянное хранение на диске, избежать исчезновения объекта из памяти после запуска программы и преобразовать его в байты. Последовательности также легче транспортировать. и распространяться по сети, поэтому концептуально хорошо понятны:
- Сериализация: Преобразует объект Java в последовательность байтов.
- десериализовать: восстановить последовательность байтов исходного объекта Java.
И механизм сериализации тоже компенсирует некоторые различия в платформизации в некотором смысле, ведь преобразованный поток байтов можно десериализовать на других платформах для восстановления объекта.
Дело в том, что это выглядит очень просто, но позади еще много вещей, пожалуйста, посмотрите вниз.
Как сериализуются объекты?
Однако в настоящее время в Java нет ключевого слова для прямого определения так называемого «постоянного» объекта.
Сохранение и удаление объекта должны выполняться программистом вручную в коде.явноДействия по сериализации и десериализации восстановления.
Например, если бы мыStudent
Объект класса сериализуется в файл с именемstudent.txt
в текстовом файле, а затем десериализовать текстовый файл вStudent
объект класса:
1. Определение студенческого класса
public class Student implements Serializable {
private String name;
private Integer age;
private Integer score;
@Override
public String toString() {
return "Student:" + '\n' +
"name = " + this.name + '\n' +
"age = " + this.age + '\n' +
"score = " + this.score + '\n'
;
}
// ... 其他省略 ...
}
2. Сериализация
public static void serialize( ) throws IOException {
Student student = new Student();
student.setName("CodeSheep");
student.setAge( 18 );
student.setScore( 1000 );
ObjectOutputStream objectOutputStream =
new ObjectOutputStream( new FileOutputStream( new File("student.txt") ) );
objectOutputStream.writeObject( student );
objectOutputStream.close();
System.out.println("序列化成功!已经生成student.txt文件");
System.out.println("==============================================");
}
3. Десериализация
public static void deserialize( ) throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream =
new ObjectInputStream( new FileInputStream( new File("student.txt") ) );
Student student = (Student) objectInputStream.readObject();
objectInputStream.close();
System.out.println("反序列化结果为:");
System.out.println( student );
}
4. Запуск результатов
Отпечатки консоли:
序列化成功!已经生成student.txt文件
==============================================
反序列化结果为:
Student:
name = CodeSheep
age = 18
score = 1000
Каково использование интерфейса Serializable?
определено вышеStudent
класс, который реализуетSerializable
интерфейс, однако, когда мы нажимаем наSerializable
Загляните внутрь интерфейса и найдите его.Оказался пустой интерфейс, и не содержит никаких методов!
Представьте, если вышеизложенное определено вStudent
Забыл добавитьimplements Serializable
что случится?
Результат эксперимента: программа работает в это времясообщит об ошибке, и бросаетNotSerializableException
аномальный:
Следим за сообщением об ошибке, от исходного кода кObjectOutputStream
изwriteObject0()
На дне метода я вдруг понял:
если объект не является нинить,множество,перечислить, а также не реализовалSerializable
Если интерфейс используется, он будет выброшен во время сериализации.NotSerializableException
аномальный!
О, я вижу!
оригинальныйSerializable
Интерфейс просто маркер! ! !
Он сообщает код, пока он реализованSerializable
Все классы интерфейса сериализуемы! Однако ему не нужно выполнять реальное действие сериализации.
serialVersionUID
Какая польза от номера?
Я считаю, что вы должны часто видеть следующие строки кода, определенные в некоторых классах, которые определяют класс с именемserialVersionUID
поля:
private static final long serialVersionUID = -4392658638228508589L;
Вы знаете, что означает это утверждение? Зачем делатьserialVersionUID
серийный номер?
Продолжайте делать простой эксперимент и возьмите вышеперечисленное.Student
например, мы не объявляли явноserialVersionUID
поле.
Сначала мы вызываем вышеуказанноеserialize()
метод, аStudent
Объект сериализован на локальный дискstudent.txt
документ:
public static void serialize() throws IOException {
Student student = new Student();
student.setName("CodeSheep");
student.setAge( 18 );
student.setScore( 100 );
ObjectOutputStream objectOutputStream =
new ObjectOutputStream( new FileOutputStream( new File("student.txt") ) );
objectOutputStream.writeObject( student );
objectOutputStream.close();
}
Далее мыStudent
Перемещайте руки и ноги в классе, например, добавьте другое имя с именемstudentID
поле, указывающее номер студенческого билета:
В это время мы берем только что сериализованные данные в локальныйstudent.txt
файл и десериализуйте его с помощью следующего кода, пытаясь восстановить только чтоStudent
Объект:
public static void deserialize( ) throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream =
new ObjectInputStream( new FileInputStream( new File("student.txt") ) );
Student student = (Student) objectInputStream.readObject();
objectInputStream.close();
System.out.println("反序列化结果为:");
System.out.println( student );
}
запустить обнаружениесообщил об ошибке, и бросаетInvalidClassException
аномальный:
Информация, предложенная здесь, очень ясна: до и после сериализации.serialVersionUID
Номера не совпадают!
По крайней мере, с этого местадваВажная информация:
- 1. serialVersionUID — уникальный идентификатор до и после сериализации
- 2. По умолчанию, если никто явно не определил
serialVersionUID
, компилятор автоматически объявит его для него!
Вопрос 1: serialVersionUID
Идентификатор сериализации можно рассматривать как "секрет" в процессе сериализации и десериализации. Во время десериализации JVM сравнивает идентификатор серийного номера в потоке байтов с идентификатором серийного номера в сериализованном классе. Да, только когда они оба последовательно может быть выполнена десериализация снова, в противном случае будет сообщено об исключении, чтобы завершить процесс десериализации.
2-й вопрос:Если при определении сериализуемого класса никто явно не определяетserialVersionUID
Если это так, среда выполнения Java автоматически создастserialVersionUID
, после изменения структуры или информации класса, как указано выше, классserialVersionUID
Тоже изменится!
Таким образом, дляserialVersionUID
Определенность кода, по-прежнему рекомендуется при написании кода, любойimplements Serializable
класс, лучше явно объявитьserialVersionUID
Четкое значение!
Конечно, если вы не хотите назначать значения вручную, вы также можете использовать функцию автоматического добавления IDE, такую как я используюIntelliJ IDEA
,в соответствии сalt + enter
может быть автоматически сгенерирован и добавлен для классаserialVersionUID
поле, очень удобно:
два особых случая
- 1. Все, что есть
static
Измененные поля не сериализуются - 2. Все, что есть
transient
Поля, измененные модификаторами, также не сериализуются.
для первой точки, потому что сериализация сохраняетсостояние объектаа не состояние класса, поэтому оно будет проигнорированоstatic
Статические поля также разумны.
для второй точки, ты должен знатьtransient
Модификаторы работают.
Если вы не хотите, чтобы поле сериализовалось при сериализации объекта класса (например, это поле хранит частные значения, такие как:密码
д.), то вы можете использоватьtransient
модификатор для украшения поля.
такие, как ранее определенныеStudent
класс, добавьполе пароля, но не хотите сериализоваться вtxt
текст, вы можете:
Это сериализацияStudent
объект класса,password
Для полей установлены значения по умолчаниюnull
, что видно из результатов, полученных от десериализации:
Контролируемая и принудительная сериализация
Связующее благословение
Как видно из приведенного выше процесса, процесс сериализации и десериализации на самом делеущербный, потому что есть промежуточный процесс от сериализации к десериализации.Если кто-то другой получит промежуточный поток байтов, а затем подделает или подделает, десериализованный объект будет иметь определенные риски.
В конце концов, десериализация также эквивалентна«Неявное» построение объекта, поэтому мы хотим сделатьконтролируемыйДействие десериализации объекта.
Какой контролируемый закон?
Ответ:Напишите свой собственныйreadObject()
Функция десериализации построения объектов, что обеспечивает ограничения.
Поскольку вы сами пишетеreadObject()
функция, то вы можете делать много контролируемых вещей: например, различные суждения.
также вышеStudent
Например, в классе обычно оценки учащихся должны быть в0 ~ 100
Между тем, чтобы предотвратить подделку тестовых результатов студентов странным значением во время десериализации, мы можем написать свой собственныйreadObject()
Функция контроля десериализации:
private void readObject( ObjectInputStream objectInputStream ) throws IOException, ClassNotFoundException {
// 调用默认的反序列化函数
objectInputStream.defaultReadObject();
// 手工检查反序列化后学生成绩的有效性,若发现有问题,即终止操作!
if( 0 > score || 100 < score ) {
throw new IllegalArgumentException("学生分数只能在0到100之间!");
}
}
Например, я намеренно изменил оценку учащегося на101
, десериализация немедленно прекращается и сообщается об ошибке:
Для приведенного выше кода некоторым друзьям может быть любопытно, почему пользовательскийprivate
изreadObject()
Метод может быть вызван автоматически, что требует от вас следовать базовому исходному коду, чтобы узнать, я помогу вам следоватьObjectStreamClass
Нижний слой класса, я думаю, вы вдруг поймете, когда увидите это:
Механизм отражения снова работает! Да, в Java все можно "отражать" (забавно), даже если это определено в классеprivate
Частные методы также могут быть извлечены и выполнены, что просто удобно.
Усовершенствования одноэлементного шаблона
Легко упускаемый из виду вопрос:Сериализуемые одноэлементные классы не могут быть одноэлементными!
Небольшой пример кода прояснит ситуацию.
Например, здесь мы используемjava
Напишите одноэлементную реализацию общего подхода «статический внутренний класс»:
public class Singleton implements Serializable {
private static final long serialVersionUID = -1576643344804979563L;
private Singleton() {
}
private static class SingletonHolder {
private static final Singleton singleton = new Singleton();
}
public static synchronized Singleton getSingleton() {
return SingletonHolder.singleton;
}
}
Затем напишите основную функцию проверки:
public class Test2 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectOutputStream objectOutputStream =
new ObjectOutputStream(
new FileOutputStream( new File("singleton.txt") )
);
// 将单例对象先序列化到文本文件singleton.txt中
objectOutputStream.writeObject( Singleton.getSingleton() );
objectOutputStream.close();
ObjectInputStream objectInputStream =
new ObjectInputStream(
new FileInputStream( new File("singleton.txt") )
);
// 将文本文件singleton.txt中的对象反序列化为singleton1
Singleton singleton1 = (Singleton) objectInputStream.readObject();
objectInputStream.close();
Singleton singleton2 = Singleton.getSingleton();
// 运行结果竟打印 false !
System.out.println( singleton1 == singleton2 );
}
}
После запуска мы обнаружили:Десериализованный одноэлементный объект не равен исходному одноэлементному объекту., что, несомненно, не соответствует нашей цели.
Решение: написано от руки в классе singletonreadResolve()
функция, которая напрямую возвращает одноэлементный объект, чтобы избежать его:
private Object readResolve() {
return SingletonHolder.singleton;
}
Таким образом, когда десериализация считывает объект из потока,readResolve()
будет вызываться для замены нового десериализованного объекта возвращаемым в нем объектом.
не ожидал
Я думал, что эту статью скоро закончат, но оказалось, что так много всего выдрано, но после того, как разобрал и соединил последовательно, все равно чувствуется намного яснее.
Вот и все, увидимся в следующий раз.
Эта статья Проект с открытым исходным кодом Github:GitHub.com/Hanson.net 9…Он был включен в , есть подробные маршруты обучения программированию для самообучения, вопросы интервью и личные встречи, материалы по программированию и серия технических статей и т. д. Ресурсы постоянно обновляются...
Медленнее, быстрее