Transient java что это
Перейти к содержимому

Transient java что это

  • автор:

Для чего нужен модификатор transient?

Сериализация — это преобразование экземпляра класса в форму, пригодную для его сохранения (например в файл, в БД или для передачи по сети). Сериализованные объекты можно затем восстановить (десериализовать).

Свойства класса, помеченные модификатором transient , не сериализуются.

Обычно в таких полях хранится промежуточное состояние объекта, которое, к примеру, проще вычислить, чем сериализовать, а затем десериализовать. Другой пример такого поля — ссылка на экземпляр объекта, который не требует сериализации или не может быть сериализован.

Что такое transient в Java?

В Java ключевое слово transient используется в контексте сериализации объектов. Когда поле класса помечено как transient , оно исключается из процесса сериализации, что означает, что его значение не сохраняется при преобразовании объекта в последовательность байтов. Это может быть полезно, если есть поля, которые не должны сохраняться или передаваться между системами.

Основные моменты:

  1. Не участвует в сериализации:
    • Поле, помеченное как transient , не участвует в процессе сериализации. При десериализации такое поле получит значение по умолчанию для своего типа.
  2. Исключение временных данных:
    • Ключевое слово transient часто используется для исключения временных данных, кэшей или другой информации, которая не должна сохраняться.
  3. Сериализация пользовательских объектов:
    • При реализации интерфейса Serializable в пользовательских классах, transient может быть применено к полям, которые не подлежат сериализации.
class Person implements Serializable < private static final long serialVersionUID = 1L; private String name; private transient int age; // Поле, помеченное transient >

Модификатор transient

— Привет, Амиго! Хотела тебе порассказывать одно маленькое дополнение к сериализации.

Допустим наш класс содержит ссылку на какой-нибудь InputStream, тогда его нельзя сериализовать, ведь так?

— Да. Ты же сама говорила, что потоки сериализовать нельзя. А сериализовать объект, у которого есть несериализуемые данные – тоже нельзя.

— Да. Именно так. Но что, если класс хранит данные, которые не играют значащей роли в его состоянии, но мешают считаться ему сериализуемым классом? Мало ли что класс может у себя хранить ненужного. Возможно, он может выбросить эти данные в любой момент или даже так и делает постоянно.

Для таких случаев разработчики Java придумали специальное слово – transient . Его можно написать перед переменной класса и она не будет учитываться при сериализации. Ее состояние не будет ни сохраняться, ни восстанавливаться. Как будто и нет ее вовсе. Как раз для таких ситуаций, как мы только что рассмотрели.

Помнишь кеширование и модификатор volatile ? Нет правил без исключений.

Вот тебе один примерчик такого счастья:

Пример «кота» с невидимой для сериализации переменной — in:

class Cat implements Serializable < public String name; public int age; public int weight; transient public InputStream in = System.in; >

Комментарии (91)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Dmitry Shaburov Уровень 33
17 ноября 2023
Raf Java Master Уровень 31
11 сентября 2023
Дисциплина и гринд господа, мы победим!
Ислам Уровень 33
10 июля 2023
Александр Горбунов Уровень 51
18 июня 2023

При сериализации сохраняются все переменные экземпляра (instance variables). Однако переменные, объявленные с модификатором transient, не сохраняются сериализацией, и их значения не включаются в сериализованный поток данных. Также статические переменные (static variables) не сохраняются сериализацией. Для более явного управления процессом сериализации можно использовать массив serialPersistentFields. В этом массиве можно указать конкретные переменные, которые будут сохранены при сериализации, независимо от их модификаторов. Давайте рассмотрим пример:

 import java.io.*; public class MyClass implements Serializable < private String name; private transient int age; private static double weight; public MyClass(String name, int age, double weight) < this.name = name; this.age = age; MyClass.weight = weight; >public void displayInfo() < System.out.println("Name: " + name); System.out.println("Age: " + age); System.out.println("Weight: " + weight); >public static void main(String[] args) < MyClass obj = new MyClass("John", 30, 70.5); try < ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("data.bin")); out.writeObject(obj); out.close(); ObjectInputStream in = new ObjectInputStream(new FileInputStream("data.bin")); MyClass restoredObj = (MyClass) in.readObject(); in.close(); restoredObj.displayInfo(); >catch (IOException | ClassNotFoundException e) < e.printStackTrace(); >> > 

Gans Electro Уровень 50
20 апреля 2023

Возможный смысл строчки «Помнишь кеширование и модификатор volatile? Нет правил без исключений.» GPT: Да, сейчас я понимаю, что вы имели в виду. В данном контексте выражение «Нет правил без исключений» скорее всего относится к следующей ситуации: — Обычно, чтобы объект можно было сериализовать, все его поля должны быть сериализуемыми. — Однако, в случае, когда объект содержит поле, которое не является сериализуемым (например, поле типа InputStream), разработчики могут использовать модификатор transient, чтобы указать, что это поле не должно быть учтено при сериализации. — Тем не менее, есть определенные исключения, когда применение модификатора transient может привести к ошибкам или неожиданному поведению объекта при десериализации. Например, если поле было скрыто от сериализации, то при десериализации его значение будет равно значению по умолчанию, а не сохраненному значению. — Таким образом, правило о том, что все поля должны быть сериализуемыми, имеет исключения, но эти исключения могут привести к неожиданному поведению, и их использование следует тщательно обдумывать. Вот почему в статье говорится «Нет правил без исключений».

Что скрывает модификатор transient в Java

Что скрывает модификатор transient в Java - 1

Привет! В сегодняшней статье мы рассмотрим модификатор transient в Java. Поговорим о том, зачем данный модификатор нужен и как его правильно использовать. Поехали!

Вспомним сериализацию

Что скрывает модификатор transient в Java - 2

Модификатор transient используется в процессе сериализации и десериализации объектов. Поэтому для начала кратко поговорим об этом.Предположим, у нас есть некоторый объект, а у него — поля, каждое из которых имеет какое-то значение. Все это называется состоянием объекта. Сериализация — это конвертация состояния объекта в последовательность байт. Данные байты сохраняются, как правило, в каком-либо файле. Десериализация — это обратный процесс. Представим, что мы сериализовали объект в байты и сохранили данный набор байтов в некотором файле. При десериализации программе нужно:

  1. Считать набор байтов из файла.
  2. Сконструировать из данного набора байтов исходный объект и задать каждому полю значение, которое было у объекта на момент сериализации.

Когда это может быть полезным? Например, когда мы хотим, чтобы при завершении работы программа сохраняла свое состояние, и при следующем включении восстанавливала его. Когда вы завершаете работу IntelliJ IDEA, при следующем включении, скорее всего, у вас открыты те же вкладки и классы

Вспомним сериализацию на практике

Что же, теперь рассмотрим сериализацию на практике. Если хочется получше разобраться в теме, советуем почитать материал Сериализация и десериализация в Java. Ну а в этой статье мы пройдемся по верхам и перейдем сразу к примерам. Предположим, у нас есть класс User с набором некоторых полей, геттерами и сеттерами, а также методом toString :

 public class User implements Serializable < private static final long serialVersionUID = 1L; private String firstName; private String lastName; private String email; private LocalDate birthDate; private String login; private String password; public User() <>public User(String firstName, String lastName, String email, LocalDate birthDate, String login, String password) < this.firstName = firstName; this.lastName = lastName; this.email = email; this.birthDate = birthDate; this.login = login; this.password = password; >/* Геттеры, Сеттеры */ @Override public String toString() < return "User'; > >

Мы хотим сериализовывать объекты данного класса в дальнейшем. Напишем метод, который принимает объект User и строку path — путь до файла, в котором мы сохраним байты:

 static void serialize(User user, String path) throws IOException < FileOutputStream outputStream = null; ObjectOutputStream objectOutputStream = null; try < //создаем 2 потока для сериализации объекта и сохранения его в файл outputStream = new FileOutputStream(path); objectOutputStream = new ObjectOutputStream(outputStream); // сохраняем объект в файл objectOutputStream.writeObject(user); >finally < // Закроем потоки в блоке finally if (objectOutputStream != null) < objectOutputStream.close(); >if (outputStream != null) < outputStream.close(); >> > 

Также напишем метод для десериализации. Метод принимает строку path (путь до файла из которого объект будет “загружен”) и возвращает объект типа User :

 static User deserialize(String path) throws IOException, ClassNotFoundException < FileInputStream fileInputStream = null; ObjectInputStream objectInputStream = null; try < //создаем 2 потока для десериализации объекта из файла fileInputStream = new FileInputStream(path); objectInputStream = new ObjectInputStream(fileInputStream); //загружаем объект из файла return (User) objectInputStream.readObject(); >finally < if (fileInputStream != null) < fileInputStream.close(); >if (objectInputStream != null) < objectInputStream.close(); >> > 

Все инструменты готовы к работе. Пришло время расщеплять на атомы байты. Напишем метод main , в котором создадим объект класса User и сериализуем его. Затем загрузим и сравним с тем, что было изначально:

 public static void main(String[] args) throws IOException, ClassNotFoundException < // вставьте свой путь до файла final String path = "/home/zor/user.ser"; //создаем наш объект User user = new User(); user.setFirstName("Stefan"); user.setLastName("Smith"); user.setEmail("ssmith@email.com"); user.setBirthDate(LocalDate.of(1991, 7, 16)); user.setLogin("ssmith"); user.setPassword("gemma_arterton_4ever_in_my_heart91"); System.out.println("Initial user: " + user + "\r\n"); serialize(user, path); User loadedUser = deserialize(path); System.out.println("Loaded user from file: " + loadedUser + "\r\n"); >

Если запустить метод, мы увидим следующий вывод:

 Initial user: User Loaded user from file: User

Как видно из вывода, объекты идентичны. Но есть маленькое но… И это как раз то место, когда в игру вступает испанский стыд transient .

Модификатор (ну наконец-таки) transient

Никого не смутило, что мы пароль пользователя сохранили? Особенно такой пароль… Да-да, мы сами его придумали, но все же… Порой бывают ситуации, когда некоторые поля невозможно сериализовать, или лучше этого не делать. В примере выше хотелось бы сохранять все поля, за исключением пароля. Как это добиться? Ответ: использовать модификатор transient . transient — это модификатор, указываемый перед полем класса (подобно другим модификаторам, таким как public , final и т.д.) для обозначения того, что данное поле не должно быть сериализовано. Поля, помеченные ключевым словом transient , не сериализуются. Теперь отредактируем пример с нашим пользователем, чтобы исправить небольшой конфуз и не сохранять пароль пользователя. Для этого отметим соответствующее поле в классе ключевым словом transient :

 public class User implements Serializable < private static final long serialVersionUID = 1L; private String firstName; private String lastName; private String email; private LocalDate birthDate; private String login; private transient String password; /* Конструкторы, геттеры, сеттеры, toString. */ >

Если мы еще раз запустим метод main из примера выше, увидим, что пароль не сохранился:

 Initial user: User Loaded user from file: User

Отлично, мы добились поставленной цели и не сохраняем конфиденциальную информацию. Особенно такую информацию… (простите)

Когда использовать transient?

Пример с пользователем был нужен для того, чтобы погрузиться в контекст сериализации. Теперь поговорим предметнее о том, когда следует использовать модификатор transient .

  • Поля, которые вычисляются программно

В некоторых классах иногда бывают такие поля, которые вычисляются на основе других полей или же другой информации. Вычисляются, так сказать, на лету. Чтобы привести пример такого поля, представим себе заказ в интернет-магазине или же в каком-нибудь сервисе доставки еды. Каждый заказ, помимо прочей информации, состоит из списка товаров и итоговой стоимости. Она, в свою очередь, складывается из суммарной стоимости каждого товара. Выходит, что итоговую стоимость не стоит задавать “руками”: ее нужно вычислять программно, суммируя стоимость всех товаров. Подобные поля, которые следует вычислять программно, не нужно сериализовывать. Поэтому помечаем их модификатором transient .

 class Order implements Serializable < private Listitems; private transient BigDecimal totalAmount; //вычисляется на ходу > 
  • Поля с приватной информацией

Также бывают некоторые классы, которые хранят приватную информацию. Пример такого класса мы рассматривали в начале статьи. Не стоит допускать утечки такой информации за пределы JVM. Поэтому поля с подобными данными необходимо помечать модификатором transient , если вы собираетесь сериализовывать такой класс.

  • Поля, которые не реализуют интерфейс Serializable

Иногда класс содержит поля — объекты других классов, которые не реализуют интерфейс Serializable . Пример таких полей — логгеры, потоки ввода-вывода, объекты, которые хранят соединения с базой данных и прочие служебные классы. Если попытаться сериализовать объект, который содержит несериализуемые поля, возникнет ошибка java.io.NotSerializableException . Чтобы избежать этого, все поля, которые не реализуют интерфейс Serializable , необходимо помечать модификатором transient .

 public class FileReader implements Serializable < // Первые 2 поля не реализуют Serializable // Помечаем их как transient поля private transient InputStream is; private transient BufferedReader buf; private String fileName; // Constructors, Getters, Setters public String readFile() throws IOException < try < is = new FileInputStream(fileName); buf = new BufferedReader(new InputStreamReader(is)); String line = buf.readLine(); StringBuilder sb = new StringBuilder(); while (line != null) < sb.append(line).append("\n"); line = buf.readLine(); >return sb.toString(); > finally < if (buf != null) < buf.close(); >if (is != null) < is.close(); >> > > 
  • Поля с информацией о состоянии объекта

Ну и последнее. Не нужно сериализовывать поля, которые не являются частью информации о состоянии объекта. Примеры выше попадают под это правило. Но также сюда можно включить и все прочие поля, добавленные для дебага или для выполнения какой то служебной функции, которые не несут информации о состоянии объекта.

transient и final

Итоги

  1. Вспомнили сериализацию в теории и на практике.
  2. Поняли, что для того, чтобы не сериализовать некоторые поля класса, их нужно помечать модификатором transient .
  3. Обсудили, в каких ситуациях следует использовать данный модификатор. Таких ситуаций оказалось четыре:
    1. поля, которые вычисляются программно;
    2. поля, которые содержат секретную информацию;
    3. поля, которые не реализуют интерфейс Serializable ;
    4. поля, которые не являются частью состояния объекта.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *