Мои два цента из моего собственного блога:
Вот подробное объяснение сериализации : (мой собственный блог)
Сериализация:
Сериализация - это процесс сохранения состояния объекта. Он представлен и хранится в виде последовательности байтов. Это может быть сохранено в файле. Процесс чтения состояния объекта из файла и его восстановления называется десериализацией.
Зачем нужна сериализация?
В современной архитектуре всегда необходимо сохранять состояние объекта, а затем извлекать его. Например, в Hibernate, чтобы сохранить объект, мы должны сделать класс Serializable. Что он делает, так это то, что после сохранения состояния объекта в виде байтов его можно перенести в другую систему, которая затем может прочитать из состояния и извлечь класс. Состояние объекта может исходить из базы данных, другого jvm или отдельного компонента. С помощью сериализации мы можем получить состояние объекта.
Код Пример и пояснение:
Сначала давайте взглянем на класс предметов:
public class Item implements Serializable{
/**
* This is the Serializable class
*/
private static final long serialVersionUID = 475918891428093041L;
private Long itemId;
private String itemName;
private transient Double itemCostPrice;
public Item(Long itemId, String itemName, Double itemCostPrice) {
super();
this.itemId = itemId;
this.itemName = itemName;
this.itemCostPrice = itemCostPrice;
}
public Long getItemId() {
return itemId;
}
@Override
public String toString() {
return "Item [itemId=" + itemId + ", itemName=" + itemName + ", itemCostPrice=" + itemCostPrice + "]";
}
public void setItemId(Long itemId) {
this.itemId = itemId;
}
public String getItemName() {
return itemName;
}
public void setItemName(String itemName) {
this.itemName = itemName;
}
public Double getItemCostPrice() {
return itemCostPrice;
}
public void setItemCostPrice(Double itemCostPrice) {
this.itemCostPrice = itemCostPrice;
}
}
В приведенном выше коде видно, что Item class реализует Serializable .
Это интерфейс, который позволяет сериализуемости класса.
Теперь мы видим, что переменная с именем serialVersionUID инициализируется переменной Long. Это число вычисляется компилятором на основе состояния класса и атрибутов класса. Это число, которое поможет jvm определить состояние объекта при чтении состояния объекта из файла.
Для этого мы можем взглянуть на официальную документацию Oracle:
Среда выполнения сериализации связывается с каждым сериализуемым классом a
номер версии, называемый serialVersionUID, который используется во время
десериализация для проверки того, что отправитель и получатель сериализованы
объект загрузил классы для этого объекта, которые совместимы с
уважение к сериализации. Если получатель загрузил класс для
объект, который имеет другой serialVersionUID, чем у
соответствующий класс отправителя, тогда десериализация приведет к
InvalidClassException. Сериализуемый класс может объявить свой собственный
serialVersionUID явно, объявив поле с именем
"serialVersionUID", который должен быть статическим, конечным и типа long:
ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L; Если
Сериализуемый класс явно не объявляет serialVersionUID,
тогда среда выполнения сериализации рассчитает значение по умолчанию
Значение serialVersionUID для этого класса на основе различных аспектов
класс, как описано в Сериализации объектов Java (TM)
Спецификация. Тем не менее, настоятельно рекомендуется, чтобы все
Сериализуемые классы явно объявляют значения serialVersionUID, так как
вычисление serialVersionUID по умолчанию очень чувствительно к классу
детали, которые могут варьироваться в зависимости от реализации компилятора, и могут
таким образом приводят к неожиданным InvalidClassExceptions во время
десериализации. Поэтому, чтобы гарантировать последовательный serialVersionUID
значение в различных реализациях Java-компилятора, сериализуемый
класс должен объявить явное значение serialVersionUID. Это также
Настоятельно рекомендуется, чтобы явные объявления serialVersionUID использовали
частный модификатор, где это возможно, поскольку такие объявления применяются только к
немедленно объявленный класс - поля serialVersionUID не являются
полезно как унаследованные члены.
Если вы заметили, что мы использовали другое ключевое слово, которое transient .
Если поле не сериализуемо, оно должно быть помечено как временное. Здесь мы пометили itemCostPrice как переходные и не хотим, чтобы они записывались в файл
Теперь давайте посмотрим, как записать состояние объекта в файле, а затем прочитать его оттуда.
public class SerializationExample {
public static void main(String[] args){
serialize();
deserialize();
}
public static void serialize(){
Item item = new Item(1L,"Pen", 12.55);
System.out.println("Before Serialization" + item);
FileOutputStream fileOut;
try {
fileOut = new FileOutputStream("/tmp/item.ser");
ObjectOutputStream out = new ObjectOutputStream(fileOut);
out.writeObject(item);
out.close();
fileOut.close();
System.out.println("Serialized data is saved in /tmp/item.ser");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void deserialize(){
Item item;
try {
FileInputStream fileIn = new FileInputStream("/tmp/item.ser");
ObjectInputStream in = new ObjectInputStream(fileIn);
item = (Item) in.readObject();
System.out.println("Serialized data is read from /tmp/item.ser");
System.out.println("After Deserialization" + item);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
В приведенном выше примере мы видим пример сериализации и десериализации объекта.
Для этого мы использовали два класса. Для сериализации объекта мы использовали ObjectOutputStream. Мы использовали метод writeObject для записи объекта в файл.
Для десериализации мы использовали ObjectInputStream, который читает объект из файла. Он использует readObject для чтения данных объекта из файла.
Вывод вышеуказанного кода будет выглядеть следующим образом:
Before SerializationItem [itemId=1, itemName=Pen, itemCostPrice=12.55]
Serialized data is saved in /tmp/item.ser
After DeserializationItem [itemId=1, itemName=Pen, itemCostPrice=null]
Обратите внимание, что itemCostPrice из десериализованного объекта равно null , поскольку оно не было записано.
Мы уже обсуждали основы Java-сериализации в первой части этой статьи.
Теперь давайте обсудим это подробно и как это работает.
Сначала давайте начнем с serialversionuid.
serialVersionUID используется в качестве элемента управления версиями в классе Serializable.
Если вы явно не объявите serialVersionUID, JVM сделает это за вас автоматически, основываясь на различных свойствах класса Serializable.
Алгоритм вычисления Java serialversionuid (подробнее здесь)
- Название класса.
- Модификаторы класса, записанные в виде 32-разрядного целого числа.
- Имя каждого интерфейса, отсортированное по имени.
- Для каждого поля класса, отсортированного по имени поля (кроме закрытых статических и закрытых переходных полей: имя поля.
модификаторы поля записываются как 32-разрядное целое число. Дескриптор
поля.
- Если инициализатор класса существует, запишите следующее: Имя метода.
- Модификатор метода, java.lang.reflect.Modifier.STATIC, записанный как 32-разрядное целое число.
- Дескриптор метода, () V.
- Для каждого не приватного конструктора, отсортированного по имени метода и сигнатуре: Имя метода,. Модификаторы
Метод записывается как 32-разрядное целое число. Дескриптор метода.
- Для каждого не закрытого метода, отсортированного по имени метода и сигнатуре: название метода. Модификаторы метода записываются как
32-разрядное целое число Дескриптор метода.
- Алгоритм SHA-1 выполняется в потоке байтов, созданных DataOutputStream, и выдает пять 32-битных значений sha [0..4].
значение хеша составляется из первого и второго 32-битных значений
Дайджест сообщения SHA-1. Если результат дайджеста сообщения, пять
32-битные слова H0 H1 H2 H3 H4 в массиве из пяти значений int
sha, значение хеша будет вычислено следующим образом:
long hash = ((sha[0] >>> 24) & 0xFF) |
> ((sha[0] >>> 16) & 0xFF) << 8 |
> ((sha[0] >>> 8) & 0xFF) << 16 |
> ((sha[0] >>> 0) & 0xFF) << 24 |
> ((sha[1] >>> 24) & 0xFF) << 32 |
> ((sha[1] >>> 16) & 0xFF) << 40 |
> ((sha[1] >>> 8) & 0xFF) << 48 |
> ((sha[1] >>> 0) & 0xFF) << 56;
Алгоритм сериализации Java
Алгоритм сериализации объекта описан ниже:
1. Он записывает метаданные класса, связанного с экземпляром.
2. Он рекурсивно записывает описание суперкласса, пока не найдет java.lang.object .
3. Как только он заканчивает запись информации метаданных, он начинает с фактических данных, связанных с экземпляром. Но на этот раз это
начинается с самого верхнего суперкласса.
4. Он рекурсивно записывает данные, связанные с экземпляром, начиная с наименьшего суперкласса, до самого производного класса.
Что нужно помнить:
Статические поля в классе не могут быть сериализованы.
public class A implements Serializable{
String s;
static String staticString = "I won't be serializable";
}
Если serialversionuid отличается в классе чтения, он выдаст исключение InvalidClassException
.
Если класс реализует сериализуемый, то все его подклассы также будут сериализуемыми.
public class A implements Serializable {....};
public class B extends A{...} //also Serializable
Если у класса есть ссылка на другой класс, все ссылки должны быть сериализуемыми, иначе процесс сериализации не будет выполнен. В этом случае NotSerializableException генерируется во время выполнения.
Например:
public class B{
String s,
A a; // class A needs to be serializable i.e. it must implement Serializable
}