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

What does the transient modifier hide 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 does the transient modifier hide in Java - 1

Let's remember serialization

The modifier transientis used in the process of serializing and deserializing objects. So let's talk briefly about this 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 some file. Deserialization is the reverse process. Let's imagine that we serialized an object into bytes and stored this set of bytes in some file. When deserializing, the program needs:
  1. Read a set of bytes from a file.
  2. Construct an initial object from this set of bytes and set each field to the value that the object had at the time of serialization.
When might this be useful? For example, when we want the program to save its state when shutting down and restore it the next time it is turned on. When you shut down IntelliJ IDEA, you will most likely have the same tabs and classes open the next time you turn it on

Let's remember serialization in practice

Well, now let's look at serialization in practice. If you want to understand the topic better, we recommend reading the material Serialization and deserialization in Java . Well, in this article we will go over the top and go straight to the examples. Let's say 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 for use. It's time to split bytes into atoms . Let's write a method mainin which we create a class object Userand serialize it. Then we’ll load it and compare it 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 where Spanish shame transient comes into play .

Modifier (finally)transient

Was anyone confused 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 this. In the example above, I would like to save all fields except the password. How to achieve this? Answer: use the modifier transient. transientis a modifier placed before a class field (similar to other modifiers such as public, finaletc.) to indicate that the field should not be serialized. Fields marked with the keyword transientare not serialized. Now let’s edit the example with our user to correct a small confusion 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 is not 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 achieved our goal and do not store confidential information. Especially this kind of information... (sorry)

When to use transient?

An example with a user was needed in order to dive into the context of serialization. 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. They are calculated, so to speak, on the fly. To give an example of such a field, let’s imagine an order in an online store or some 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 a 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 looked at 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 a modifier transientif you are going to serialize such a class.

  • Fields that do not implement the interfaceSerializable

Sometimes a class contains fields - objects of other classes that do not implement the interface 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, you will receive an error java.io.NotSerializableException. To avoid this, all fields that do not implement the interface Serializablemust be marked with a 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 object state

Well, one last thing. There is 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 to perform 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. We remembered serialization in theory and practice.
  2. We realized that in order not to serialize some fields of the class, they need to be marked with a modifier transient.
  3. We discussed in what 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 interface Serializable;
    4. fields that are not part of the object's state.
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION