Transient class in java

Ключевое слово transient в Java

В этой статье мы сначала разберемся с ключевым словом transient , а затем рассмотрим его поведение на примерах.

2. Использование переходных

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

Сериализация – это процесс преобразования объекта в поток байтов, а десериализация является его противоположностью|/.

Когда мы помечаем любую переменную как переходную, , то эта переменная не сериализуется . Поэтому процесс сериализации игнорирует исходное значение переменных и сохраняет значения по умолчанию для этого типа данных.

Ключевое слово transient полезно в нескольких сценариях:

  • Мы можем использовать его для производных полей
  • Это полезно для полей, которые не представляют состояние объекта
  • Мы используем его для любых несериализуемых ссылок

3. Пример

Чтобы увидеть это в действии, давайте сначала создадим класс Book , объект которого мы хотели бы сериализовать:

public class Book implements Serializable < private static final long serialVersionUID = -2936687026040726549L; private String bookName; private transient String description; private transient int copies; // getters and setters >

Здесь мы отметили описание и копии как переходные поля.

После создания класса мы создадим объект этого класса:

Book book = new Book(); book.setBookName("Java Reference"); book.setDescription("will not be saved"); book.setCopies(25);

Теперь мы сериализуем объект в файл:

public static void serialize(Book book) throws Exception

Давайте теперь десериализуем объект из файла:

public static Book deserialize() throws Exception

Наконец, мы проверим значения объекта book :

assertEquals("Java Reference", book.getBookName()); assertNull(book.getDescription()); assertEquals(0, book.getCopies());

Здесь мы видим, что Имя книги сохранено должным образом. С другой стороны, поле копирует имеет значение 0 и описание равно null – значения по умолчанию для соответствующих типов данных – вместо исходных значений.

4. Поведение С окончательным

Теперь давайте рассмотрим особый случай, когда мы будем использовать transient с ключевым словом final . Для этого сначала мы добавим конечный переходный элемент в наш Book класс, а затем создадим пустой Book объект:

public class Book implements Serializable < // existing fields private final transient String bookCategory = "Fiction"; // getters and setters >

Когда мы проверим значения после десериализации, мы заметим, что переходный был проигнорирован для этого поля, а исходное значение было сохранено :

assertEquals("Fiction", book.getBookCategory());

5. Заключение

В этой статье мы рассмотрели использование ключевого слова transient и его поведение при сериализации и десериализации. Мы также видели его другое поведение с ключевым словом final .

Как всегда, весь код доступен на GitHub .

Читайте ещё по теме:

Источник

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

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. */ >
 Initial user: User Loaded user from file: User

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

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

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

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

 class Order implements Serializable < private Listitems; private transient BigDecimal totalAmount; //вычисляется на ходу > 

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

Иногда класс содержит поля — объекты других классов, которые не реализуют интерфейс 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

Итоги

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

На этом все. Сегодня мы говорили о модификаторе transient :

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

Источник

Модификатор 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; >

Java-университет

При сериализации сохраняются все переменные экземпляра (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(); >> > 

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

Источник

Читайте также:  Setting cookie in java
Оцените статью