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!atoms . Let's write a method the Spanish shame
Let's remember serialization
The modifiertransient
is used in the process of serializing and deserializing objects. So let's talk about it briefly first.
Suppose 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:
- Read a set of bytes from a file.
- 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.
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 classUser
with 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;
}
/*
Getters, Setters
*/
@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 User
and 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);
// save an object to a file
objectOutputStream.writeObject(user);
} finally {
// Close the streams in the finally block
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 {
//create 2 threads to deserialize an object from file
fileInputStream = new FileInputStream(path);
objectInputStream = new ObjectInputStream(fileInputStream);
//load an object from 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 main
in which we create a class object User
and serialize it. Then load and compare with what was originally:
public static void main(String[] args) throws IOException, ClassNotFoundException {
// insert your path to 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 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 modifiertransient
. transient
is a modifier given before a class field (similar to other modifiers such as public
, final
etc.) to indicate that the field is not to be serialized. Fields marked with the keyword transient
are 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;
/*
Constructors, getters, setters, 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 modifiertransient
.
- Fields that are calculated programmatically
transient
.
class Order implements Serializable {
private List items;
private transient BigDecimal totalAmount;
}
- Fields with private information
transient
if you are going to serialize such a class.
- Fields that do not implement an interface
Serializable
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 Serializable
must be marked with the modifier transient
.
public class FileReader implements Serializable {
// The first 2 fields do not implement Serializable
// We mark them as transient fields
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
transient And final
That's all. Today we talked about the modifiertransient
:
- Remembered serialization in theory and in practice.
- We realized that in order not to serialize some fields of the class, they need to be marked with the modifier
transient
. - Discussed in which situations this modifier should be used. There were four such situations:
- fields that are calculated programmatically;
- fields that contain secret information;
- fields that do not implement the
Serializable
; - fields that are not part of the state of the object.
GO TO FULL VERSION