JavaRush /Java Blog /Random EN /What hides the transient modifier in Java
Анзор Кармов
Level 31
Санкт-Петербург

What hides the transient modifier in Java

Published in the Random EN group
Hello! In today's article, we will look at the transient modifier in Java. Let's talk about why this modifier is needed and how to use it correctly. Go! What hides the transient modifier in Java - 1

Let's remember serialization

The modifier transientis used in the process of serializing and deserializing objects. So let's talk about it briefly first. What does the transient modifier hide in Java - 2Suppose we have some object, and it has fields, each of which has some value. All this is called the state of the object. Serialization is the conversion of an object's state into a sequence of bytes. These bytes are usually stored in a file. Deserialization is the reverse process. Let's imagine that we serialized an object into bytes and saved this set of bytes in some file. When deserializing, the program needs:
  1. Read a set of bytes from a file.
  2. Construct a source object from the given set of bytes and set each field to the value that the object had at the time of serialization.
When can it be useful? For example, when we want the program to save its state at the end of the work, and restore it the next time it is turned on. When you shut down IntelliJ IDEA, the next time you turn it on you most likely have the same tabs and classes open

Let's remember serialization in practice

Well, now let's look at serialization in practice. If you want to better understand the topic, we advise you to read the material Serialization and Deserialization in Java . Well, in this article we will go over the tops and go straight to the examples. Suppose we have a class Userwith a set of some fields, getters and setters, and a method 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{" +
                "firstName='" + firstName + '\'' +
                ", lastName='" + lastName + '\'' +
                ", email='" + email + '\'' +
                ", birthDate=" + birthDate +
                ", login='" + login + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}
We want to serialize objects of this class in the future. Let's write a method that takes an object Userand a string path- the path to the file in which we will save the bytes:
static void serialize(User user, String path) throws IOException {
    FileOutputStream outputStream = null;
    ObjectOutputStream objectOutputStream = null;
    try {
        //create 2 threads to serialize the object and save it to a file
        outputStream = new FileOutputStream(path);
        objectOutputStream = new ObjectOutputStream(outputStream);

        // сохраняем an object в файл
        objectOutputStream.writeObject(user);
    } finally {
        // Закроем потоки в блоке finally
        if (objectOutputStream != null) {
            objectOutputStream.close();
        }
        if (outputStream != null) {
            outputStream.close();
        }
    }
}
We will also write a method for deserialization. The method takes a string path(the path to the file from which the object will be “loaded”) and returns an object of type User:
static User deserialize(String path) throws IOException, ClassNotFoundException {
    FileInputStream fileInputStream = null;
    ObjectInputStream objectInputStream = null;

    try {

        //создаем 2 потока для десериализации an object из file
        fileInputStream = new FileInputStream(path);
        objectInputStream = new ObjectInputStream(fileInputStream);

        //загружаем an object из file
        return  (User) objectInputStream.readObject();
    } finally {
        if (fileInputStream != null) {
            fileInputStream.close();
        }
        if (objectInputStream != null) {
            objectInputStream.close();
        }
    }
}
All tools are ready to go. It's time to split bytes into atoms . Let's write a method mainin which we create a class object Userand serialize it. Then load and compare with what was originally:
public static void main(String[] args) throws IOException, ClassNotFoundException {
    // вставьте свой путь до file
    final String path = "/home/zor/user.ser";

    // create our object
    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");
}
If we run the method, we will see the following output:
Initial user: User{firstName='Stefan', lastName='Smith', email='ssmith@email.com', birthDate=1991-07-16, login='ssmith', password='gemma_arterton_4ever_in_my_heart91'}

Loaded user from file: User{firstName='Stefan', lastName='Smith', email='ssmith@email.com', birthDate=1991-07-16, login='ssmith', password='gemma_arterton_4ever_in_my_heart91'}
As you can see from the output, the objects are identical. But there is a small but... And this is exactly the place where the Spanish shame transient comes into play .

Modifier (well, finally)transient

Nobody was embarrassed that we saved the user's password? Especially such a password… Yes, yes, we came up with it ourselves, but still… Sometimes there are situations when some fields cannot be serialized, or it is better not to do so. In the example above, I would like to save all fields except for the password. How to achieve this? Answer: use modifier transient. transientis a modifier given before a class field (similar to other modifiers such as public, finaletc.) to indicate that the field is not to be serialized. Fields marked with the keyword transientare not serialized. Now let's edit the example with our user to fix a little embarrassment and not save the user's password. To do this, mark the corresponding field in the class with the keyword 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...
     */
}
If we run the method from the example above again main, we will see that the password has not been saved:
Initial user: User{firstName='Stefan', lastName='Smith', email='ssmith@email.com', birthDate=1991-07-16, login='ssmith', password='gemma_arterton_4ever_in_my_heart91'}

Loaded user from file: User{firstName='Stefan', lastName='Smith', email='ssmith@email.com', birthDate=1991-07-16, login='ssmith', password='null'}
Great, we have achieved our goal and do not store confidential information. Especially information like this… (sorry)

When to use transient?

The user example was needed in order to dive into the serialization context. Now let's talk more specifically about when to use the modifier transient.

  • Fields that are calculated programmatically

Some classes sometimes have fields that are calculated based on other fields or other information. Calculated, so to speak, on the fly. To give an example of such a field, imagine an order in an online store or in some kind of food delivery service. Each order, among other information, consists of a list of goods and a total cost. It, in turn, consists of the total cost of each product. It turns out that the final cost should not be set “by hand”: it must be calculated programmatically, summing up the cost of all goods. Fields like these that should be calculated programmatically do not need to be serialized. Therefore, we mark them with the modifier transient.
class Order implements Serializable {

    private List items;
    private transient BigDecimal totalAmount; //вычисляется на ходу

}

  • Fields with private information

There are also some classes that store private information. We considered an example of such a class at the beginning of the article. You should not allow such information to leak outside the JVM. Therefore, fields with such data must be marked with the modifier transientif you are going to serialize such a class.

  • Fields that do not implement an interfaceSerializable

Sometimes a class contains fields, which are objects of other classes that do not implement the Serializable. Examples of such fields are loggers, I/O streams, objects that store database connections, and other utility classes. If you try to serialize an object that contains non-serializable fields, an error will occur java.io.NotSerializableException. To avoid this, all fields that do not implement the interface Serializablemust be marked with the modifier transient.
public class FileReader implements Serializable {
    // Первые 2 поля не реализуют Serializable
    // Помечаем их How 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();
            }
        }
    }
}

  • Fields with information about the state of the object

Well, the last. No need to serialize fields that are not part of the object's state information. The examples above fall under this rule. But you can also include here all other fields added for debugging or for performing some kind of service function that do not carry information about the state of the object.

transientAndfinal

Results

That's all. Today we talked about the modifier transient:
  1. Remembered serialization in theory and in practice.
  2. We realized that in order not to serialize some fields of the class, they need to be marked with the modifier transient.
  3. Discussed in which situations this modifier should be used. There were four such situations:
    1. fields that are calculated programmatically;
    2. fields that contain secret information;
    3. fields that do not implement the Serializable;
    4. fields that are not part of the state of the object.
What does the transient modifier hide in Java - 3
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION