У цій статті ми розповімо, що таке серіалізація і як вона працює у Java
Малюнок 1. Графічне подання Потоків
Потоки в основному поділяються на два типи:
Вступ
Серіалізація об'єкта - це здатність об'єкта зберігати повну копію його та будь-яких інших об'єктів, на які він посилається, використовуючи потік виводу (наприклад, у зовнішній файл). Таким чином, об'єкт може бути відтворений із серіалізованої (збереженої) копії трохи пізніше, коли це буде потрібно. Серіалізація об'єктів, як нова можливість введена в JDK 1.1, надає функцію для перетворення груп або окремих об'єктів, потік бітів або масив байтів, для зберігання або передачі по мережі. І як було сказано, даний потік бітів або масив байтів можна перетворити назад в об'єкти Java. Головним чином це відбувається автоматично завдяки класамObjectInputStream
та ObjectOutputStream
. Програміст може вирішити реалізувати цю функцію шляхом реалізації інтерфейсуSerializable
під час створення класу. Процес серіалізації також відомий як маршалінг об'єкта, десеріалізація ж відома як демаршалінг . Серіалізація це механізм, що надає можливість об'єкту зберегти свою копію та всі інші об'єкти, на які посилається даний об'єкт, у зовнішній файл за допомогою класу ObjectOutputStream
. Збережені можуть бути структури даних, діаграми, об'єкти класу JFrame
або будь-які інші об'єкти незалежно від їх типу. У той же час, серіалізація зберігає інформацію про те, якого типу об'єкта, щоб у подальшому, при десеріалізації, ця інформація використовувалася для відтворення точного типу об'єкта, яким він був. Отже, серіалізація надає такі можливості:
- Система зберігання об'єктів, тобто: збереження їх властивостей у зовнішній файл, диск або базу даних.
- Система дзвінків віддалених процедур.
- Система розподілу об'єктів, наприклад, у програмних компонентах типу COM, COBRA.
- Система ідентифікації змінних даних у часі.
Потоки:
Кожна програма повинна записувати свої дані до місця зберігання або каналу, і кожна програма повинна зчитувати дані з каналу або місця зберігання. У Java ці канали, куди програми записують і звідки програми зчитують дані, називаються Потоками (Stream
) .
- Байтові класи-потоки звані *Streams
- Символьні класи-потоки звані *Reader та *Writer
Персистентність
Персистентність об'єкта - це здатність об'єкта жити або по-іншому - "пережити" виконання програми. Це означає, що будь-який об'єкт, створений у часі виконання, знищується "сміттярем" JVM, щоразу, коли цей об'єкт далі перестає використовуватися. Але у разі реалізації API персистентності, дані об'єкти не будуть знищуватись "сміттярем" JVM, замість чого їм буде дозволено "жити", що також дає можливість доступу до них при наступному запуску програми. Іншими словами, персистенція означає існування часу життя об'єкта, незалежно від часу життя програми, яка запущена. Один із способів реалізації персистентності це зберігання об'єктів де-небудь у зовнішньому файлі або в базі даних, а потім відновлення їх у пізніший час, використовуючи дані файли чи базу даних як джерела. Тут серіалізація і входить у гру. Будь-який неперсистентний об'єкт існує так довго, як довго працює JVM. Серіалізовані об'єкти — це просто об'єкти, перетворені на потоки, які потім зберігаються у зовнішній файл або передаються через мережу для зберігання та відновлення.Реалізація інтерфейсу Serializable
Будь-який клас має реалізовувати інтерфейсjava.io.Serializable
для серіалізації об'єктів цього класу. Інтерфейс Serializable
не має методів і тільки маркерує клас, щоб можна було його ідентифікувати як серіалізований. Тільки поля об'єкта серіалізованого класу можна зберегти. Методи чи конструктори не зберігаються, як частини серіалізованого потоку. Якщо будь-який об'єкт діє як посилання інший об'єкт, то поля цього об'єкта також сериализованны, якщо клас цього об'єкта реалізує інтерфейс Serializable
. Іншими словами, граф цього об'єкта, що отримується таким чином, серіалізуємо повністю. Граф об'єкта включає дерево чи структуру полів об'єкта та його подоб'єктов. Два головних класи, які допомагають реалізувати інтерфейс Seriliazable
:
ObjectInputStream
ObjectOutputStream
import java.io.*;
public class RandomClass implements Serializable {
// Генерация рандомного значения
private static int r() {
return (int)(Math.random() * 10);
}
private int data[];
// Конструктор
public RandomClass() {
datafile = new int[r()];
for (int i=0; i<datafile.length; i++)
datafile[i]=r();
}
public void printout() {
System.out.println("This RandomClass has "+datafile.length+" random integers");
for (int i=0; i<datafile.length; i++) {
System.out.print(datafile[i]+":");
System.out.println();
}
}
У наведеному вище коді створюється клас, який є серіалізується, т.к. "промаркований" інтерфейсом серіалізації. Клас створює масив випадкових цілих чисел, коли створюється його екземпляр. Наведений нижче код показує можливість запису об'єктів у потік, використовуючи клас ObjectOutputStream
. Програма має масив цілих чисел, але для серіалізації ми не повинні перебирати її внутрішні об'єкти. Інтерфейс Seriliazable
піклується про це автоматично. Лістинг 2. Простий приклад серіалізації об'єктів для виведення файлу
import java.io.*;
import java.util.*;
public class OutSerialize {
public static void main (String args[]) throws IOException {
RandomClass rc1 = new RandomClass();
RandomClass rc2 = new RandomClass();
//создание цепи потоков с потоком вывода об'єкта в конце
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("objects.dat"));
Date now = new Date(System.currentTimeMillis());
//java.util.* был импортирован для использования класса Date
out.writeObject(now);
out.writeObject(rc1);
out.writeObject(rc2);
out.close();
System.out.println("I have written:");
System.out.println("A Date object: "+now);
System.out.println("Two Group of randoms");
rc1.printout();
rc2.printout();
}
}
Код нижче демонструє можливості класу ObjectInputStream
, який зчитує серіалізовані дані із зовнішнього файлу до програми. Зауважте, що об'єкти зчитуються в тому самому порядку, в якому були записані у файл. Лістинг 3. Читання серіалізованих об'єктів або Десеріалізація
import java.io.*;
import java.util.*;
public class InSerialize {
public static void main (String args[]) throws IOException, ClassNotFoundException {
ObjectInputStream in = new ObjectInputStream (new FileInputStream("objects.dat"));
Date d1 = (Date)in.readObject();
RandomClass rc1 = (RandomClass)in.readObject();
RandomClass rc2 = (RandomClass)in.readObject();
System.out.println("I have read:");
System.out.println("A Date object: "+d1);
System.out.println("Two Group of randoms");
rc1.printout();
rc2.printout();
}
}
Майже всі класи Java можуть бути серіалізованими, включаючи класи AWT. Фрейм, який є вікном містить набір графічних компонентів. Якщо кадр серіалізований, механізм серіалізації піклується про це і серіалізує всі його компоненти та дані (позицію, зміст і т.д.). Деякі об'єкти класів Java не можуть бути серіалізовані, тому що містять дані, що посилаються на короткочасні ресурси операційних систем. Наприклад класи java.io.FileInputStream
та java.lang.Thread
. Якщо об'єкт містить посилання на елементи, що не серіалізуються, вся операція серіалізації зазнає невдачі і буде викинуто виняток NotSerializableException
. Якщо будь-який об'єкт посилається на посилання несеріалізованого об'єкта, його можна серіалізувати використовуючи ключове слово transient . Лістинг 4. Створення об'єктів, що серіалізуються, використовуючи ключове слово transient
public class Sclass implements Serializable{
public transient Thread newThread;
//помните, что поток(поток параллельного исполнения) по умолчанию не сериализуемый класс
private String studentID;
private int sum;
}
Безпека у Серіалізації
Серіалізація класу в Java передбачає передачу всіх його даних у зовнішній файл або базу даних через потік. Ми можемо обмежити дані, які будуть серіалізовані, коли того забажаємо. Є два способи зробити це:- Кожен параметр класу оголошений як transient , не серіалізуються (за замовчуванням усі параметри класу серіалізуються)
- Або кожен параметр класу, який ми хочемо серіалізувати, позначається тегом
Externalizable
(за замовчуванням параметри не серіалізуються).
ObjectOutputStream
, коли воно буде викликане для об'єкта, якщо це поле даних даного об'єкта позначено як transient . Наприклад: private transient String password
. З іншого боку, для явного оголошення даних об'єкта як серіалізується, ми повинні промаркувати клас як ExternalizablewriteExternal
і readExteranl
для запису та читання даних цього об'єкта явно.
Висновок
Особливість серіалізації об'єктів використана у багатьох розподілених системах як спосіб передачі даних. Але серіалізація розкриває приховані деталі, таким чином руйнуючи справжність абстрактних типів даних, що руйнує інкапсуляцію. У той же час приємно знати, що дані серіалізованого об'єкта, ті самі дані, що були у вихідному, оригінальному об'єкті. Це також відмінна можливість реалізації інтерфейсуObjectInputValidation
і перевизначення методу validateObject()
, навіть якщо використовуються кілька рядків коду. Якщо об'єкт не знайдено, ми можемо належним чином викинути виняток InvalidObjectException
. Оригінал статті: How serialization works in Java
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ